1 //
2 // Copyright 2018 Pixar
3 //
4 // Licensed under the Apache License, Version 2.0 (the "Apache License")
5 // with the following modification; you may not use this file except in
6 // compliance with the Apache License and the following modification to it:
7 // Section 6. Trademarks. is deleted and replaced with:
8 //
9 // 6. Trademarks. This License does not grant permission to use the trade
10 //    names, trademarks, service marks, or product names of the Licensor
11 //    and its affiliates, except as required to comply with Section 4(c) of
12 //    the License and to reproduce the content of the NOTICE file.
13 //
14 // You may obtain a copy of the Apache License at
15 //
16 //     http://www.apache.org/licenses/LICENSE-2.0
17 //
18 // Unless required by applicable law or agreed to in writing, software
19 // distributed under the Apache License with the above modification is
20 // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21 // KIND, either express or implied. See the Apache License for the specific
22 // language governing permissions and limitations under the Apache License.
23 //
24 
25 #ifndef PXR_USD_NDR_REGISTRY_H
26 #define PXR_USD_NDR_REGISTRY_H
27 
28 /// \file ndr/registry.h
29 
30 #include "pxr/pxr.h"
31 #include "pxr/usd/ndr/api.h"
32 #include "pxr/base/tf/weakBase.h"
33 #include "pxr/usd/ndr/declare.h"
34 #include "pxr/usd/ndr/discoveryPlugin.h"
35 #include "pxr/usd/ndr/node.h"
36 #include "pxr/usd/ndr/nodeDiscoveryResult.h"
37 #include "pxr/usd/ndr/parserPlugin.h"
38 #include "pxr/usd/sdf/assetPath.h"
39 #include <mutex>
40 
41 PXR_NAMESPACE_OPEN_SCOPE
42 
43 /// \class NdrRegistry
44 ///
45 /// The registry provides access to node information. "Discovery Plugins" are
46 /// responsible for finding the nodes that should be included in the registry.
47 ///
48 /// Discovery plugins are found through the plugin system. If additional
49 /// discovery plugins need to be specified, a client can pass them to
50 /// `SetExtraDiscoveryPlugins()`.
51 ///
52 /// When the registry is first told about the discovery plugins, the plugins
53 /// will be asked to discover nodes. These plugins will generate
54 /// `NdrNodeDiscoveryResult` instances, which only contain basic metadata. Once
55 /// the client asks for information that would require the node's contents to
56 /// be parsed (eg, what its inputs and outputs are), the registry will begin the
57 /// parsing process on an as-needed basis. See `NdrNodeDiscoveryResult` for the
58 /// information that can be retrieved without triggering a parse.
59 ///
60 /// Some methods in this library may allow for a "family" to be provided. A
61 /// family is simply a generic grouping which is optional.
62 ///
63 class NdrRegistry : public TfWeakBase
64 {
65 public:
66     using DiscoveryPluginRefPtrVec = NdrDiscoveryPluginRefPtrVector;
67 
68     /// Allows the client to set any additional discovery plugins that would
69     /// otherwise NOT be found through the plugin system. Runs the discovery
70     /// process for the specified plugins immediately.
71     ///
72     /// Note that this method cannot be called after any nodes in the registry
73     /// have been parsed (eg, through GetNode*()), otherwise an error will
74     /// result.
75     NDR_API
76     void SetExtraDiscoveryPlugins(DiscoveryPluginRefPtrVec plugins);
77 
78     /// Allows the client to set any additional discovery plugins that would
79     /// otherwise NOT be found through the plugin system. Runs the discovery
80     /// process for the specified plugins immediately.
81     ///
82     /// Note that this method cannot be called after any nodes in the registry
83     /// have been parsed (eg, through GetNode*()), otherwise an error will
84     /// result.
85     NDR_API
86     void SetExtraDiscoveryPlugins(const std::vector<TfType>& pluginTypes);
87 
88     /// Allows the client to set any additional parser plugins that would
89     /// otherwise NOT be found through the plugin system.
90     ///
91     /// Note that this method cannot be called after any nodes in the registry
92     /// have been parsed (eg, through GetNode*()), otherwise an error will
93     /// result.
94     NDR_API
95     void SetExtraParserPlugins(const std::vector<TfType>& pluginTypes);
96 
97     /// Parses the given \p asset, constructs a NdrNode from it and adds it to
98     /// the registry.
99     ///
100     /// Nodes created from an asset using this API can be looked up by the
101     /// unique identifier and sourceType of the returned node, or by URI,
102     /// which will be set to the unresolved asset path value.
103     ///
104     /// \p metadata contains additional metadata needed for parsing and
105     /// compiling the source code in the file pointed to by \p asset correctly.
106     /// This metadata supplements the metadata available in the asset and
107     /// overrides it in cases where there are key collisions.
108     ///
109     /// \p subidentifier is optional, and it would be used to indicate a
110     /// particular definition in the asset file if the asset contains multiple
111     /// node definitions.
112     ///
113     /// \p sourceType is optional, and it is only needed to indicate a
114     /// particular type if the asset file is capable of representing a node
115     /// definition of multiple source types.
116     ///
117     /// Returns a valid node if the asset is parsed successfully using one
118     /// of the registered parser plugins.
119     NDR_API
120     NdrNodeConstPtr GetNodeFromAsset(const SdfAssetPath &asset,
121                                      const NdrTokenMap &metadata,
122                                      const TfToken &subIdentifier=TfToken(),
123                                      const TfToken &sourceType=TfToken());
124 
125     /// Parses the given \p sourceCode string, constructs a NdrNode from it and
126     /// adds it to the registry. The parser to be used is determined by the
127     /// specified \p sourceType.
128     ///
129     /// Nodes created from source code using this API can be looked up by the
130     /// unique identifier and sourceType of the returned node.
131     ///
132     /// \p metadata contains additional metadata needed for parsing and
133     /// compiling the source code correctly. This metadata supplements the
134     /// metadata available in \p sourceCode and overrides it cases where there
135     /// are key collisions.
136     ///
137     /// Returns a valid node if the given source code is parsed successfully
138     /// using the parser plugins that is registered for the specified
139     /// \p sourceType.
140     NDR_API
141     NdrNodeConstPtr GetNodeFromSourceCode(const std::string &sourceCode,
142                                           const TfToken &sourceType,
143                                           const NdrTokenMap &metadata);
144 
145     /// Get the locations where the registry is searching for nodes.
146     ///
147     /// Depending on which discovery plugins were used, this may include
148     /// non-filesystem paths.
149     NDR_API
150     NdrStringVec GetSearchURIs() const;
151 
152     /// Get the identifiers of all the nodes that the registry is aware of.
153     ///
154     /// This will not run the parsing plugins on the nodes that have been
155     /// discovered, so this method is relatively quick. Optionally, a "family"
156     /// name can be specified to only get the identifiers of nodes that belong
157     /// to that family and a filter can be specified to get just the default
158     /// version (the default) or all versions of the node.
159     NDR_API
160     NdrIdentifierVec
161     GetNodeIdentifiers(const TfToken& family = TfToken(),
162                        NdrVersionFilter filter =
163                            NdrVersionFilterDefaultOnly) const;
164 
165     /// Get the names of all the nodes that the registry is aware of.
166     ///
167     /// This will not run the parsing plugins on the nodes that have been
168     /// discovered, so this method is relatively quick. Optionally, a "family"
169     /// name can be specified to only get the names of nodes that belong to
170     /// that family.
171     NDR_API
172     NdrStringVec GetNodeNames(const TfToken& family = TfToken()) const;
173 
174     /// Get the node with the specified \p identifier, and an optional
175     /// \p sourceTypePriority list specifying the set of node SOURCE types (see
176     /// `NdrNode::GetSourceType()`) that should be searched.
177     ///
178     /// If no sourceTypePriority is specified, the first encountered node with
179     /// the specified identifier will be returned (first is arbitrary) if found.
180     /// If no matching node is found then the first node found with an alias
181     /// matching the identifier will be returned if one exists.
182     ///
183     /// If a sourceTypePriority list is specified, then this will iterate
184     /// through each source type and try to find a node matching by identifier
185     /// or alias. This is equivalent to calling
186     /// NdrRegistry::GetNodeByIdentifierAndType for each source type until a
187     /// node is found.
188     ///
189     /// Nodes of the same identifier but different source type can exist
190     /// in the registry. If a node 'Foo' with source types 'abc' and 'xyz'
191     /// exist in the registry, and you want to make sure the 'abc' version
192     /// is fetched before the 'xyz' version, the priority list would be
193     /// specified as ['abc', 'xyz']. If the 'abc' version did not exist in
194     /// the registry, then the 'xyz' version would be returned.
195     ///
196     /// Returns `nullptr` if a node matching the arguments can't be found.
197     ///
198     /// \sa NdrNodeDiscoveryResult::aliases
199     NDR_API
200     NdrNodeConstPtr GetNodeByIdentifier(const NdrIdentifier& identifier,
201                         const NdrTokenVec& sourceTypePriority = NdrTokenVec());
202 
203     /// Get the node with the specified \p identifier and \p sourceType. If,
204     /// for the given sourceType, there is no node with the given identifier,
205     /// this will instead search for a node which has an alias that matches the
206     /// identifier and return it if it exists. Otherwise there is no matching
207     /// node for the sourceType and nullptr is returned.
208     ///
209     /// \sa NdrNodeDiscoveryResult::aliases
210     NDR_API
211     NdrNodeConstPtr GetNodeByIdentifierAndType(const NdrIdentifier& identifier,
212                                                const TfToken& sourceType);
213 
214     /// Get the node with the specified name.  An optional priority list
215     /// specifies the set of node SOURCE types (\sa NdrNode::GetSourceType())
216     /// that should be searched and in what order.
217     ///
218     /// Optionally, a filter can be specified to consider just the default
219     /// versions of nodes matching \p name (the default) or all versions
220     /// of the nodes.
221     ///
222     /// \sa GetNodeByIdentifier().
223     NDR_API
224     NdrNodeConstPtr GetNodeByName(const std::string& name,
225                         const NdrTokenVec& sourceTypePriority = NdrTokenVec(),
226                         NdrVersionFilter filter = NdrVersionFilterDefaultOnly);
227 
228     /// A convenience wrapper around \c GetNodeByName(). Instead of
229     /// providing a priority list, an exact type is specified, and
230     /// `nullptr` is returned if a node with the exact identifier and
231     /// type does not exist.
232     ///
233     /// Optionally, a filter can be specified to consider just the default
234     /// versions of nodes matching \p name (the default) or all versions
235     /// of the nodes.
236     NDR_API
237     NdrNodeConstPtr GetNodeByNameAndType(const std::string& name,
238                                          const TfToken& sourceType,
239                                          NdrVersionFilter filter =
240                                              NdrVersionFilterDefaultOnly);
241 
242     /// Get all nodes matching the specified identifier (multiple nodes of
243     /// the same identifier, but different source types, may exist) as well as
244     /// any nodes which have an alias that matches the identifier. If no nodes
245     /// match the identifier, an empty vector is returned.
246     ///
247     /// \sa NdrNodeDiscoveryResult::aliases
248     NDR_API
249     NdrNodeConstPtrVec GetNodesByIdentifier(const NdrIdentifier& identifier);
250 
251     /// Get all nodes matching the specified name. Only nodes matching the
252     /// specified name will be parsed. Optionally, a filter can be specified
253     /// to get just the default version (the default) or all versions of the
254     /// node.  If no nodes match an empty vector is returned.
255     NDR_API
256     NdrNodeConstPtrVec GetNodesByName(const std::string& name,
257                                       NdrVersionFilter filter =
258                                           NdrVersionFilterDefaultOnly);
259 
260     /// Get all nodes from the registry, optionally restricted to the nodes
261     /// that fall under a specified family and/or the default version.
262     ///
263     /// Note that this will parse \em all nodes that the registry is aware of
264     /// (unless a family is specified), so this may take some time to run
265     /// the first time it is called.
266     NDR_API
267     NdrNodeConstPtrVec GetNodesByFamily(const TfToken& family = TfToken(),
268                                         NdrVersionFilter filter =
269                                             NdrVersionFilterDefaultOnly);
270 
271     /// Get a sorted list of all node source types that may be present on the
272     /// nodes in the registry.
273     ///
274     /// Source types originate from the discovery process, but there is no
275     /// guarantee that the discovered source types will also have a registered
276     /// parser plugin.  The actual supported source types here depend on the
277     /// parsers that are available.  Also note that some parser plugins may not
278     /// advertise a source type.
279     ///
280     /// See the documentation for `NdrParserPlugin` and
281     /// `NdrNode::GetSourceType()` for more information.
282     NDR_API
283     NdrTokenVec GetAllNodeSourceTypes() const;
284 
285 protected:
286     NdrRegistry(const NdrRegistry&) = delete;
287     NdrRegistry& operator=(const NdrRegistry&) = delete;
288 
289     NDR_API
290     NdrRegistry();
291 
292     NDR_API
293     ~NdrRegistry();
294 
295 private:
296     class _DiscoveryContext;
297     friend class _DiscoveryContext;
298 
299     typedef std::unordered_map<TfToken, NdrParserPlugin*,
300         TfToken::HashFunctor> TypeToParserPluginMap;
301     typedef std::pair<NdrIdentifier, TfToken> NodeMapKey;
302     struct NodeMapKeyHashFunctor {
operatorNodeMapKeyHashFunctor303         size_t operator()(const NodeMapKey& x) const {
304             return NdrIdentifierHashFunctor()(x.first) ^
305                    TfToken::HashFunctor()(x.second);
306         }
307     };
308     typedef std::unordered_multimap<NodeMapKey, NdrNodeUniquePtr,
309                                     NodeMapKeyHashFunctor> NodeMap;
310 
311     // The discovery result vec is not a concurrent data structure, thus it
312     // needs some locking infrastructure.
313     mutable std::mutex _discoveryResultMutex;
314 
315     // The node map is not a concurrent data structure, thus it needs some
316     // locking infrastructure.
317     mutable std::mutex _nodeMapMutex;
318 
319     // Runs each discovery plugin provided and appends the results to the
320     // internal discovery results vector
321     void _RunDiscoveryPlugins(const DiscoveryPluginRefPtrVec& discoveryPlugins);
322 
323     // Finds and instantiates the discovery plugins
324     void _FindAndInstantiateDiscoveryPlugins();
325 
326     // Finds and instantiates the parser plugins
327     void _FindAndInstantiateParserPlugins();
328 
329     // Instantiates the specified parser plugins and adds them to
330     // the registry.
331     void _InstantiateParserPlugins(const std::set<TfType>& parserPluginTypes);
332 
333     // Returns the cached or newly parsed node for the discovery result if its
334     // identifier matches the given identifier.
335     NdrNodeConstPtr _ParseNodeMatchingIdentifier(
336         const NdrNodeDiscoveryResult& dr, const NdrIdentifier& identifier);
337 
338     // Returns the cached or newly parsed node for the discovery result if it
339     // has an alias that matches the given identifier.
340     NdrNodeConstPtr _ParseNodeMatchingAlias(
341         const NdrNodeDiscoveryResult& dr, const NdrIdentifier& identifier);
342 
343     // Returns the cached or newly parsed node for the discovery result if its
344     // name and version match the given name and version filter.
345     NdrNodeConstPtr _ParseNodeMatchingNameAndFilter(
346         const NdrNodeDiscoveryResult& dr, const std::string& name,
347         NdrVersionFilter filter);
348 
349     // Implementation helper for getting the first node of the given sourceType
350     // that matches the given indentifier. This includes node that match the
351     // identifier through an alias.
352     NdrNodeConstPtr _GetNodeByIdentifierAndTypeImpl(
353         const NdrIdentifier& identifier, const TfToken& sourceType);
354 
355     // Implementation helper for getting the first node of the given sourceType
356     // that matches the given name and version filter.
357     NdrNodeConstPtr _GetNodeByNameAndTypeImpl(
358         const std::string& name, const TfToken& sourceType,
359         NdrVersionFilter filter);
360 
361     // Inserts a new node into the node cache. If a node with the
362     // same name and type already exists in the cache, the pointer to the
363     // existing node will be returned. If there was an error inserting the node,
364     // `nullptr` will be returned.
365     NdrNodeConstPtr _InsertNodeIntoCache(const NdrNodeDiscoveryResult& dr);
366 
367     // Get a vector of all of the node unique_ptrs in the node map as raw ptrs
368     NdrNodeConstPtrVec _GetNodeMapAsNodePtrVec(const TfToken& family,
369                                                NdrVersionFilter filter) const;
370 
371     // Return the parser plugin for a discovery type. Returns null if no parser
372     // plugin has that discovery type.
373     NdrParserPlugin*
374     _GetParserForDiscoveryType(const TfToken& discoveryType) const;
375 
376     // The discovery plugins that were found through libplug and/or provided by
377     // the client
378     DiscoveryPluginRefPtrVec _discoveryPlugins;
379 
380     // The parser plugins that have been discovered via the plugin system. Maps
381     // a discovery result's "discovery type" to a specific parser.
382     TypeToParserPluginMap _parserPluginMap;
383 
384     // The parser plugins.  This has ownership of the plugin objects.
385     std::vector<std::unique_ptr<NdrParserPlugin>> _parserPlugins;
386 
387     // The preliminary discovery results prior to parsing. If accessing or
388     // mutating, _discoveryResultMutex should be used.
389     NdrNodeDiscoveryResultVec _discoveryResults;
390 
391     // Additional mapping of discovery results by grouped source type to aid in
392     // getting nodes by type priority. Stored as indices into the
393     // _disoveryResults vector.
394     std::map<TfToken, std::vector<size_t>> _discoveryResultIndicesBySourceType;
395 
396     // Maps a node's name to a node instance. If accessing or mutating,
397     // _nodeMapMutex should be used.
398     NodeMap _nodeMap;
399 };
400 
401 PXR_NAMESPACE_CLOSE_SCOPE
402 
403 #endif // PXR_USD_NDR_REGISTRY_H
404