1 //
2 // Copyright 2016 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 #include "pxr/pxr.h"
25 #include "pxr/usd/usd/schemaRegistry.h"
26 #include "pxr/usd/usd/primDefinition.h"
27 
28 #include "pxr/usd/usd/debugCodes.h"
29 #include "pxr/usd/usd/clip.h"
30 #include "pxr/usd/usd/typed.h"
31 #include "pxr/usd/usd/schemaBase.h"
32 #include "pxr/usd/usd/apiSchemaBase.h"
33 #include "pxr/usd/usd/tokens.h"
34 
35 #include "pxr/base/plug/plugin.h"
36 #include "pxr/base/plug/registry.h"
37 #include "pxr/usd/sdf/attributeSpec.h"
38 #include "pxr/usd/sdf/changeBlock.h"
39 #include "pxr/usd/sdf/primSpec.h"
40 #include "pxr/usd/sdf/propertySpec.h"
41 #include "pxr/usd/sdf/relationshipSpec.h"
42 #include "pxr/usd/sdf/schema.h"
43 
44 #include "pxr/base/tf/envSetting.h"
45 #include "pxr/base/tf/fileUtils.h"
46 #include "pxr/base/tf/instantiateSingleton.h"
47 #include "pxr/base/tf/registryManager.h"
48 #include "pxr/base/tf/token.h"
49 #include "pxr/base/tf/type.h"
50 #include "pxr/base/work/loops.h"
51 #include "pxr/base/work/withScopedParallelism.h"
52 
53 #include <set>
54 #include <utility>
55 #include <vector>
56 
57 PXR_NAMESPACE_OPEN_SCOPE
58 
59 using std::set;
60 using std::string;
61 using std::vector;
62 
63 TF_DEFINE_ENV_SETTING(
64     USD_DISABLE_PRIM_DEFINITIONS_FOR_USDGENSCHEMA, false,
65     "Set to true to disable the generation of prim definitions for schema "
66     "types in the schema registry. This is used is to prevent the processing "
67     "of generatedSchema.usda files during schema generation as it's the "
68     "process used to create, update, or fix generatedSchema.usda files. "
69     "This should only be used by usdGenSchema.py as this can cause crashes in "
70     "most contexts which expect prim definitions for schema types.");
71 
72 TF_DEFINE_ENV_SETTING(
73     USD_DISABLE_AUTO_APPLY_API_SCHEMAS, false,
74     "Set to true to disable the application of all auto-apply API schemas.");
75 
76 TF_INSTANTIATE_SINGLETON(UsdSchemaRegistry);
77 
78 TF_DEFINE_PRIVATE_TOKENS(
79     _tokens,
80     (appliedAPISchemas)
81     (multipleApplyAPISchemas)
82     (multipleApplyAPISchemaPrefixes)
83     (autoApplyAPISchemas)
84 
85     (apiSchemaAutoApplyTo)
86     (apiSchemaCanOnlyApplyTo)
87     (apiSchemaAllowedInstanceNames)
88     (apiSchemaInstances)
89     (schemaKind)
90     (nonAppliedAPI)
91     (singleApplyAPI)
92     (multipleApplyAPI)
93     (concreteTyped)
94     (abstractTyped)
95     (abstractBase)
96 
97     ((PluginAutoApplyAPISchemasKey, "AutoApplyAPISchemas"))
98 );
99 
100 using _TypeToTokenVecMap =
101     TfHashMap<TfType, TfTokenVector, TfHash>;
102 using _TokenToTokenMap =
103     TfHashMap<TfToken, TfToken, TfToken::HashFunctor>;
104 
105 namespace {
106 // Helper struct for caching a bidirecional mapping between schema TfType and
107 // USD type name token. This cache is used as a static local instance providing
108 // this type mapping without having to build the entire schema registry
109 struct _TypeMapCache {
_TypeMapCache__anond69f1a350111::_TypeMapCache110     _TypeMapCache() {
111         const TfType schemaBaseType = TfType::Find<UsdSchemaBase>();
112 
113         auto _MapDerivedTypes = [this, &schemaBaseType](
114             const TfType &baseType, bool isTyped)
115         {
116             set<TfType> types;
117             PlugRegistry::GetAllDerivedTypes(baseType, &types);
118             for (const TfType &type : types) {
119                 // The USD type name is the type's alias under UsdSchemaBase.
120                 // All schemas should have a type name alias.
121                 const vector<string> aliases = schemaBaseType.GetAliases(type);
122                 if (aliases.size() == 1) {
123                     TfToken typeName(aliases.front(), TfToken::Immortal);
124                     nameToType.insert(std::make_pair(
125                         typeName, TypeInfo(type, isTyped)));
126                     typeToName.insert(std::make_pair(
127                         type, TypeNameInfo(typeName, isTyped)));
128                 }
129             }
130         };
131 
132         _MapDerivedTypes(TfType::Find<UsdTyped>(), /*isTyped=*/true);
133         _MapDerivedTypes(TfType::Find<UsdAPISchemaBase>(), /*isTyped=*/false);
134     }
135 
136     // For each type and type name mapping we also want to store if it's a
137     // concrete prim type vs an API schema type.
138     struct TypeInfo {
139         TfType type;
140         bool isTyped;
TypeInfo__anond69f1a350111::_TypeMapCache::TypeInfo141         TypeInfo(const TfType &type_, bool isTyped_)
142             : type(type_), isTyped(isTyped_) {}
143     };
144 
145     struct TypeNameInfo {
146         TfToken name;
147         bool isTyped;
TypeNameInfo__anond69f1a350111::_TypeMapCache::TypeNameInfo148         TypeNameInfo(const TfToken &name_, bool isTyped_)
149             : name(name_), isTyped(isTyped_) {}
150     };
151 
152     TfHashMap<TfToken, TypeInfo, TfHash> nameToType;
153     TfHashMap<TfType, TypeNameInfo, TfHash> typeToName;
154 };
155 
156 // Static singleton accessor
157 static const _TypeMapCache &
_GetTypeMapCache()158 _GetTypeMapCache() {
159     static _TypeMapCache typeCache;
160     return typeCache;
161 }
162 
163 // Helper struct for caching the information extracted from plugin metadata
164 // about how API schema types are applied.
165 struct _APISchemaApplyToInfoCache {
_APISchemaApplyToInfoCache__anond69f1a350111::_APISchemaApplyToInfoCache166     _APISchemaApplyToInfoCache()
167     {
168         TRACE_FUNCTION();
169 
170         // Get all types that derive UsdSchemaBase by getting the type map
171         // cache.
172         const _TypeMapCache &typeCache = _GetTypeMapCache();
173 
174         // For each schema type, extract the can apply and auto apply plugin
175         // info into the cache.
176         for (const auto &valuePair : typeCache.typeToName) {
177             const TfType &type = valuePair.first;
178             const TfToken &typeName = valuePair.second.name;
179 
180             Usd_GetAPISchemaPluginApplyToInfoForType(
181                 type,
182                 typeName,
183                 &autoApplyAPISchemasMap,
184                 &canOnlyApplyAPISchemasMap,
185                 &allowedInstanceNamesMap);
186         }
187 
188         // Collect any plugin auto apply API schema mappings. These can be
189         // defined in any plugin to auto apply schemas in a particular
190         // application context instead of the type itself being defined to
191         // always auto apply whenever it is present.
192         UsdSchemaRegistry::CollectAddtionalAutoApplyAPISchemasFromPlugins(
193             &autoApplyAPISchemasMap);
194     }
195 
196     // Mapping of API schema type name to a list of type names it should auto
197     // applied to.
198     std::map<TfToken, TfTokenVector> autoApplyAPISchemasMap;
199 
200     // Mapping of API schema type name to a list of prim type names that it
201     // is ONLY allowed to be applied to.
202     TfHashMap<TfToken, TfTokenVector, TfHash> canOnlyApplyAPISchemasMap;
203 
204     // Mapping of multiple apply API schema type name to a set of instance names
205     // that are the only allowed instance names for that type.
206     TfHashMap<TfToken, TfToken::Set, TfHash> allowedInstanceNamesMap;
207 };
208 
209 // Static singleton accessor
210 static const _APISchemaApplyToInfoCache &
_GetAPISchemaApplyToInfoCache()211 _GetAPISchemaApplyToInfoCache() {
212     static _APISchemaApplyToInfoCache applyToInfo;
213     return applyToInfo;
214 }
215 
216 }; // end anonymous namespace
217 
218 static bool
_IsConcreteSchemaKind(const UsdSchemaKind schemaKind)219 _IsConcreteSchemaKind(const UsdSchemaKind schemaKind)
220 {
221     return schemaKind == UsdSchemaKind::ConcreteTyped;
222 }
223 
224 static bool
_IsAppliedAPISchemaKind(const UsdSchemaKind schemaKind)225 _IsAppliedAPISchemaKind(const UsdSchemaKind schemaKind)
226 {
227     return schemaKind == UsdSchemaKind::SingleApplyAPI ||
228            schemaKind == UsdSchemaKind::MultipleApplyAPI;
229 }
230 
231 static bool
_IsMultipleApplySchemaKind(const UsdSchemaKind schemaKind)232 _IsMultipleApplySchemaKind(const UsdSchemaKind schemaKind)
233 {
234     return schemaKind == UsdSchemaKind::MultipleApplyAPI;
235 }
236 
237 static UsdSchemaKind
_GetSchemaKindFromMetadata(const JsObject & dict)238 _GetSchemaKindFromMetadata(const JsObject &dict)
239 {
240     const JsValue *kindValue = TfMapLookupPtr(dict, _tokens->schemaKind);
241     if (!kindValue) {
242         return UsdSchemaKind::Invalid;
243     }
244 
245     const TfToken schemaTypeToken(kindValue->GetString());
246     if (schemaTypeToken == _tokens->nonAppliedAPI) {
247         return UsdSchemaKind::NonAppliedAPI;
248     } else if (schemaTypeToken == _tokens->singleApplyAPI) {
249         return UsdSchemaKind::SingleApplyAPI;
250     } else if (schemaTypeToken == _tokens->multipleApplyAPI) {
251         return UsdSchemaKind::MultipleApplyAPI;
252     } else if (schemaTypeToken == _tokens->concreteTyped) {
253         return UsdSchemaKind::ConcreteTyped;
254     } else if (schemaTypeToken == _tokens->abstractTyped) {
255         return UsdSchemaKind::AbstractTyped;
256     } else if (schemaTypeToken == _tokens->abstractBase) {
257         return UsdSchemaKind::AbstractBase;
258     }
259 
260     TF_CODING_ERROR("Invalid schema kind name '%s' found for plugin metadata "
261                     "key '%s'.",
262                     schemaTypeToken.GetText(), _tokens->schemaKind.GetText());
263     return UsdSchemaKind::Invalid;
264 }
265 
266 static UsdSchemaKind
_GetSchemaKindFromPlugin(const TfType & schemaType)267 _GetSchemaKindFromPlugin(const TfType &schemaType)
268 {
269     PlugPluginPtr plugin =
270         PlugRegistry::GetInstance().GetPluginForType(schemaType);
271     if (!plugin) {
272         TF_CODING_ERROR("Failed to find plugin for schema type '%s'",
273                         schemaType.GetTypeName().c_str());
274         return UsdSchemaKind::Invalid;
275     }
276 
277     return _GetSchemaKindFromMetadata(plugin->GetMetadataForType(schemaType));
278 }
279 
280 /*static*/
281 TfToken
GetSchemaTypeName(const TfType & schemaType)282 UsdSchemaRegistry::GetSchemaTypeName(const TfType &schemaType)
283 {
284     const _TypeMapCache & typeMapCache = _GetTypeMapCache();
285     auto it = typeMapCache.typeToName.find(schemaType);
286     return it != typeMapCache.typeToName.end() ? it->second.name : TfToken();
287 }
288 
289 /*static*/
290 TfToken
GetConcreteSchemaTypeName(const TfType & schemaType)291 UsdSchemaRegistry::GetConcreteSchemaTypeName(const TfType &schemaType)
292 {
293     const _TypeMapCache & typeMapCache = _GetTypeMapCache();
294     auto it = typeMapCache.typeToName.find(schemaType);
295     if (it != typeMapCache.typeToName.end() &&
296         it->second.isTyped &&
297         _IsConcreteSchemaKind(_GetSchemaKindFromPlugin(schemaType))) {
298         return it->second.name;
299     }
300     return TfToken();
301 }
302 
303 /*static*/
304 TfToken
GetAPISchemaTypeName(const TfType & schemaType)305 UsdSchemaRegistry::GetAPISchemaTypeName(const TfType &schemaType)
306 {
307     const _TypeMapCache & typeMapCache = _GetTypeMapCache();
308     auto it = typeMapCache.typeToName.find(schemaType);
309     return it != typeMapCache.typeToName.end() && !it->second.isTyped ?
310         it->second.name : TfToken();
311 }
312 
313 /*static*/
314 TfType
GetTypeFromSchemaTypeName(const TfToken & typeName)315 UsdSchemaRegistry::GetTypeFromSchemaTypeName(const TfToken &typeName)
316 {
317     const _TypeMapCache & typeMapCache = _GetTypeMapCache();
318     auto it = typeMapCache.nameToType.find(typeName);
319     return it != typeMapCache.nameToType.end() ? it->second.type : TfType();
320 }
321 
322 /*static*/
323 TfType
GetConcreteTypeFromSchemaTypeName(const TfToken & typeName)324 UsdSchemaRegistry::GetConcreteTypeFromSchemaTypeName(const TfToken &typeName)
325 {
326     const _TypeMapCache & typeMapCache = _GetTypeMapCache();
327     auto it = typeMapCache.nameToType.find(typeName);
328     if (it != typeMapCache.nameToType.end() &&
329         it->second.isTyped &&
330         _IsConcreteSchemaKind(_GetSchemaKindFromPlugin(it->second.type))) {
331         return it->second.type;
332     }
333     return TfType();
334 }
335 
336 /*static*/
337 TfType
GetAPITypeFromSchemaTypeName(const TfToken & typeName)338 UsdSchemaRegistry::GetAPITypeFromSchemaTypeName(const TfToken &typeName)
339 {
340     const _TypeMapCache & typeMapCache = _GetTypeMapCache();
341     auto it = typeMapCache.nameToType.find(typeName);
342     return it != typeMapCache.nameToType.end() && !it->second.isTyped ?
343         it->second.type : TfType();
344 }
345 
346 /*static*/
347 UsdSchemaKind
GetSchemaKind(const TfType & schemaType)348 UsdSchemaRegistry::GetSchemaKind(const TfType &schemaType)
349 {
350     const _TypeMapCache & typeMapCache = _GetTypeMapCache();
351     auto it = typeMapCache.typeToName.find(schemaType);
352     if (it == typeMapCache.typeToName.end()) {
353         // No schema kind because it is not a schema type.
354         return UsdSchemaKind::Invalid;
355     }
356     // Is a valid schema type.
357     return _GetSchemaKindFromPlugin(schemaType);
358 }
359 
360 /*static*/
361 UsdSchemaKind
GetSchemaKind(const TfToken & typeName)362 UsdSchemaRegistry::GetSchemaKind(const TfToken &typeName)
363 {
364     const _TypeMapCache & typeMapCache = _GetTypeMapCache();
365     auto it = typeMapCache.nameToType.find(typeName);
366     if (it == typeMapCache.nameToType.end()) {
367         // No schema kind because it is not a schema type.
368         return UsdSchemaKind::Invalid;
369     }
370     // Is a valid schema type.
371     return _GetSchemaKindFromPlugin(it->second.type);
372 }
373 
374 /*static*/
375 bool
IsConcrete(const TfType & primType)376 UsdSchemaRegistry::IsConcrete(const TfType& primType)
377 {
378     return _IsConcreteSchemaKind(GetSchemaKind(primType));
379 }
380 
381 /*static*/
382 bool
IsConcrete(const TfToken & primType)383 UsdSchemaRegistry::IsConcrete(const TfToken& primType)
384 {
385     return _IsConcreteSchemaKind(GetSchemaKind(primType));
386 }
387 
388 /*static*/
389 bool
IsMultipleApplyAPISchema(const TfType & apiSchemaType)390 UsdSchemaRegistry::IsMultipleApplyAPISchema(const TfType& apiSchemaType)
391 {
392     return _IsMultipleApplySchemaKind(GetSchemaKind(apiSchemaType));
393 }
394 
395 /*static*/
396 bool
IsMultipleApplyAPISchema(const TfToken & apiSchemaType)397 UsdSchemaRegistry::IsMultipleApplyAPISchema(const TfToken& apiSchemaType)
398 {
399     return _IsMultipleApplySchemaKind(GetSchemaKind(apiSchemaType));
400 }
401 
402 /*static*/
403 bool
IsAppliedAPISchema(const TfType & apiSchemaType)404 UsdSchemaRegistry::IsAppliedAPISchema(const TfType& apiSchemaType)
405 {
406     return _IsAppliedAPISchemaKind(GetSchemaKind(apiSchemaType));
407 }
408 
409 /*static*/
410 bool
IsAppliedAPISchema(const TfToken & apiSchemaType)411 UsdSchemaRegistry::IsAppliedAPISchema(const TfToken& apiSchemaType)
412 {
413     return _IsAppliedAPISchemaKind(GetSchemaKind(apiSchemaType));
414 }
415 
416 template <class T>
417 static void
_CopySpec(const T & srcSpec,const T & dstSpec)418 _CopySpec(const T &srcSpec, const T &dstSpec)
419 {
420     for (const TfToken& key : srcSpec->ListInfoKeys()) {
421         if (!UsdSchemaRegistry::IsDisallowedField(key)) {
422             dstSpec->SetInfo(key, srcSpec->GetInfo(key));
423         }
424     }
425 }
426 
427 static void
_AddSchema(SdfLayerRefPtr const & source,SdfLayerRefPtr const & target)428 _AddSchema(SdfLayerRefPtr const &source, SdfLayerRefPtr const &target)
429 {
430     for (SdfPrimSpecHandle const &prim: source->GetRootPrims()) {
431         if (!target->GetPrimAtPath(prim->GetPath())) {
432 
433             SdfPrimSpecHandle newPrim =
434                 SdfPrimSpec::New(target, prim->GetName(), prim->GetSpecifier(),
435                                  prim->GetTypeName());
436             _CopySpec(prim, newPrim);
437 
438             for (SdfAttributeSpecHandle const &attr: prim->GetAttributes()) {
439                 SdfAttributeSpecHandle newAttr =
440                     SdfAttributeSpec::New(
441                         newPrim, attr->GetName(), attr->GetTypeName(),
442                         attr->GetVariability(), attr->IsCustom());
443                 _CopySpec(attr, newAttr);
444             }
445 
446             for (SdfRelationshipSpecHandle const &rel:
447                      prim->GetRelationships()) {
448                 SdfRelationshipSpecHandle newRel =
449                     SdfRelationshipSpec::New(
450                         newPrim, rel->GetName(), rel->IsCustom());
451                 _CopySpec(rel, newRel);
452             }
453         }
454     }
455 }
456 
457 static SdfLayerRefPtr
_GetGeneratedSchema(const PlugPluginPtr & plugin)458 _GetGeneratedSchema(const PlugPluginPtr &plugin)
459 {
460     // Look for generatedSchema in Resources.
461     const string fname = TfStringCatPaths(plugin->GetResourcePath(),
462                                           "generatedSchema.usda");
463     SdfLayerRefPtr layer = SdfLayer::OpenAsAnonymous(fname);
464 
465     TF_DEBUG(USD_SCHEMA_REGISTRATION).Msg(
466        "Looking up generated schema for plugin %s at path %s. "
467        "Generated schema %s.\n",
468        plugin->GetName().c_str(),
469        fname.c_str(),
470        (layer ? "valid" : "invalid")
471     );
472     return layer;
473 }
474 
475 static TfTokenVector
_GetNameListFromMetadata(const JsObject & dict,const TfToken & key)476 _GetNameListFromMetadata(const JsObject &dict, const TfToken &key)
477 {
478     const JsValue *value = TfMapLookupPtr(dict, key);
479     if (!value) {
480         return TfTokenVector();
481     }
482 
483     if (!value->IsArrayOf<std::string>()) {
484         TF_CODING_ERROR("Plugin metadata value for key '%s' does not hold a "
485                         "string array", key.GetText());
486         return TfTokenVector();
487     }
488     return TfToTokenVector(value->GetArrayOf<std::string>());
489 }
490 
491 /*static*/
492 void
CollectAddtionalAutoApplyAPISchemasFromPlugins(std::map<TfToken,TfTokenVector> * autoApplyAPISchemas)493 UsdSchemaRegistry::CollectAddtionalAutoApplyAPISchemasFromPlugins(
494     std::map<TfToken, TfTokenVector> *autoApplyAPISchemas)
495 {
496     TRACE_FUNCTION();
497 
498     // Skip if auto apply API schemas have been disabled.
499     if (TfGetEnvSetting(USD_DISABLE_AUTO_APPLY_API_SCHEMAS)) {
500         return;
501     }
502 
503     // Check all registered plugins for metadata that may supply additional
504     // auto apply API schem mappings.
505     const PlugPluginPtrVector& plugins =
506         PlugRegistry::GetInstance().GetAllPlugins();
507     for (const PlugPluginPtr &plug : plugins) {
508 
509         // The metadata will contain a dictionary with entries of the form:
510         // "AutoApplyAPISchemas": {
511         //     "<APISchemaName1>": {
512         //         "apiSchemaAutoApplyTo": [
513         //             "<TypedSchema1>", "<TypedSchema2>"
514         //         ]
515         //     },
516         //     "<APISchemaName2>": {
517         //         "apiSchemaAutoApplyTo": [
518         //             "<TypedSchema1>", "<TypedSchema2>"
519         //         ]
520         //     }
521         // }
522         const JsObject &metadata = plug->GetMetadata();
523         const JsValue *autoApplyMetadataValue = TfMapLookupPtr(
524             metadata, _tokens->PluginAutoApplyAPISchemasKey);
525         if (!autoApplyMetadataValue) {
526             continue;
527         }
528 
529         TF_DEBUG(USD_AUTO_APPLY_API_SCHEMAS).Msg(
530             "Collecting additional auto apply API schemas from "
531             "'AutoApplyAPISchemas' metadata in plugin '%s' at path '%s'.",
532             plug->GetName().c_str(), plug->GetPath().c_str());
533 
534         const JsObject &autoApplyMetadata =
535             autoApplyMetadataValue->GetJsObject();
536         for(const auto &entry : autoApplyMetadata) {
537             if (!entry.second.IsObject()) {
538                 continue;
539             }
540 
541             TfToken apiSchemaName(entry.first);
542 
543             // The metadata for the apiSchemaAutoApplyTo list is the same as
544             // for the auto apply built in to the schema type info.
545             TfTokenVector apiSchemaAutoApplyToNames =
546                 _GetNameListFromMetadata(
547                     entry.second.GetJsObject(), _tokens->apiSchemaAutoApplyTo);
548 
549             if (!apiSchemaAutoApplyToNames.empty()) {
550 
551                 TF_DEBUG(USD_AUTO_APPLY_API_SCHEMAS).Msg(
552                     "Plugin '%s' is adding automatic application of API schema "
553                     "'%s' to the following schema types: [%s].\n",
554                     plug->GetName().c_str(), apiSchemaName.GetText(),
555                     TfStringJoin(apiSchemaAutoApplyToNames.begin(),
556                                  apiSchemaAutoApplyToNames.end(), ", ").c_str());
557 
558                 // The API schema may already have an entry in the map, in which
559                 // case we have to append to the existing entry.
560                 auto it = autoApplyAPISchemas->find(apiSchemaName);
561                 if (it == autoApplyAPISchemas->end()) {
562                     autoApplyAPISchemas->emplace(
563                         apiSchemaName, std::move(apiSchemaAutoApplyToNames));
564                 } else {
565                     it->second.insert(it->second.end(),
566                                       apiSchemaAutoApplyToNames.begin(),
567                                       apiSchemaAutoApplyToNames.end());
568                 }
569             }
570         }
571     }
572 }
573 
574 static _TypeToTokenVecMap
_GetTypeToAutoAppliedAPISchemaNames()575 _GetTypeToAutoAppliedAPISchemaNames()
576 {
577     _TypeToTokenVecMap result;
578 
579     const _TypeMapCache & typeMapCache = _GetTypeMapCache();
580 
581     for (const auto &valuePair : UsdSchemaRegistry::GetAutoApplyAPISchemas()) {
582         const TfToken &apiSchemaName = valuePair.first;
583         const TfTokenVector &autoApplyToSchemas = valuePair.second;
584 
585         // Collect all the types to apply the API schema to which includes any
586         // derived types of each of the listed types.
587         std::set<TfType> applyToTypes;
588         for (const TfToken &schemaName : autoApplyToSchemas) {
589             // The names listed are the USD type names (not the full TfType
590             // name) for abstract and concrete Typed schemas, so we need to get
591             // the actual TfType of the schema and its derived types.
592             const auto it = typeMapCache.nameToType.find(schemaName);
593             if (it != typeMapCache.nameToType.end()) {
594                 const TfType &schemaType = it->second.type;
595                 if (applyToTypes.insert(schemaType).second) {
596                     schemaType.GetAllDerivedTypes(&applyToTypes);
597                 }
598             }
599         }
600 
601         // With all the apply to types collected we can add the API schema to
602         // the list of applied schemas for each Typed schema type.
603         //
604         // Note that the auto apply API schemas map is sorted alphabetically by
605         // API schema name so this list for each prim type will also be sorted
606         // alphabetically which is intentional. This ordering is arbitrary but
607         // necessary to ensure we get a consistent strength ordering for auto
608         // applied schemas every time. In practice, schema writers should be
609         // careful to make sure that auto applied API schemas have unique
610         // property names so that application order doesn't matter, but this at
611         // least gives us consistent behavior if property name collisions occur.
612         for (const TfType &applyToType : applyToTypes) {
613             result[applyToType].push_back(apiSchemaName);
614         }
615     }
616 
617     return result;
618 }
619 
620 // Helper class for initializing the schema registry by finding all generated
621 // schema types in plugin libraries and creating the static prim definitions
622 // for all concrete and applied API schema types.
623 class UsdSchemaRegistry::_SchemaDefInitHelper
624 {
625 public:
_SchemaDefInitHelper(UsdSchemaRegistry * registry)626     _SchemaDefInitHelper(UsdSchemaRegistry *registry)
627         : _registry(registry)
628     {};
629 
FindAndBuildAllSchemaDefinitions()630     void FindAndBuildAllSchemaDefinitions()
631     {
632         // Find and load all the generated schema in plugin libraries.  We find
633         // these files adjacent to pluginfo files in libraries that provide
634         // subclasses of UsdSchemaBase.
635         _InitializePrimDefsAndSchematicsForPluginSchemas();
636 
637         // Populate multiple apply API schema definitions first. These can't
638         // include other API schemas so they're populated directly from the
639         // their prim spec in the schematics.
640         _PopulateMultipleApplyAPIPrimDefinitions();
641 
642         // Populate single apply API schema definitions second. These can
643         // include other API schemas including instances of multiple apply
644         // API schemas.
645         _PopulateSingleApplyAPIPrimDefinitions();
646 
647         // Populate all concrete API schema definitions after all API schemas
648         // they may depend on have been populated.
649         _PopulateConcretePrimDefinitions();
650     }
651 
652 private:
653     // Single apply API schemas require some processing to determine all
654     // the included API schemas and all the prim specs that need to be composed
655     // into its prim definition. This structure is used to hold this info.
656     struct _SchemaDefCompositionInfo {
657 
658         // The prim definition to compose.
659         UsdPrimDefinition *primDef;
660 
661         // The ordered list of prim specs from the schematics to compose into
662         // the prim definition. The list is actually a vector of pairs, where
663         // the pair is the prim spec and a possible instance name (for included
664         // instances of multiple apply API schemas).
665         using _SchemaPrimSpecToCompose =
666             std::pair<const SdfPrimSpecHandle, TfToken>;
667         using _SchemaPrimSpecsToCompose = std::vector<_SchemaPrimSpecToCompose>;
668         _SchemaPrimSpecsToCompose schemaPrimSpecsToCompose;
669 
670         // The expanded list of names of all API schemas that will be present
671         // in the prim definition.
672         TfTokenVector allAPISchemaNames;
673 
_SchemaDefCompositionInfoUsdSchemaRegistry::_SchemaDefInitHelper::_SchemaDefCompositionInfo674         _SchemaDefCompositionInfo(
675             UsdPrimDefinition *primDef_,
676             const TfToken &apiSchemaName,
677             const SdfPrimSpecHandle &apiSchemaPrimSpec)
678         : primDef(primDef_)
679         {
680             // We'll always compose the schema type's own prim spec.
681             schemaPrimSpecsToCompose.emplace_back(apiSchemaPrimSpec, TfToken());
682             // The schema's own name will always be the first entry in its own
683             // list of applied API schemas.
684             allAPISchemaNames.push_back(apiSchemaName);
685         }
686     };
687 
688     void _InitializePrimDefsAndSchematicsForPluginSchemas();
689 
690     bool _CollectMultipleApplyAPISchemaNamespaces(
691         const VtDictionary &customDataDict);
692 
693     void _PrependAPISchemasFromSchemaPrim(
694         const SdfPath &schematicsPrimPath,
695         TfTokenVector *appliedAPISchemas);
696 
697     void _GatherAllAPISchemaPrimSpecsToCompose(
698         _SchemaDefCompositionInfo *apiDefInfo,
699         const TfTokenVector &appliedAPISchemas) const;
700 
701     void _PopulateMultipleApplyAPIPrimDefinitions();
702     void _PopulateSingleApplyAPIPrimDefinitions();
703     void _PopulateConcretePrimDefinitions();
704 
705     UsdSchemaRegistry *_registry;
706 };
707 
708 bool
709 UsdSchemaRegistry::_SchemaDefInitHelper::
_CollectMultipleApplyAPISchemaNamespaces(const VtDictionary & customDataDict)710 _CollectMultipleApplyAPISchemaNamespaces(
711     const VtDictionary &customDataDict)
712 {
713     // Names of multiple apply API schemas are stored in their schemas
714     // in a dictionary mapping them to their property namespace prefixes.
715     // These will be useful in mapping schema instance property names
716     // to the schema property specs.
717 
718     auto it = customDataDict.find(_tokens->multipleApplyAPISchemas);
719     if (it == customDataDict.end()) {
720         return true;
721     }
722 
723     if (!it->second.IsHolding<VtDictionary>()) {
724         TF_CODING_ERROR("Found an unexpected value type for layer "
725             "customData key '%s'; expected a dictionary. Multiple apply API "
726             "schemas may be incorrect.",
727             _tokens->multipleApplyAPISchemas.GetText());
728         return false;
729     }
730 
731     bool success = true;
732     const VtDictionary &multipleApplyAPISchemas =
733         it->second.UncheckedGet<VtDictionary>();
734     for (const auto &it : multipleApplyAPISchemas) {
735         const TfToken apiSchemaName(it.first);
736 
737         if (!it.second.IsHolding<std::string>()) {
738             TF_CODING_ERROR("Found an unexpected value type for key '%s' in "
739                 "the dictionary for layer customData field '%s'; expected a "
740                 "string. Multiple apply API schema of type '%s' will not be "
741                 "correctly registered.",
742                 apiSchemaName.GetText(),
743                 _tokens->multipleApplyAPISchemas.GetText(),
744                 apiSchemaName.GetText());
745             success = false;
746             continue;
747         }
748 
749         // The property namespace is stored along side where the prim definition
750         // defining the schema's properties will be.
751         _registry->_multiApplyAPIPrimDefinitions[apiSchemaName]
752             .propertyNamespace = TfToken(it.second.UncheckedGet<std::string>());
753     }
754     return success;
755 }
756 
757 void
758 UsdSchemaRegistry::_SchemaDefInitHelper::
_InitializePrimDefsAndSchematicsForPluginSchemas()759 _InitializePrimDefsAndSchematicsForPluginSchemas()
760 {
761     // Get all types that derive from UsdSchemaBase by getting the type map
762     // cache.
763     const _TypeMapCache &typeCache = _GetTypeMapCache();
764 
765     // Gather the mapping of TfTypes to the schemas that are auto applied to
766     // those types. We need this before initializing our prim definitions. Note
767     // the prim definitions will take the API schema lists from this map in the
768     // following loop.
769     _TypeToTokenVecMap typeToAutoAppliedAPISchemaNames =
770         _GetTypeToAutoAppliedAPISchemaNames();
771 
772     // Get all the plugins that provide the types and initialize prim defintions
773     // for the found schema types.
774     std::vector<PlugPluginPtr> plugins;
775     for (const auto &valuePair : typeCache.typeToName) {
776         const TfType &type = valuePair.first;
777         const TfToken &typeName = valuePair.second.name;
778 
779         if (PlugPluginPtr plugin =
780             PlugRegistry::GetInstance().GetPluginForType(type)) {
781 
782             auto insertIt =
783                 std::lower_bound(plugins.begin(), plugins.end(), plugin);
784             if (insertIt == plugins.end() || *insertIt != plugin) {
785                 plugins.insert(insertIt, plugin);
786             }
787         }
788 
789         // We register prim definitions by the schema type name which we already
790         // grabbed from the TfType alias, and is also the name of the defining
791         // prim in the schema layer. The actual TfType's typename
792         // (i.e. C++ type name) is not a valid typename for a prim.
793         SdfPath schematicsPrimPath =
794             SdfPath::AbsoluteRootPath().AppendChild(typeName);
795 
796         // Create a new UsdPrimDefinition for this type in the appropriate map
797         // based on the type's schema kind. The prim definitions are not fully
798         // populated by the schematics here; we'll populate all these prim
799         // definitions in the rest FindAndBuildAllSchemaDefinitions.
800         const UsdSchemaKind schemaKind = _GetSchemaKindFromPlugin(type);
801         if (_IsConcreteSchemaKind(schemaKind)) {
802             UsdPrimDefinition *newPrimDef = new UsdPrimDefinition(
803                 schematicsPrimPath, /* isAPISchema = */ false);
804             _registry->_concreteTypedPrimDefinitions.emplace(
805                 typeName, newPrimDef);
806             // Check if there are any API schemas that have been setup to apply
807             // to this type. We'll set these in the prim definition's applied
808             // API schemas list so they can be processed when building out this
809             // prim definition in _PopulateConcretePrimDefinitions.
810             if (TfTokenVector *autoAppliedAPIs =
811                     TfMapLookupPtr(typeToAutoAppliedAPISchemaNames, type)) {
812                 TF_DEBUG(USD_AUTO_APPLY_API_SCHEMAS).Msg(
813                     "The prim definition for schema type '%s' has "
814                     "these additional built-in auto applied API "
815                     "schemas: [%s].\n",
816                     typeName.GetText(),
817                     TfStringJoin(autoAppliedAPIs->begin(),
818                                  autoAppliedAPIs->end(), ", ").c_str());
819 
820                 // Note that we take the API schema list from the map as the
821                 // map was only created help populate these prim definitions.
822                 newPrimDef->_appliedAPISchemas = std::move(*autoAppliedAPIs);
823             }
824         } else if (_IsAppliedAPISchemaKind(schemaKind)) {
825             UsdPrimDefinition *newPrimDef = new UsdPrimDefinition(
826                 schematicsPrimPath, /* isAPISchema = */ true);
827             if (_IsMultipleApplySchemaKind(schemaKind)) {
828                 // The multiple apply schema map stores both the prim definition
829                 // and the property prefix for the schema, but we won't know
830                 // the prefix until we read the generated schemas.
831                 _registry->_multiApplyAPIPrimDefinitions[typeName].primDef =
832                     newPrimDef;
833             } else {
834                 _registry->_singleApplyAPIPrimDefinitions.emplace(
835                     typeName, newPrimDef);
836 
837                 // Check if there are any API schemas that have been setup to
838                 // apply to this API schema type. We'll set these in the prim
839                 // definition's applied API schemas list so they can be
840                 // processed when building out this prim definition in
841                 // _PopulateSingleApplyAPIPrimDefinitions.
842                 if (TfTokenVector *autoAppliedAPIs =
843                         TfMapLookupPtr(typeToAutoAppliedAPISchemaNames, type)) {
844                     TF_DEBUG(USD_AUTO_APPLY_API_SCHEMAS).Msg(
845                         "The prim definition for API schema type '%s' has "
846                         "these additional built-in auto applied API "
847                         "schemas: [%s].\n",
848                         typeName.GetText(),
849                         TfStringJoin(autoAppliedAPIs->begin(),
850                                      autoAppliedAPIs->end(), ", ").c_str());
851 
852                     // Note that we take the API schema list from the map as the
853                     // map was only created help populate these prim definitions.
854                     newPrimDef->_appliedAPISchemas = std::move(*autoAppliedAPIs);
855                 }
856             }
857         }
858     }
859 
860     // For each plugin, if it has generated schema, add it to the schematics.
861     std::vector<SdfLayerRefPtr> generatedSchemas(plugins.size());
862     WorkWithScopedParallelism([&plugins, &generatedSchemas]() {
863             WorkParallelForN(
864                 plugins.size(),
865                 [&plugins, &generatedSchemas](size_t begin, size_t end) {
866                     for (; begin != end; ++begin) {
867                         generatedSchemas[begin] =
868                             _GetGeneratedSchema(plugins[begin]);
869                     }
870                 });
871         });
872 
873     SdfChangeBlock block;
874 
875     for (const SdfLayerRefPtr& generatedSchema : generatedSchemas) {
876         if (generatedSchema) {
877             VtDictionary customDataDict = generatedSchema->GetCustomLayerData();
878 
879             bool hasErrors = false;
880 
881             // This collects all the multiple apply API schema namespaces
882             // prefixes that are defined in the generated schema and stores
883             // them in the prim definition map entry that will contain the
884             // prim definition for the corresponding multiple apply API schema
885             // name.
886             if (!_CollectMultipleApplyAPISchemaNamespaces(customDataDict)) {
887                 hasErrors = true;
888             }
889 
890             _AddSchema(generatedSchema, _registry->_schematics);
891 
892             // Schema generation will have added any defined fallback prim
893             // types as a dictionary in layer metadata which will be composed
894             // into the single fallback types dictionary.
895             VtDictionary generatedFallbackPrimTypes;
896             if (generatedSchema->HasField(SdfPath::AbsoluteRootPath(),
897                                           UsdTokens->fallbackPrimTypes,
898                                           &generatedFallbackPrimTypes)) {
899                 for (const auto &it: generatedFallbackPrimTypes) {
900                     if (it.second.IsHolding<VtTokenArray>()) {
901                         _registry->_fallbackPrimTypes.insert(it);
902                     } else {
903                         TF_CODING_ERROR("Found a VtTokenArray value for type "
904                             "name key '%s' in fallbackPrimTypes layer metadata "
905                             "dictionary in generated schema file '%s'.",
906                             it.first.c_str(),
907                             generatedSchema->GetRealPath().c_str());
908                     }
909                 }
910             }
911 
912             if (hasErrors) {
913                 TF_CODING_ERROR(
914                     "Encountered errors in layer metadata from generated "
915                     "schema file '%s'. This schema must be regenerated.",
916                     generatedSchema->GetRealPath().c_str());
917             }
918         }
919     }
920 }
921 
922 // Helper that gets the authored API schemas from the schema prim path in the
923 // schematics layer and prepends them to the given applied API schemas list.
924 void
_PrependAPISchemasFromSchemaPrim(const SdfPath & schematicsPrimPath,TfTokenVector * appliedAPISchemas)925 UsdSchemaRegistry::_SchemaDefInitHelper::_PrependAPISchemasFromSchemaPrim(
926     const SdfPath &schematicsPrimPath,
927     TfTokenVector *appliedAPISchemas)
928 {
929     // Get the API schemas from the list op field in the schematics.
930     SdfTokenListOp apiSchemasListOp;
931     if (!_registry->_schematics->HasField(
932             schematicsPrimPath, UsdTokens->apiSchemas, &apiSchemasListOp)) {
933         return;
934     }
935     TfTokenVector apiSchemas;
936     apiSchemasListOp.ApplyOperations(&apiSchemas);
937     if (apiSchemas.empty()) {
938         return;
939     }
940 
941     // If the existing list has API schemas, append them to list we just pulled
942     // from the schematics before replacing the existing list with the new list.
943     if (!appliedAPISchemas->empty()) {
944         apiSchemas.insert(
945             apiSchemas.end(),
946             appliedAPISchemas->begin(),
947             appliedAPISchemas->end());
948     }
949     *appliedAPISchemas = std::move(apiSchemas);
950 }
951 
952 // For the single API schema prim definition in depCompInfo, that is in the
953 // process of being built, this takes the list of appliedAPISchemas and
954 // recursively gathers all the API schemas that the prim definition needs to
955 // include as well as the prim specs that will provide the properties. This
956 // is all stored back in the depCompInfo in preparation for the final population
957 // of the prim definition. This step is expected to be run after all prim
958 // definitions representing API schemas have been established with their
959 // directly included API schemas. but before any of the API schema prim
960 // definitions have been updated with their fully expanded API schemas list.
961 void
_GatherAllAPISchemaPrimSpecsToCompose(_SchemaDefCompositionInfo * defCompInfo,const TfTokenVector & appliedAPISchemas) const962 UsdSchemaRegistry::_SchemaDefInitHelper::_GatherAllAPISchemaPrimSpecsToCompose(
963     _SchemaDefCompositionInfo *defCompInfo,
964     const TfTokenVector &appliedAPISchemas) const
965 {
966     for (const TfToken &apiSchemaName : appliedAPISchemas) {
967         // This mainly to avoid API schema inclusion cycles but also has the
968         // added benefit of avoiding duplicates if included API schemas
969         // themselves include the same other API schema.
970         //
971         // Note that we linear search the vector of API schema names. This
972         // vector is expected to be small and shouldn't be a performance
973         // concern. But it's something to be aware of in case it does cause
974         // performance problems in the future, especially since by far the most
975         // common case will be that we don't find the name in the list.
976         if (std::find(defCompInfo->allAPISchemaNames.begin(),
977                       defCompInfo->allAPISchemaNames.end(),
978                       apiSchemaName)
979                 != defCompInfo->allAPISchemaNames.end()) {
980             continue;
981         }
982 
983         // Find the registered prim definition (and property prefix if it's a
984         // multiple apply instance). Skip this API if we can't find a def.
985         std::string propPrefix;
986         const UsdPrimDefinition *apiSchemaTypeDef =
987             _registry->_FindAPIPrimDefinitionByFullName(
988                 apiSchemaName, &propPrefix);
989         if (!apiSchemaTypeDef) {
990             continue;
991         }
992 
993         // The found API schema def may not be fully populated at this point,
994         // but it will always have its prim spec path stored. Get the prim spec
995         // for this API def.
996         SdfPrimSpecHandle primSpec =
997             _registry->_schematics->GetPrimAtPath(
998                 apiSchemaTypeDef->_schematicsPrimPath);
999         if (!primSpec) {
1000             continue;
1001         }
1002 
1003         // Add this API schema and its prim spec to the composition
1004         defCompInfo->schemaPrimSpecsToCompose.emplace_back(
1005             primSpec, propPrefix);
1006         defCompInfo->allAPISchemaNames.push_back(apiSchemaName);
1007 
1008         // At this point in initialization, all API schemas prim defs will
1009         // have their directly included API schemas set in the definition, but
1010         // will not have had them expanded to include APIs included from other
1011         // APIs. Thus, we can do a depth first recursion on the current applied
1012         // API schemas of the API prim definition to continue expanding the
1013         // full list of API schemas and prim specs to compose in strength order.
1014         _GatherAllAPISchemaPrimSpecsToCompose(
1015             defCompInfo,
1016             apiSchemaTypeDef->GetAppliedAPISchemas());
1017     }
1018 }
1019 
1020 void
1021 UsdSchemaRegistry::_SchemaDefInitHelper::
_PopulateMultipleApplyAPIPrimDefinitions()1022 _PopulateMultipleApplyAPIPrimDefinitions()
1023 {
1024     // Populate all multiple apply API schema definitions. These can't
1025     // include other API schemas so they're populated directly from the
1026     // their prim spec in the schematics.
1027     for (auto &nameAndDefPtr : _registry->_multiApplyAPIPrimDefinitions) {
1028         UsdPrimDefinition *&primDef = nameAndDefPtr.second.primDef;
1029         if (!TF_VERIFY(primDef)) {
1030             continue;
1031         }
1032 
1033         SdfPrimSpecHandle primSpec =
1034             _registry->_schematics->GetPrimAtPath(primDef->_schematicsPrimPath);
1035         if (!primSpec) {
1036             // XXX: This, and the warnings below, should probably be coding
1037             // errors. However a coding error here causes usdGenSchema to
1038             // fail when there are plugin schema types that are missing a
1039             // generated schema prim spec. Since running usdGenSchema is how
1040             // you'd fix this error, that's not helpful.
1041             TF_WARN("Could not find a prim spec at path '%s' in the "
1042                     "schematics layer for registered multiple apply "
1043                     "API schema '%s'. Schemas need to be regenerated.",
1044                     primDef->_schematicsPrimPath.GetText(),
1045                     nameAndDefPtr.first.GetText());
1046             continue;
1047         }
1048 
1049         // Compose the properties from the prim spec to the prim definition.
1050         primDef->_ComposePropertiesFromPrimSpec(primSpec);
1051     }
1052 }
1053 
1054 void
1055 UsdSchemaRegistry::_SchemaDefInitHelper::
_PopulateSingleApplyAPIPrimDefinitions()1056 _PopulateSingleApplyAPIPrimDefinitions()
1057 {
1058     // Single apply schemas are more complicated. These may contain other
1059     // applied API schemas which may also include other API schemas. To populate
1060     // their properties correctly, we must do this in multiple passes.
1061     std::vector<_SchemaDefCompositionInfo> apiSchemasWithAppliedSchemas;
1062 
1063     // Step 1. For each single apply API schema, we determine what (if any)
1064     // applied API schemas it has. If it has none, we can just populate its
1065     // prim definition from the schematics prim spec and be done. Otherwise we
1066     // need to store the directly included built-in API schemas now and process
1067     // them in the next pass once we know ALL the API schemas that every single
1068     // apply API includes.
1069     for (auto &nameAndDefPtr : _registry->_singleApplyAPIPrimDefinitions) {
1070         const TfToken &usdTypeNameToken = nameAndDefPtr.first;
1071         UsdPrimDefinition *&primDef = nameAndDefPtr.second;
1072         if (!TF_VERIFY(primDef)) {
1073             continue;
1074         }
1075 
1076         SdfPrimSpecHandle primSpec =
1077             _registry->_schematics->GetPrimAtPath(primDef->_schematicsPrimPath);
1078         if (!primSpec) {
1079             TF_WARN("Could not find a prim spec at path '%s' in the "
1080                     "schematics layer for registered single apply "
1081                     "API schema '%s'. Schemas need to be regenerated.",
1082                     primDef->_schematicsPrimPath.GetText(),
1083                     nameAndDefPtr.first.GetText());
1084             continue;
1085         }
1086 
1087         // During initialization, any auto applied API schema names relevant
1088         // to this API schema type were put in prim definition's applied API
1089         // schema list. There may be applied API schemas defined in the metadata
1090         // for the type in the schematics. If so, these must be prepended to the
1091         // collected auto applied schema names (auto apply API schemas are
1092         // weaker) to get full list of API schemas that need to be composed into
1093         // this prim definition.
1094         _PrependAPISchemasFromSchemaPrim(
1095             primDef->_schematicsPrimPath,
1096             &primDef->_appliedAPISchemas);
1097 
1098         if (primDef->_appliedAPISchemas.empty()) {
1099             // If there are no API schemas to apply to this schema, we can just
1100             // apply the prim specs properties and be done.
1101             primDef->_ComposePropertiesFromPrimSpec(primSpec);
1102             // We always include the API schema itself as an applied API schema
1103             // in its prim definition. Note that we don't do this for multiple
1104             // apply schema definitions which cannot be applied without an
1105             // instance name.
1106             primDef->_appliedAPISchemas = {usdTypeNameToken};
1107         } else {
1108             apiSchemasWithAppliedSchemas.emplace_back(
1109                 primDef, usdTypeNameToken, primSpec);
1110         }
1111     }
1112 
1113     // Step 2. For each single apply API that has other applied API schemas,
1114     // recursively gather the fully expanded list of API schemas and their
1115     // corresponding prim specs that will be used to populate the prim
1116     // definition's properties.
1117     //
1118     // We specifically do this step here because each API schema prim
1119     // definition will have only its direct built-in API schemas in its list
1120     // allowing us to recurse without cycling. Only once we've gathered what
1121     // will be the fully expanded list of API schemas for all of them can we
1122     // start to populate the API schemas with all their properties.
1123     for (_SchemaDefCompositionInfo &defCompInfo : apiSchemasWithAppliedSchemas) {
1124         _GatherAllAPISchemaPrimSpecsToCompose(
1125             &defCompInfo,
1126             defCompInfo.primDef->_appliedAPISchemas);
1127     }
1128 
1129     // Step 3. For each single apply API schema from step 2, we can update the
1130     // prim definition by applying the properties from all the gathered prim
1131     // specs and setting the fully expanded list of API schemas (which will
1132     // include itself).
1133     for (_SchemaDefCompositionInfo &defCompInfo : apiSchemasWithAppliedSchemas) {
1134         for (const auto &primSpecAndPropPrefix :
1135                 defCompInfo.schemaPrimSpecsToCompose) {
1136             defCompInfo.primDef->_ComposePropertiesFromPrimSpec(
1137                 primSpecAndPropPrefix.first,
1138                 primSpecAndPropPrefix.second);
1139         }
1140         defCompInfo.primDef->_appliedAPISchemas =
1141             std::move(defCompInfo.allAPISchemaNames);
1142     }
1143 }
1144 
1145 void
1146 UsdSchemaRegistry::_SchemaDefInitHelper::
_PopulateConcretePrimDefinitions()1147 _PopulateConcretePrimDefinitions()
1148 {
1149     // Populate all concrete API schema definitions; it is expected that all
1150     // API schemas, which these may depend on, have already been populated.
1151     for (auto &nameAndDefPtr : _registry->_concreteTypedPrimDefinitions) {
1152         UsdPrimDefinition *&primDef = nameAndDefPtr.second;
1153         if (!TF_VERIFY(primDef)) {
1154             continue;
1155         }
1156 
1157         SdfPrimSpecHandle primSpec =
1158             _registry->_schematics->GetPrimAtPath(primDef->_schematicsPrimPath);
1159         if (!primSpec) {
1160             TF_WARN("Could not find a prim spec at path '%s' in the "
1161                     "schematics layer for registered concrete typed "
1162                     "schema '%s'. Schemas need to be regenerated.",
1163                     primDef->_schematicsPrimPath.GetText(),
1164                     nameAndDefPtr.first.GetText());
1165             continue;
1166         }
1167 
1168         // During initialization, any auto applied API schema names relevant
1169         // to this prim type were put in prim definition's applied API schema
1170         // list. There may be applied API schemas defined in the metadata for
1171         // the type in the schematics. If so, these must be prepended to the
1172         // collected auto applied schema names (auto apply API schemas are
1173         // weaker) to get full list of API schemas that need to be composed into
1174         // this prim definition.
1175         _PrependAPISchemasFromSchemaPrim(
1176             primDef->_schematicsPrimPath,
1177             &primDef->_appliedAPISchemas);
1178 
1179         // Compose the properties from the prim spec to the prim definition
1180         // first as these are stronger than the built-in API schema properties.
1181         primDef->_ComposePropertiesFromPrimSpec(primSpec);
1182 
1183         // If there are any applied API schemas in the list, compose them
1184         // in now
1185         if (!primDef->_appliedAPISchemas.empty()) {
1186             // We've stored the names of all the API schemas we're going to
1187             // compose in the primDef's _appliedAPISchemas even though we
1188             // haven't composed in any of their properties yet. In addition to
1189             // composing in properties, _ComposeAPISchemasIntoPrimDefinition
1190             // will also append the API schemas it receives to the primDef's
1191             // _appliedAPISchemas. Thus, we copy _appliedAPISchemas to a
1192             // temporary and clear it first so that we don't end up with the
1193             // entire contents of list appended to itself again.
1194             TfTokenVector apiSchemasToCompose =
1195                 std::move(primDef->_appliedAPISchemas);
1196             primDef->_appliedAPISchemas.clear();
1197             _registry->_ComposeAPISchemasIntoPrimDefinition(
1198                 primDef, apiSchemasToCompose);
1199         }
1200     }
1201 }
1202 
UsdSchemaRegistry()1203 UsdSchemaRegistry::UsdSchemaRegistry()
1204 {
1205     _schematics = SdfLayer::CreateAnonymous("registry.usda");
1206     _emptyPrimDefinition = new UsdPrimDefinition();
1207 
1208     // Find and load all the generated schema in plugin libraries and build all
1209     // the schema prim definitions.
1210     if (!TfGetEnvSetting(USD_DISABLE_PRIM_DEFINITIONS_FOR_USDGENSCHEMA)) {
1211         _SchemaDefInitHelper schemaDefHelper(this);
1212         schemaDefHelper.FindAndBuildAllSchemaDefinitions();
1213     }
1214 
1215     TfSingleton<UsdSchemaRegistry>::SetInstanceConstructed(*this);
1216     TfRegistryManager::GetInstance().SubscribeTo<UsdSchemaRegistry>();
1217 }
1218 
1219 /*static*/
1220 bool
IsDisallowedField(const TfToken & fieldName)1221 UsdSchemaRegistry::IsDisallowedField(const TfToken &fieldName)
1222 {
1223     static TfHashSet<TfToken, TfToken::HashFunctor> disallowedFields;
1224 
1225     // XXX -- Use this instead of an initializer list in case TfHashSet
1226     //        doesn't support initializer lists.  Should ensure that
1227     //        TfHashSet does support them.
1228     static std::once_flag once;
1229     std::call_once(once, [](){
1230         // Disallow fallback values for composition arc fields, since they
1231         // won't be used during composition.
1232         disallowedFields.insert(SdfFieldKeys->InheritPaths);
1233         disallowedFields.insert(SdfFieldKeys->Payload);
1234         disallowedFields.insert(SdfFieldKeys->References);
1235         disallowedFields.insert(SdfFieldKeys->Specializes);
1236         disallowedFields.insert(SdfFieldKeys->VariantSelection);
1237         disallowedFields.insert(SdfFieldKeys->VariantSetNames);
1238 
1239         // Disallow customData, since it contains information used by
1240         // usdGenSchema that isn't relevant to other consumers.
1241         disallowedFields.insert(SdfFieldKeys->CustomData);
1242 
1243         // Disallow fallback values for these fields, since they won't be
1244         // used during scenegraph population or value resolution.
1245         disallowedFields.insert(SdfFieldKeys->Active);
1246         disallowedFields.insert(SdfFieldKeys->Instanceable);
1247         disallowedFields.insert(SdfFieldKeys->TimeSamples);
1248         disallowedFields.insert(SdfFieldKeys->ConnectionPaths);
1249         disallowedFields.insert(SdfFieldKeys->TargetPaths);
1250 
1251         // Disallow fallback values for specifier. Even though it will always
1252         // be present, it has no meaning as a fallback value.
1253         disallowedFields.insert(SdfFieldKeys->Specifier);
1254 
1255         // Disallow fallback values for children fields.
1256         disallowedFields.insert(SdfChildrenKeys->allTokens.begin(),
1257                                 SdfChildrenKeys->allTokens.end());
1258 
1259         // Disallow fallback values for clip-related fields, since they won't
1260         // be used during value resolution.
1261         const std::vector<TfToken> clipFields = UsdGetClipRelatedFields();
1262         disallowedFields.insert(clipFields.begin(), clipFields.end());
1263     });
1264 
1265     return (disallowedFields.find(fieldName) != disallowedFields.end());
1266 }
1267 
1268 /*static*/
1269 bool
IsTyped(const TfType & primType)1270 UsdSchemaRegistry::IsTyped(const TfType& primType)
1271 {
1272     return primType.IsA<UsdTyped>();
1273 }
1274 
1275 TfType
GetTypeFromName(const TfToken & typeName)1276 UsdSchemaRegistry::GetTypeFromName(const TfToken& typeName){
1277     static const TfType schemaBaseType = TfType::Find<UsdSchemaBase>();
1278     return PlugRegistry::GetInstance().FindDerivedTypeByName(
1279         schemaBaseType, typeName.GetString());
1280 }
1281 
1282 std::pair<TfToken, TfToken>
GetTypeNameAndInstance(const TfToken & apiSchemaName)1283 UsdSchemaRegistry::GetTypeNameAndInstance(const TfToken &apiSchemaName)
1284 {
1285     // Try to split the string at the first namespace delimiter. We always use
1286     // the first as type names can not have embedded namespaces but instances
1287     // names can.
1288     const char namespaceDelimiter =
1289         SdfPathTokens->namespaceDelimiter.GetText()[0];
1290     const std::string &typeString = apiSchemaName.GetString();
1291     size_t delim = typeString.find(namespaceDelimiter);
1292     // If the delimiter is not found, we have a single apply API schema and
1293     // no instance name.
1294     if (delim == std::string::npos) {
1295         return std::make_pair(apiSchemaName, TfToken());
1296     } else {
1297         return std::make_pair(TfToken(typeString.substr(0, delim)),
1298                               TfToken(typeString.c_str() + delim + 1));
1299     }
1300 }
1301 
1302 /*static*/
1303 const std::map<TfToken, TfTokenVector> &
GetAutoApplyAPISchemas()1304 UsdSchemaRegistry::GetAutoApplyAPISchemas()
1305 {
1306     return _GetAPISchemaApplyToInfoCache().autoApplyAPISchemasMap;
1307 }
1308 
1309 /*static*/
1310 bool
IsAllowedAPISchemaInstanceName(const TfToken & apiSchemaName,const TfToken & instanceName)1311 UsdSchemaRegistry::IsAllowedAPISchemaInstanceName(
1312     const TfToken &apiSchemaName, const TfToken &instanceName)
1313 {
1314     // Verify we have a multiple apply API schema and a non-empty instance name.
1315     if (instanceName.IsEmpty() || !IsMultipleApplyAPISchema(apiSchemaName)) {
1316         return false;
1317     }
1318 
1319     // A multiple apply schema may specify a list of instance names that
1320     // are allowed for it. If so we check for that here. If no list of
1321     // instance names exist or it is empty, then any valid instance name is
1322     // allowed.
1323     const TfHashMap<TfToken, TfToken::Set, TfHash> &allowedInstanceNamesMap =
1324         _GetAPISchemaApplyToInfoCache().allowedInstanceNamesMap;
1325     if (const TfToken::Set *allowedInstanceNames =
1326             TfMapLookupPtr(allowedInstanceNamesMap, apiSchemaName)) {
1327         if (!allowedInstanceNames->empty() &&
1328             !allowedInstanceNames->count(instanceName)) {
1329             return false;
1330         }
1331     }
1332 
1333     // In all cases, we don't allow instance names whose base name matches the
1334     // name of a property of the API schema. We check the prim definition for
1335     // this.
1336     const UsdPrimDefinition *apiSchemaDef =
1337         GetInstance().FindAppliedAPIPrimDefinition(apiSchemaName);
1338     if (!apiSchemaDef) {
1339         TF_CODING_ERROR("Could not find UsdPrimDefinition for multiple apply "
1340                         "API schema '%s'", apiSchemaName.GetText());
1341         return false;
1342     }
1343 
1344     const TfTokenVector tokens =
1345         SdfPath::TokenizeIdentifierAsTokens(instanceName);
1346     if (tokens.empty()) {
1347         return false;
1348     }
1349 
1350     const TfToken &baseName = tokens.back();
1351     if (apiSchemaDef->_propPathMap.count(baseName)) {
1352         return false;
1353     }
1354 
1355     return true;
1356 }
1357 
1358 const TfTokenVector &
GetAPISchemaCanOnlyApplyToTypeNames(const TfToken & apiSchemaName,const TfToken & instanceName)1359 UsdSchemaRegistry::GetAPISchemaCanOnlyApplyToTypeNames(
1360     const TfToken &apiSchemaName, const TfToken &instanceName)
1361 {
1362     const TfHashMap<TfToken, TfTokenVector, TfHash> &canOnlyApplyToMap =
1363         _GetAPISchemaApplyToInfoCache().canOnlyApplyAPISchemasMap;
1364 
1365     if (!instanceName.IsEmpty()) {
1366         // It's possible that specific instance names of the schema can only be
1367         // applied to the certain types. If a list of "can only apply to" types
1368         // is exists for the given instance, we use it.
1369         TfToken fullApiSchemaName(
1370             SdfPath::JoinIdentifier(apiSchemaName, instanceName));
1371         if (const TfTokenVector *canOnlyApplyToTypeNames =
1372             TfMapLookupPtr(canOnlyApplyToMap, fullApiSchemaName)) {
1373             return *canOnlyApplyToTypeNames;
1374         }
1375     }
1376 
1377     // Otherwise, no there's no instance specific list, so try to find one just
1378     // from the API schema type name.
1379     if (const TfTokenVector *canOnlyApplyToTypeNames =
1380         TfMapLookupPtr(canOnlyApplyToMap, apiSchemaName)) {
1381         return *canOnlyApplyToTypeNames;
1382     }
1383 
1384     static const TfTokenVector empty;
1385     return empty;
1386 }
1387 
1388 TfToken
GetPropertyNamespacePrefix(const TfToken & multiApplyAPISchemaName) const1389 UsdSchemaRegistry::GetPropertyNamespacePrefix(
1390     const TfToken &multiApplyAPISchemaName) const
1391 {
1392     if (const _MultipleApplyAPIDefinition *def = TfMapLookupPtr(
1393             _multiApplyAPIPrimDefinitions, multiApplyAPISchemaName)) {
1394         return def->propertyNamespace;
1395     }
1396     return TfToken();
1397 }
1398 
1399 std::unique_ptr<UsdPrimDefinition>
BuildComposedPrimDefinition(const TfToken & primType,const TfTokenVector & appliedAPISchemas) const1400 UsdSchemaRegistry::BuildComposedPrimDefinition(
1401     const TfToken &primType, const TfTokenVector &appliedAPISchemas) const
1402 {
1403     if (appliedAPISchemas.empty()) {
1404         TF_CODING_ERROR("BuildComposedPrimDefinition without applied API "
1405                         "schemas is not allowed. If you want a prim definition "
1406                         "for a single prim type with no appied schemas, use "
1407                         "FindConcretePrimDefinition instead.");
1408         return std::unique_ptr<UsdPrimDefinition>();
1409     }
1410 
1411     // Start with a new prim definition mapped to the same prim spec path as the
1412     // prim type's definition. This does not yet add the prim type's properties.
1413     // Note that its perfectly valid for there to be no prim definition for the
1414     // given prim type, in which case we compose API schemas over an empty prim
1415     // definition.
1416     const UsdPrimDefinition *primDef = FindConcretePrimDefinition(primType);
1417     std::unique_ptr<UsdPrimDefinition> composedPrimDef(primDef ?
1418         new UsdPrimDefinition(
1419             primDef->_schematicsPrimPath, /* isAPISchema = */ false) :
1420         new UsdPrimDefinition());
1421 
1422     // We compose in the new API schemas first as these API schema property
1423     // opinions need to be stronger than the prim type's prim definition's
1424     // opinions.
1425     _ComposeAPISchemasIntoPrimDefinition(
1426         composedPrimDef.get(), appliedAPISchemas);
1427 
1428     // Now compose in the prim type's properties if we can.
1429     if (primDef) {
1430         composedPrimDef->_ComposePropertiesFromPrimDef(*primDef);
1431         // The prim type's prim definition may have its own built-in API
1432         // schemas (which were already composed into its definition). We need
1433         // to append these to applied API schemas list for our composed prim
1434         // definition.
1435         composedPrimDef->_appliedAPISchemas.insert(
1436             composedPrimDef->_appliedAPISchemas.end(),
1437             primDef->_appliedAPISchemas.begin(),
1438             primDef->_appliedAPISchemas.end());
1439     }
1440 
1441     return composedPrimDef;
1442 }
1443 
1444 const UsdPrimDefinition *
_FindAPIPrimDefinitionByFullName(const TfToken & apiSchemaName,std::string * propertyPrefix) const1445 UsdSchemaRegistry::_FindAPIPrimDefinitionByFullName(
1446     const TfToken &apiSchemaName,
1447     std::string *propertyPrefix) const
1448 {
1449     // Applied schemas may be single or multiple apply so we have to parse
1450     // the full schema name into a type and possibly an instance name.
1451     auto typeNameAndInstance = GetTypeNameAndInstance(apiSchemaName);
1452     const TfToken &typeName = typeNameAndInstance.first;
1453     const TfToken &instanceName = typeNameAndInstance.second;
1454 
1455     // If the instance name is empty we expect a single apply API schema
1456     // otherwise it should be a multiple apply API.
1457     if (instanceName.IsEmpty()) {
1458         if (const UsdPrimDefinition * const *apiSchemaTypeDef =
1459                 TfMapLookupPtr(_singleApplyAPIPrimDefinitions, typeName)) {
1460             return *apiSchemaTypeDef;
1461         }
1462     } else {
1463         const _MultipleApplyAPIDefinition *multiApplyDef =
1464             TfMapLookupPtr(_multiApplyAPIPrimDefinitions, typeName);
1465         if (multiApplyDef) {
1466             // We also provide the full property namespace prefix for this
1467             // particular instance of the multiple apply API.
1468             *propertyPrefix = SdfPath::JoinIdentifier(
1469                 multiApplyDef->propertyNamespace, instanceName);
1470             return multiApplyDef->primDef;
1471         }
1472     }
1473 
1474     return nullptr;
1475 }
1476 
_ComposeAPISchemasIntoPrimDefinition(UsdPrimDefinition * primDef,const TfTokenVector & appliedAPISchemas) const1477 void UsdSchemaRegistry::_ComposeAPISchemasIntoPrimDefinition(
1478     UsdPrimDefinition *primDef, const TfTokenVector &appliedAPISchemas) const
1479 {
1480     // Add in properties from each new applied API schema. Applied API schemas
1481     // are ordered strongest to weakest so we compose, in order, each weaker
1482     // schema's properties.
1483     for (const TfToken &apiSchemaName : appliedAPISchemas) {
1484 
1485         std::string propPrefix;
1486         const UsdPrimDefinition *apiSchemaTypeDef =
1487             _FindAPIPrimDefinitionByFullName(apiSchemaName, &propPrefix);
1488 
1489         if (apiSchemaTypeDef) {
1490             // Compose in the properties from the API schema def.
1491             primDef->_ComposePropertiesFromPrimDef(
1492                 *apiSchemaTypeDef, propPrefix);
1493 
1494             // Append all the API schemas included in the schema def to the
1495             // prim def's API schemas list.
1496             const TfTokenVector &apiSchemasToAppend =
1497                 apiSchemaTypeDef->GetAppliedAPISchemas();
1498 
1499             if (apiSchemasToAppend.empty()) {
1500                 // The API def's applied API schemas list will be empty if it's
1501                 // multiple apply API so in that case we append the schema name.
1502                 primDef->_appliedAPISchemas.push_back(apiSchemaName);
1503             } else {
1504                 // Otherwise, it's a single apply API and its definition's API
1505                 // schemas list will hold itself followed by all other API
1506                 // schemas that were composed into its definition.
1507                 primDef->_appliedAPISchemas.insert(
1508                     primDef->_appliedAPISchemas.end(),
1509                     apiSchemasToAppend.begin(), apiSchemasToAppend.end());
1510             }
1511         }
1512     }
1513 }
1514 
1515 void
Usd_GetAPISchemaPluginApplyToInfoForType(const TfType & apiSchemaType,const TfToken & apiSchemaName,std::map<TfToken,TfTokenVector> * autoApplyAPISchemasMap,TfHashMap<TfToken,TfTokenVector,TfHash> * canOnlyApplyAPISchemasMap,TfHashMap<TfToken,TfToken::Set,TfHash> * allowedInstanceNamesMap)1516 Usd_GetAPISchemaPluginApplyToInfoForType(
1517     const TfType &apiSchemaType,
1518     const TfToken &apiSchemaName,
1519     std::map<TfToken, TfTokenVector> *autoApplyAPISchemasMap,
1520     TfHashMap<TfToken, TfTokenVector, TfHash> *canOnlyApplyAPISchemasMap,
1521     TfHashMap<TfToken, TfToken::Set, TfHash> *allowedInstanceNamesMap)
1522 {
1523     PlugPluginPtr plugin =
1524         PlugRegistry::GetInstance().GetPluginForType(apiSchemaType);
1525     if (!plugin) {
1526         TF_CODING_ERROR("Failed to find plugin for schema type '%s'",
1527                         apiSchemaType.GetTypeName().c_str());
1528         return;
1529     }
1530 
1531     // We don't load the plugin, we just use its metadata.
1532     const JsObject dict = plugin->GetMetadataForType(apiSchemaType);
1533 
1534     // Skip types that aren't applied API schemas
1535     const UsdSchemaKind schemaKind = _GetSchemaKindFromMetadata(dict);
1536     if (!_IsAppliedAPISchemaKind(schemaKind)) {
1537         return;
1538     }
1539 
1540     // Both single and multiple apply API schema types can have metadata
1541     // specifying the list that the type can only be applied to.
1542     TfTokenVector canOnlyApplyToTypeNames =
1543         _GetNameListFromMetadata(dict, _tokens->apiSchemaCanOnlyApplyTo);
1544     if (!canOnlyApplyToTypeNames.empty()) {
1545         (*canOnlyApplyAPISchemasMap)[apiSchemaName] =
1546             std::move(canOnlyApplyToTypeNames);
1547     }
1548 
1549     if (schemaKind == UsdSchemaKind::SingleApplyAPI) {
1550         // Skip if auto apply API schemas have been disabled.
1551         if (TfGetEnvSetting(USD_DISABLE_AUTO_APPLY_API_SCHEMAS)) {
1552             return;
1553         }
1554 
1555         // For single apply API schemas, we can get the types it should auto
1556         // apply to.
1557         TfTokenVector autoApplyToTypeNames =
1558             _GetNameListFromMetadata(dict, _tokens->apiSchemaAutoApplyTo);
1559         if (!autoApplyToTypeNames.empty()) {
1560              TF_DEBUG(USD_AUTO_APPLY_API_SCHEMAS).Msg(
1561                  "API schema '%s' is defined to auto apply to the following "
1562                  "schema types: [%s].\n",
1563                  apiSchemaName.GetText(),
1564                  TfStringJoin(autoApplyToTypeNames.begin(),
1565                               autoApplyToTypeNames.end(), ", ").c_str());
1566             (*autoApplyAPISchemasMap)[apiSchemaName] =
1567                 std::move(autoApplyToTypeNames);
1568         }
1569     } else {
1570         // For multiple apply schemas, the metadata may specify a list of
1571         // allowed instance names.
1572         TfTokenVector allowedInstanceNames =
1573             _GetNameListFromMetadata(dict, _tokens->apiSchemaAllowedInstanceNames);
1574         if (!allowedInstanceNames.empty()) {
1575             (*allowedInstanceNamesMap)[apiSchemaName].insert(
1576                 allowedInstanceNames.begin(), allowedInstanceNames.end());
1577         }
1578 
1579         // Multiple apply API schema metadata may specify a dictionary of
1580         // additional apply info for individual instance names. Right now this
1581         // will only contain additional "can only apply to" types for individual
1582         // instances names, but in the future we can add auto-apply metadata
1583         // here as well.
1584         const JsValue *apiSchemaInstancesValue =
1585             TfMapLookupPtr(dict, _tokens->apiSchemaInstances);
1586         if (!apiSchemaInstancesValue) {
1587             return;
1588         }
1589 
1590         if (!apiSchemaInstancesValue->IsObject()) {
1591             TF_CODING_ERROR("Metadata value for key '%s' for API schema type "
1592                             "'%s' is not holding a dictionary. PlugInfo may "
1593                             "need to be regenerated.",
1594                             _tokens->apiSchemaInstances.GetText(),
1595                             apiSchemaName.GetText());
1596             return;
1597         }
1598 
1599         // For each instance name in the metadata dictionary we grab any
1600         // "can only apply to" types specified for and add it to
1601         // "can only apply to" types map under the fully joined API schema name
1602         for (const auto &entry : apiSchemaInstancesValue->GetJsObject()) {
1603             const std::string &instanceName = entry.first;
1604 
1605             if (!entry.second.IsObject()) {
1606                 TF_CODING_ERROR("%s value for instance name '%s' for API "
1607                                 "schema type '%s' is not holding a dictionary. "
1608                                 "PlugInfo may need to be regenerated.",
1609                                 _tokens->apiSchemaInstances.GetText(),
1610                                 instanceName.c_str(),
1611                                 apiSchemaName.GetText());
1612                 continue;
1613             }
1614             const JsObject &instanceDict = entry.second.GetJsObject();
1615 
1616             const TfToken schemaInstanceName(
1617                 SdfPath::JoinIdentifier(apiSchemaName, instanceName));
1618 
1619             TfTokenVector instanceCanOnlyApplyToTypeNames =
1620                 _GetNameListFromMetadata(instanceDict,
1621                                          _tokens->apiSchemaCanOnlyApplyTo);
1622             if (!instanceCanOnlyApplyToTypeNames.empty()) {
1623                 (*canOnlyApplyAPISchemasMap)[schemaInstanceName] =
1624                     std::move(instanceCanOnlyApplyToTypeNames);
1625             }
1626         }
1627     }
1628 }
1629 
1630 PXR_NAMESPACE_CLOSE_SCOPE
1631 
1632