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/prim.h"
26 
27 #include "pxr/usd/usd/apiSchemaBase.h"
28 #include "pxr/usd/usd/inherits.h"
29 #include "pxr/usd/usd/instanceCache.h"
30 #include "pxr/usd/usd/payloads.h"
31 #include "pxr/usd/usd/primRange.h"
32 #include "pxr/usd/usd/relationship.h"
33 #include "pxr/usd/usd/references.h"
34 #include "pxr/usd/usd/resolver.h"
35 #include "pxr/usd/usd/schemaBase.h"
36 #include "pxr/usd/usd/schemaRegistry.h"
37 #include "pxr/usd/usd/specializes.h"
38 #include "pxr/usd/usd/stage.h"
39 #include "pxr/usd/usd/tokens.h"
40 #include "pxr/usd/usd/variantSets.h"
41 
42 #include "pxr/usd/pcp/layerStack.h"
43 #include "pxr/usd/pcp/primIndex.h"
44 #include "pxr/usd/sdf/primSpec.h"
45 
46 #include "pxr/base/plug/registry.h"
47 #include "pxr/base/work/dispatcher.h"
48 #include "pxr/base/work/loops.h"
49 #include "pxr/base/work/singularTask.h"
50 #include "pxr/base/work/withScopedParallelism.h"
51 
52 #include "pxr/base/tf/ostreamMethods.h"
53 
54 #include <boost/functional/hash.hpp>
55 
56 #include <tbb/concurrent_queue.h>
57 #include <tbb/concurrent_unordered_set.h>
58 #include <tbb/parallel_sort.h>
59 
60 #include <algorithm>
61 #include <functional>
62 #include <vector>
63 
64 PXR_NAMESPACE_OPEN_SCOPE
65 
66 UsdPrim
GetChild(const TfToken & name) const67 UsdPrim::GetChild(const TfToken &name) const
68 {
69     return GetStage()->GetPrimAtPath(GetPath().AppendChild(name));
70 }
71 
72 bool
_IsA(const TfType & schemaType,bool validateSchemaType) const73 UsdPrim::_IsA(const TfType& schemaType, bool validateSchemaType) const
74 {
75     if (validateSchemaType) {
76         // Check Schema TfType
77         if (schemaType.IsUnknown()) {
78             TF_CODING_ERROR("Unknown schema type (%s) is invalid for IsA query",
79                             schemaType.GetTypeName().c_str());
80             return false;
81         }
82     }
83 
84     // Check that the actual schema type of the prim (accounts for fallback
85     // types for types with no schema) is or derives from the passed in type.
86     return GetPrimTypeInfo().GetSchemaType().IsA(schemaType);
87 }
88 
89 bool
IsA(const TfType & schemaType) const90 UsdPrim::IsA(const TfType& schemaType) const
91 {
92     return _IsA(schemaType, true);
93 }
94 
95 bool
_HasSingleApplyAPI(const TfType & schemaType) const96 UsdPrim::_HasSingleApplyAPI(const TfType& schemaType) const
97 {
98     TRACE_FUNCTION();
99 
100     // Get our composed set of all applied schemas.
101     auto appliedSchemas = GetAppliedSchemas();
102     if (appliedSchemas.empty()) {
103         return false;
104     }
105 
106     // The compile time and runtime schema type validation of HasAPI should
107     // ensure that this schemaName won't be empty so we don't check for that
108     // here.
109     const TfToken schemaName =
110         UsdSchemaRegistry::GetAPISchemaTypeName(schemaType);
111 
112     // Since this is a single apply API we're just looking for schemaName being
113     // in the list.
114     return std::find(appliedSchemas.begin(), appliedSchemas.end(), schemaName)
115         != appliedSchemas.end();
116 }
117 
118 bool
_HasMultiApplyAPI(const TfType & schemaType,const TfToken & instanceName) const119 UsdPrim::_HasMultiApplyAPI(const TfType& schemaType,
120                            const TfToken &instanceName) const
121 {
122     TRACE_FUNCTION();
123 
124     // Get our composed set of all applied schemas.
125     auto appliedSchemas = GetAppliedSchemas();
126     if (appliedSchemas.empty()) {
127         return false;
128     }
129 
130     // The compile time and runtime schema type validation of HasAPI should
131     // ensure that this schemaName won't be empty so we don't check for that
132     // here.
133     const TfToken schemaName =
134         UsdSchemaRegistry::GetAPISchemaTypeName(schemaType);
135 
136     // If instance name is empty, we're looking for any instance of a multiple
137     // apply schema of the schemaType. Thus we search for name in the list that
138     // starts with the "<schemaName>:" prefix.
139     if (instanceName.IsEmpty()) {
140         const std::string schemaPrefix =
141             schemaName.GetString() + UsdObject::GetNamespaceDelimiter();
142         return std::any_of(appliedSchemas.begin(), appliedSchemas.end(),
143             [&schemaPrefix](const TfToken &appliedSchema) {
144                 return TfStringStartsWith(appliedSchema, schemaPrefix);
145             });
146     }
147 
148     // Otherwise we have an instance name so we're looking for exact match of
149     // "<schemaType>:<instanceName>"
150     const TfToken apiName(SdfPath::JoinIdentifier(schemaName, instanceName));
151     return std::find(appliedSchemas.begin(), appliedSchemas.end(), apiName)
152         != appliedSchemas.end();
153 }
154 
155 bool
HasAPI(const TfType & schemaType,const TfToken & instanceName) const156 UsdPrim::HasAPI(const TfType& schemaType, const TfToken& instanceName) const
157 {
158     if (schemaType.IsUnknown()) {
159         TF_CODING_ERROR("HasAPI: Invalid unknown schema type (%s) ",
160                         schemaType.GetTypeName().c_str());
161         return false;
162     }
163 
164     if (!UsdSchemaRegistry::GetInstance().IsAppliedAPISchema(schemaType)) {
165         TF_CODING_ERROR("HasAPI: provided schema type ( %s ) is not an "
166             "applied API schema type.", schemaType.GetTypeName().c_str());
167         return false;
168     }
169 
170     static const auto apiSchemaBaseType = TfType::Find<UsdAPISchemaBase>();
171     if (!schemaType.IsA(apiSchemaBaseType) || schemaType == apiSchemaBaseType) {
172         TF_CODING_ERROR("HasAPI: provided schema type ( %s ) does not "
173             "derive from UsdAPISchemaBase.",
174             schemaType.GetTypeName().c_str());
175         return false;
176     }
177 
178     // If the type is a multi apply API call the multi apply implementation.
179     if (UsdSchemaRegistry::GetInstance().IsMultipleApplyAPISchema(schemaType)) {
180         return _HasMultiApplyAPI(schemaType, instanceName);
181     }
182 
183     // Otherwise it's a single apply API
184     if (!instanceName.IsEmpty()) {
185         TF_CODING_ERROR("HasAPI: single application API schemas like %s do "
186             "not contain an application instanceName ( %s ).",
187             schemaType.GetTypeName().c_str(), instanceName.GetText());
188         return false;
189     }
190     return _HasSingleApplyAPI(schemaType);
191 }
192 
193 // Runtime validation for the single apply schema non-templated
194 // ApplyAPI/CanApplyAPI/RemoveAPI functions. The templated versions perform this
195 // verification through compile time asserts.
196 static bool
_ValidateSingleApplySchemaType(const TfType & schemaType,std::string * reason)197 _ValidateSingleApplySchemaType(
198     const TfType& schemaType, std::string *reason)
199 {
200     if (UsdSchemaRegistry::GetSchemaKind(schemaType) !=
201             UsdSchemaKind::SingleApplyAPI) {
202         if (reason) {
203             *reason = TfStringPrintf(
204                 "Provided schema type '%s' is not a single-apply API schema "
205                 "type.",
206                 schemaType.GetTypeName().c_str());
207         }
208         return false;
209     }
210     return true;
211 }
212 
213 // Runtime validation for the multiple apply schema non-templated
214 // ApplyAPI/CanApplyAPI/RemoveAPI functions. The templated versions perform this
215 // verification through compile time asserts.
216 static bool
_ValidateMultipleApplySchemaType(const TfType & schemaType,std::string * reason)217 _ValidateMultipleApplySchemaType(
218     const TfType& schemaType, std::string *reason)
219 {
220     if (UsdSchemaRegistry::GetSchemaKind(schemaType) !=
221             UsdSchemaKind::MultipleApplyAPI) {
222         if (reason) {
223             *reason = TfStringPrintf(
224                 "Provided schema type '%s' is not a mutiple-apply API schema "
225                 "type.",
226                 schemaType.GetTypeName().c_str());
227         }
228         return false;
229     }
230     return true;
231 }
232 
233 // Determines whether the given prim type can have the given API schema applied
234 // to it based on a list of types the API "can only be applied to". If the
235 // list is empty, this always returns true, otherwise the prim type must be in
236 // the list or derived from a type in the list.
237 static bool
_IsPrimTypeValidApplyToTarget(const TfType & primType,const TfToken & apiSchemaTypeName,const TfToken & instanceName,std::string * whyNot)238 _IsPrimTypeValidApplyToTarget(const TfType &primType,
239                               const TfToken &apiSchemaTypeName,
240                               const TfToken &instanceName,
241                               std::string *whyNot)
242 {
243     // Get the list of prim types this API "can only apply to" if any.
244     const TfTokenVector &canOnlyApplyToTypes =
245         UsdSchemaRegistry::GetAPISchemaCanOnlyApplyToTypeNames(
246             apiSchemaTypeName, instanceName);
247 
248     // If no "can only apply to" types are found, the schema can be
249     // applied to any prim type (including empty or invalid prims types)
250     if (canOnlyApplyToTypes.empty()) {
251         return true;
252     }
253 
254     // If the prim type or any of its ancestor types are in the list, then it's
255     // valid!
256     if (!primType.IsUnknown()) {
257         for (const TfToken &allowedPrimTypeName : canOnlyApplyToTypes) {
258             const TfType allowedPrimType =
259                 UsdSchemaRegistry::GetTypeFromSchemaTypeName(
260                     allowedPrimTypeName);
261             if (primType.IsA(allowedPrimType)) {
262                 return true;
263             }
264         }
265     }
266 
267     // Otherwise, it wasn't in the list and can't be applied to.
268     if (whyNot) {
269         *whyNot = TfStringPrintf(
270             "API schema '%s' can only be applied to prims of the following "
271             "types: %s.",
272             SdfPath::JoinIdentifier(apiSchemaTypeName, instanceName).c_str(),
273             TfStringJoin(canOnlyApplyToTypes.begin(),
274                          canOnlyApplyToTypes.end(), ", ").c_str());
275     }
276     return false;
277 }
278 
279 
280 bool
_CanApplyAPI(const TfType & apiSchemaType,std::string * whyNot) const281 UsdPrim::_CanApplyAPI(const TfType& apiSchemaType,
282                       std::string *whyNot) const
283 {
284     // The callers of this function will have already validated the schema is
285     // single apply either through static asserts or runtime validation
286     // _ValidateSingleApplySchemaType.
287 
288     // Can't apply API schemas to an invalid prim
289     if (!IsValid()) {
290         if (whyNot) {
291             *whyNot = "Prim is not valid.";
292         }
293         return false;
294     }
295 
296     // Return whether this prim's type is a valid target for applying the given
297     // API schema.
298     const TfToken apiSchemaTypeName =
299         UsdSchemaRegistry::GetSchemaTypeName(apiSchemaType);
300     return _IsPrimTypeValidApplyToTarget(
301         GetPrimTypeInfo().GetSchemaType(),
302         apiSchemaTypeName,
303         /*instanceName=*/ TfToken(),
304         whyNot);
305 }
306 
307 bool
_CanApplyAPI(const TfType & apiSchemaType,const TfToken & instanceName,std::string * whyNot) const308 UsdPrim::_CanApplyAPI(const TfType& apiSchemaType,
309                       const TfToken& instanceName,
310                       std::string *whyNot) const
311 {
312     // The callers of this function will have already validated the schema is
313     // mulitple apply either through static asserts or runtime validation
314     // _ValidateMultipleApplySchemaType.
315 
316     // Instance name can only be validated at runtime. All API schema functions
317     // treat an empty instance for a multiple apply schema as a coding error.
318     if (instanceName.IsEmpty()) {
319         TF_CODING_ERROR("CanApplyAPI: for multiple apply API schema %s, a "
320                         "non-empty instance name must be provided.",
321             apiSchemaType.GetTypeName().c_str());
322         return false;
323     }
324 
325     // Can't apply API schemas to an invalid prim
326     if (!IsValid()) {
327         if (whyNot) {
328             *whyNot = "Prim is not valid.";
329         }
330         return false;
331     }
332 
333     const TfToken apiSchemaTypeName =
334         UsdSchemaRegistry::GetSchemaTypeName(apiSchemaType);
335 
336     // Multiple apply API schemas may have limitations on what instance names
337     // are allowed to be used. Check if the requested instance name is valid.
338     if (!UsdSchemaRegistry::IsAllowedAPISchemaInstanceName(
339             apiSchemaTypeName, instanceName)) {
340         if (whyNot) {
341             *whyNot = TfStringPrintf(
342                 "'%s' is not an allowed instance name for multiple apply API "
343                 "schema '%s'.",
344                 instanceName.GetText(), apiSchemaTypeName.GetText());
345         }
346         return false;
347     }
348 
349     // Return whether this prim's type is a valid target for applying the given
350     // API schema and instance name.
351     return _IsPrimTypeValidApplyToTarget(
352         GetPrimTypeInfo().GetSchemaType(),
353         apiSchemaTypeName,
354         instanceName,
355         whyNot);
356 }
357 
358 bool
CanApplyAPI(const TfType & schemaType,std::string * whyNot) const359 UsdPrim::CanApplyAPI(const TfType& schemaType,
360                      std::string *whyNot) const
361 {
362     // Validate that this function is being called on a single apply API schema.
363     // Failure is a coding error as the matching template function would fail
364     // to compile.
365     std::string errorMsg;
366     if (!_ValidateSingleApplySchemaType(schemaType, &errorMsg)) {
367         TF_CODING_ERROR("CanApplyAPI: %s", errorMsg.c_str());
368         if (whyNot) {
369             *whyNot = std::move(errorMsg);
370         }
371         return false;
372     }
373     return _CanApplyAPI(schemaType, whyNot);
374 }
375 
376 bool
CanApplyAPI(const TfType & schemaType,const TfToken & instanceName,std::string * whyNot) const377 UsdPrim::CanApplyAPI(const TfType& schemaType,
378                      const TfToken& instanceName,
379                      std::string *whyNot) const
380 {
381     // Validate that this function is being called on a multiple apply API
382     // schema. Failure is a coding error as the matching template function would
383     // fail to compile.
384     std::string errorMsg;
385     if (!_ValidateMultipleApplySchemaType(schemaType, &errorMsg)) {
386         TF_CODING_ERROR("CanApplyAPI: %s", errorMsg.c_str());
387         if (whyNot) {
388             *whyNot = std::move(errorMsg);
389         }
390         return false;
391     }
392     return _CanApplyAPI(schemaType, instanceName, whyNot);
393 }
394 
395 bool
_ApplyAPI(const TfType & schemaType) const396 UsdPrim::_ApplyAPI(const TfType& schemaType) const
397 {
398     // The callers of this function will have already validated the schema is
399     // single apply either through static asserts or runtime validation
400     // _ValidateSingleApplySchemaType.
401 
402     // Validate the prim to protect against crashes in the schema generated
403     // SchemaClass::Apply(const UsdPrim &prim) functions when called with a null
404     // prim as these generated functions call prim.ApplyAPI<SchemaClass>
405     //
406     // While we don't typically validate "this" prim for public UsdPrim C++ API,
407     // for performance reasons, we opt to do so here since ApplyAPI isn't
408     // performance critical. If ApplyAPI becomes performance critical in the
409     // future, we may have to move this validation elsewhere if this validation
410     // is problematic.
411     if (!IsValid()) {
412         TF_CODING_ERROR("Invalid prim '%s'", GetDescription().c_str());
413         return false;
414     }
415 
416     const TfToken typeName = UsdSchemaRegistry::GetSchemaTypeName(schemaType);
417     return AddAppliedSchema(typeName);
418 }
419 
420 bool
_ApplyAPI(const TfType & schemaType,const TfToken & instanceName) const421 UsdPrim::_ApplyAPI(const TfType& schemaType, const TfToken& instanceName) const
422 {
423     // The callers of this function will have already validated the schema is
424     // mulitple apply either through static asserts or runtime validation
425     // _ValidateMultipleApplySchemaType.
426 
427     // Instance name can only be validated at runtime. All API schema functions
428     // treat an empty instance for a multiple apply schema as a coding error.
429     if (instanceName.IsEmpty()) {
430         TF_CODING_ERROR("ApplyAPI: for mutiple apply API schema %s, a "
431                         "non-empty instance name must be provided.",
432             schemaType.GetTypeName().c_str());
433         return false;
434     }
435 
436     // Validate the prim to protect against crashes in the schema generated
437     // SchemaClass::Apply(const UsdPrim &prim) functions when called with a null
438     // prim as these generated functions call prim.ApplyAPI<SchemaClass>
439     //
440     // While we don't typically validate "this" prim for public UsdPrim C++ API,
441     // for performance reasons, we opt to do so here since ApplyAPI isn't
442     // performance critical. If ApplyAPI becomes performance critical in the
443     // future, we may have to move this validation elsewhere if this validation
444     // is problematic.
445     if (!IsValid()) {
446         TF_CODING_ERROR("Invalid prim '%s'", GetDescription().c_str());
447         return false;
448     }
449 
450     const TfToken typeName = UsdSchemaRegistry::GetSchemaTypeName(schemaType);
451     TfToken apiName(SdfPath::JoinIdentifier(typeName, instanceName));
452     return AddAppliedSchema(apiName);
453 }
454 
455 bool
ApplyAPI(const TfType & schemaType) const456 UsdPrim::ApplyAPI(const TfType& schemaType) const
457 {
458     // Validate that this function is being called on a single apply API schema.
459     // Failure is a coding error as the matching template function would fail
460     // to compile.
461     std::string errorMsg;
462     if (!_ValidateSingleApplySchemaType(schemaType, &errorMsg)) {
463         TF_CODING_ERROR("ApplyAPI: %s", errorMsg.c_str());
464         return false;
465     }
466     return _ApplyAPI(schemaType);
467 }
468 
469 bool
ApplyAPI(const TfType & schemaType,const TfToken & instanceName) const470 UsdPrim::ApplyAPI(const TfType& schemaType, const TfToken& instanceName) const
471 {
472     // Validate that this function is being called on a multiple apply API
473     // schema. Failure is a coding error as the matching template function would
474     // fail to compile.
475     std::string errorMsg;
476     if (!_ValidateMultipleApplySchemaType(schemaType, &errorMsg)) {
477         TF_CODING_ERROR("ApplyAPI: %s", errorMsg.c_str());
478         return false;
479     }
480     return _ApplyAPI(schemaType, instanceName);
481 }
482 
483 bool
_RemoveAPI(const TfType & schemaType) const484 UsdPrim::_RemoveAPI(const TfType& schemaType) const
485 {
486     // The callers of this function will have already validated the schema is
487     // single apply either through static asserts or runtime validation
488     // _ValidateSingleApplySchemaType.
489 
490     const TfToken typeName = UsdSchemaRegistry::GetSchemaTypeName(schemaType);
491     return RemoveAppliedSchema(typeName);
492 }
493 
494 bool
_RemoveAPI(const TfType & schemaType,const TfToken & instanceName) const495 UsdPrim::_RemoveAPI(const TfType& schemaType, const TfToken& instanceName) const
496 {
497     // The callers of this function will have already validated the schema is
498     // mulitple apply either through static asserts or runtime validation
499     // _ValidateMultipleApplySchemaType.
500 
501     // Instance name can only be validated at runtime. All API schema functions
502     // treat an empty instance for a multiple apply schema as a coding error.
503     if (instanceName.IsEmpty()) {
504         TF_CODING_ERROR("RemoveAPI: for mutiple apply API schema %s, a "
505                         "non-empty instance name must be provided.",
506             schemaType.GetTypeName().c_str());
507         return false;
508     }
509 
510     const TfToken typeName = UsdSchemaRegistry::GetSchemaTypeName(schemaType);
511     TfToken apiName(SdfPath::JoinIdentifier(typeName, instanceName));
512     return RemoveAppliedSchema(apiName);
513 }
514 
515 bool
RemoveAPI(const TfType & schemaType) const516 UsdPrim::RemoveAPI(const TfType& schemaType) const
517 {
518     // Validate that this function is being called on a single apply API schema.
519     // Failure is a coding error as the matching template function would fail
520     // to compile.
521     std::string errorMsg;
522     if (!_ValidateSingleApplySchemaType(schemaType, &errorMsg)) {
523         TF_CODING_ERROR("RemoveAPI: %s", errorMsg.c_str());
524         return false;
525     }
526     return _RemoveAPI(schemaType);
527 }
528 
529 bool
RemoveAPI(const TfType & schemaType,const TfToken & instanceName) const530 UsdPrim::RemoveAPI(const TfType& schemaType, const TfToken& instanceName) const
531 {
532     // Validate that this function is being called on a multiple apply API
533     // schema. Failure is a coding error as the matching template function would
534     // fail to compile.
535     std::string errorMsg;
536     if (!_ValidateMultipleApplySchemaType(schemaType, &errorMsg)) {
537         TF_CODING_ERROR("RemoveAPI: %s", errorMsg.c_str());
538         return false;
539     }
540     return _RemoveAPI(schemaType, instanceName);
541 }
542 
543 bool
AddAppliedSchema(const TfToken & appliedSchemaName) const544 UsdPrim::AddAppliedSchema(const TfToken &appliedSchemaName) const
545 {
546     // This should find or create the primSpec in the current edit target.
547     // It will also issue an error if it's unable to.
548     SdfPrimSpecHandle primSpec = _GetStage()->_CreatePrimSpecForEditing(*this);
549 
550     // _CreatePrimSpecForEditing would have already issued a runtime error
551     // in case of a failure.
552     if (!primSpec) {
553         TF_WARN("Unable to create primSpec at path <%s> in edit target '%s'. "
554                 "Failed to add applied API schema.",
555             GetPath().GetText(),
556             _GetStage()->GetEditTarget().GetLayer()->GetIdentifier().c_str());
557         return false;
558     }
559 
560     auto _HasItem = [](const TfTokenVector &items, const TfToken &item) {
561         return std::find(items.begin(), items.end(), item) != items.end();
562     };
563 
564     SdfTokenListOp listOp =
565         primSpec->GetInfo(UsdTokens->apiSchemas).Get<SdfTokenListOp>();
566 
567     if (listOp.IsExplicit()) {
568         // If the list op is explicit we check if the explicit item to see if
569         // our name is already in it. We'll add it to the end of the explicit
570         // list if it is not.
571         const TfTokenVector &items = listOp.GetExplicitItems();
572         if (_HasItem(items, appliedSchemaName)) {
573             return true;
574         }
575         // Use ReplaceOperations to append in place.
576         if (!listOp.ReplaceOperations(SdfListOpTypeExplicit,
577                 items.size(), 0, {appliedSchemaName})) {
578             return false;
579         }
580     } else {
581         // Otherwise our name could be in the append or prepend list (we
582         // purposefully ignore the "add" list which is deprecated) so we check
583         // both before adding it to the end of prepends.
584         const TfTokenVector &preItems = listOp.GetPrependedItems();
585         const TfTokenVector &appItems = listOp.GetAppendedItems();
586         if (_HasItem(preItems, appliedSchemaName) ||
587             _HasItem(appItems, appliedSchemaName)) {
588             return true;
589         }
590         // Use ReplaceOperations to append in place.
591         if (!listOp.ReplaceOperations(SdfListOpTypePrepended,
592                 preItems.size(), 0, {appliedSchemaName})) {
593             return false;
594         }
595     }
596 
597     // If we got here, we edited the list op, so author it back to the spec.
598     primSpec->SetInfo(UsdTokens->apiSchemas, VtValue::Take(listOp));
599     return true;
600 }
601 
602 bool
RemoveAppliedSchema(const TfToken & appliedSchemaName) const603 UsdPrim::RemoveAppliedSchema(const TfToken &appliedSchemaName) const
604 {
605     // This should create the primSpec in the current edit target.
606     // It will also issue an error if it's unable to.
607     SdfPrimSpecHandle primSpec = _GetStage()->_CreatePrimSpecForEditing(*this);
608 
609     // _CreatePrimSpecForEditing would have already issued a runtime error
610     // in case of a failure.
611     if (!primSpec) {
612         TF_WARN("Unable to create primSpec at path <%s> in edit target '%s'. "
613                 "Failed to remove applied API schema.",
614             GetPath().GetText(),
615             _GetStage()->GetEditTarget().GetLayer()->GetIdentifier().c_str());
616         return false;
617     }
618 
619     SdfTokenListOp listOp =
620         primSpec->GetInfo(UsdTokens->apiSchemas).Get<SdfTokenListOp>();
621 
622     // Create a list op that deletes our schema name and apply it to the current
623     // apiSchemas list op for the edit prim spec. This will take care of making
624     // sure it ends up in the deletes list (for non-explicit list ops) and is
625     // removed from any other items list that would add it back.
626     SdfTokenListOp editListOp;
627     editListOp.SetDeletedItems({appliedSchemaName});
628     if (auto result = editListOp.ApplyOperations(listOp)) {
629         primSpec->SetInfo(UsdTokens->apiSchemas, VtValue(*result));
630         return true;
631     } else {
632         TF_CODING_ERROR("Failed to apply list op edits to 'apiSchemas' on spec "
633                         "at path <%s> in layer '%s'",
634                         primSpec->GetLayer()->GetIdentifier().c_str(),
635                         primSpec->GetPath().GetText());
636         return false;
637     }
638 }
639 
640 std::vector<UsdProperty>
_MakeProperties(const TfTokenVector & names) const641 UsdPrim::_MakeProperties(const TfTokenVector &names) const
642 {
643     std::vector<UsdProperty> props;
644     UsdStage *stage = _GetStage();
645     props.reserve(names.size());
646     for (auto const &propName : names) {
647         SdfSpecType specType =
648             stage->_GetDefiningSpecType(get_pointer(_Prim()), propName);
649         if (specType == SdfSpecTypeAttribute) {
650             props.push_back(GetAttribute(propName));
651         } else if (TF_VERIFY(specType == SdfSpecTypeRelationship)) {
652             props.push_back(GetRelationship(propName));
653         }
654     }
655     return props;
656 }
657 
658 // Change the order of items in 'names' so that all the things in 'order' that
659 // are also in 'names' are at the beginning in the order that they appear in
660 // 'order', followed by any remaining items in 'names' in their existing order.
661 static void
_ApplyOrdering(const TfTokenVector & order,TfTokenVector * names)662 _ApplyOrdering(const TfTokenVector &order, TfTokenVector *names)
663 {
664     // If order is empty or names is empty, nothing to do.
665     if (order.empty() || names->empty())
666         return;
667 
668     // Perf note: this walks 'order' and linear searches 'names' to find each
669     // element, for O(M*N) operations, where M and N are the lengths of 'order'
670     // and 'names'.  We hope 1) that propertyOrder stmts are relatively rare and
671     // 2) that property lists are relatively short.  If those assumptions fail,
672     // this may need revisiting.  In some quick microbenchmarking, this linear
673     // search seems to outperform binary search up to about 5000 names.  We
674     // suspect this is because linear search does TfToken pointer comparisons,
675     // while binary search has to dereference and do string comparisons.
676 
677     typedef TfTokenVector::iterator Iter;
678 
679     Iter namesRest = names->begin(), namesEnd = names->end();
680     for (const TfToken &oName: order) {
681         // Look for this name from 'order' in the rest of 'names'.
682         Iter i = std::find(namesRest, namesEnd, oName);
683         if (i != namesEnd) {
684             // Found.  Move to the front by rotating the sub-range.  Using
685             // std::rotate invokes swap(), which avoids TfToken refcounting.
686             // Also advance 'namesRest' to the next element.
687             std::rotate(namesRest++, i, i+1);
688         }
689     }
690 }
691 
692 bool
RemoveProperty(const TfToken & propName)693 UsdPrim::RemoveProperty(const TfToken &propName)
694 {
695     SdfPath propPath = GetPath().AppendProperty(propName);
696     return _GetStage()->_RemoveProperty(propPath);
697 }
698 
699 UsdProperty
GetProperty(const TfToken & propName) const700 UsdPrim::GetProperty(const TfToken &propName) const
701 {
702     SdfSpecType specType =
703         _GetStage()->_GetDefiningSpecType(get_pointer(_Prim()), propName);
704     if (specType == SdfSpecTypeAttribute) {
705         return GetAttribute(propName);
706     }
707     else if (specType == SdfSpecTypeRelationship) {
708         return GetRelationship(propName);
709     }
710     return UsdProperty(UsdTypeProperty, _Prim(), _ProxyPrimPath(), propName);
711 }
712 
713 bool
HasProperty(const TfToken & propName) const714 UsdPrim::HasProperty(const TfToken &propName) const
715 {
716     return static_cast<bool>(GetProperty(propName));
717 }
718 
719 TfTokenVector
GetPropertyOrder() const720 UsdPrim::GetPropertyOrder() const
721 {
722     TfTokenVector order;
723     GetMetadata(SdfFieldKeys->PropertyOrder, &order);
724     return order;
725 }
726 
727 static void
_ComposePrimPropertyNames(const PcpPrimIndex & primIndex,const PcpNodeRef & node,const UsdPrim::PropertyPredicateFunc & predicate,TfTokenVector * names,TfTokenVector * localNames)728 _ComposePrimPropertyNames(
729     const PcpPrimIndex& primIndex,
730     const PcpNodeRef& node,
731     const UsdPrim::PropertyPredicateFunc &predicate,
732     TfTokenVector *names,
733     TfTokenVector *localNames)
734 {
735     if (node.IsCulled()) {
736         return;
737     }
738 
739     // Strength-order does not matter here, since we're just collecting all
740     // names.
741     TF_FOR_ALL(child, node.GetChildrenRange()) {
742         _ComposePrimPropertyNames(primIndex, *child, predicate, names,
743                                   localNames);
744     }
745 
746     // Compose the site's local names over the current result.
747     if (node.CanContributeSpecs()) {
748         for (auto &layer : node.GetLayerStack()->GetLayers()) {
749             if (layer->HasField<TfTokenVector>(node.GetPath(),
750                     SdfChildrenKeys->PropertyChildren, localNames)) {
751                 // If predicate is valid, then append only the names that pass
752                 // the predicate. If not, add all names (including duplicates).
753                 if (predicate) {
754                     for(auto &name: *localNames) {
755                         if (predicate(name)) {
756                             names->push_back(name);
757                         }
758                     }
759                 } else {
760                     names->insert(names->end(), localNames->begin(),
761                                   localNames->end());
762                 }
763             }
764         }
765     }
766 }
767 
768 // This function and the one above (_ComposePrimPropertyNames) were copied
769 // from Pcp/{PrimIndex,ComposeSite} and optimized for Usd.
770 static void
_ComputePrimPropertyNames(const PcpPrimIndex & primIndex,const UsdPrim::PropertyPredicateFunc & predicate,TfTokenVector * names)771 _ComputePrimPropertyNames(
772     const PcpPrimIndex &primIndex,
773     const UsdPrim::PropertyPredicateFunc &predicate,
774     TfTokenVector *names)
775 {
776     if (!primIndex.IsValid()) {
777         return;
778     }
779 
780     TRACE_FUNCTION();
781 
782     // Temporary shared vector for collecting local property names.
783     // This is used to re-use storage allocated for the local property
784     // names in each layer.
785     TfTokenVector localNames;
786 
787     // Walk the graph to compose prim child names.
788     _ComposePrimPropertyNames(primIndex, primIndex.GetRootNode(), predicate,
789                               names, &localNames);
790 }
791 
792 TfTokenVector
_GetPropertyNames(bool onlyAuthored,bool applyOrder,const UsdPrim::PropertyPredicateFunc & predicate) const793 UsdPrim::_GetPropertyNames(
794     bool onlyAuthored,
795     bool applyOrder,
796     const UsdPrim::PropertyPredicateFunc &predicate) const
797 {
798     TfTokenVector names;
799 
800     // If we're including unauthored properties, take names from definition, if
801     // present.
802     const UsdPrimDefinition &primDef = _Prim()->GetPrimDefinition();
803     if (!onlyAuthored) {
804         if (predicate) {
805             const TfTokenVector &builtInNames = primDef.GetPropertyNames();
806             for (const auto &builtInName : builtInNames) {
807                 if (predicate(builtInName)) {
808                     names.push_back(builtInName);
809                 }
810             }
811         } else {
812             names = primDef.GetPropertyNames();
813         }
814     }
815 
816     // Add authored names, then sort and apply ordering.
817     _ComputePrimPropertyNames(GetPrimIndex(), predicate, &names);
818 
819     if (!names.empty()) {
820         // Sort and uniquify the names.
821         sort(names.begin(), names.end(), TfDictionaryLessThan());
822         names.erase(std::unique(names.begin(), names.end()), names.end());
823         if (applyOrder) {
824             _ApplyOrdering(GetPropertyOrder(), &names);
825         }
826     }
827 
828     return names;
829 }
830 
831 TfTokenVector
GetAppliedSchemas() const832 UsdPrim::GetAppliedSchemas() const
833 {
834     return GetPrimDefinition().GetAppliedAPISchemas();
835 }
836 
837 TfTokenVector
GetPropertyNames(const UsdPrim::PropertyPredicateFunc & predicate) const838 UsdPrim::GetPropertyNames(
839     const UsdPrim::PropertyPredicateFunc &predicate) const
840 {
841     return _GetPropertyNames(/*onlyAuthored=*/ false,
842                              /*applyOrder*/ true,
843                              predicate);
844 }
845 
846 TfTokenVector
GetAuthoredPropertyNames(const UsdPrim::PropertyPredicateFunc & predicate) const847 UsdPrim::GetAuthoredPropertyNames(
848     const UsdPrim::PropertyPredicateFunc &predicate) const
849 {
850     return _GetPropertyNames(/*onlyAuthored=*/ true,
851                              /*applyOrder*/ true,
852                              predicate);
853 }
854 
855 std::vector<UsdProperty>
GetProperties(const UsdPrim::PropertyPredicateFunc & predicate) const856 UsdPrim::GetProperties(const UsdPrim::PropertyPredicateFunc &predicate) const
857 {
858     return _MakeProperties(GetPropertyNames(predicate));
859 }
860 
861 std::vector<UsdProperty>
GetAuthoredProperties(const UsdPrim::PropertyPredicateFunc & predicate) const862 UsdPrim::GetAuthoredProperties(
863     const UsdPrim::PropertyPredicateFunc &predicate) const
864 {
865     return _MakeProperties(GetAuthoredPropertyNames(predicate));
866 }
867 
868 std::vector<UsdProperty>
_GetPropertiesInNamespace(const std::string & namespaces,bool onlyAuthored) const869 UsdPrim::_GetPropertiesInNamespace(
870     const std::string &namespaces,
871     bool onlyAuthored) const
872 {
873     if (namespaces.empty())
874         return onlyAuthored ? GetAuthoredProperties() : GetProperties();
875 
876     const char delim = UsdObject::GetNamespaceDelimiter();
877 
878     // Set terminator to the expected position of the delimiter after all the
879     // supplied namespaces.  We perform an explicit test for this char below
880     // so that we don't need to allocate a new string if namespaces does not
881     // already end with the delimiter
882     const size_t terminator = namespaces.size() -
883         (*namespaces.rbegin() == delim);
884 
885     TfTokenVector names = _GetPropertyNames(onlyAuthored,
886         /*applyOrder=*/ true,
887         /*predicate*/ [&namespaces, terminator, delim](const TfToken &name) {
888             const std::string &s = name.GetString();
889             return s.size() > terminator &&
890                    TfStringStartsWith(s, namespaces)   &&
891                    s[terminator] == delim;
892         });
893 
894     return _MakeProperties(names);
895 }
896 
897 std::vector<UsdProperty>
GetPropertiesInNamespace(const std::string & namespaces) const898 UsdPrim::GetPropertiesInNamespace(const std::string &namespaces) const
899 {
900     return _GetPropertiesInNamespace(namespaces, /*onlyAuthored=*/false);
901 }
902 
903 std::vector<UsdProperty>
GetAuthoredPropertiesInNamespace(const std::string & namespaces) const904 UsdPrim::GetAuthoredPropertiesInNamespace(const std::string &namespaces) const
905 {
906     return _GetPropertiesInNamespace(namespaces, /*onlyAuthored=*/true);
907 }
908 
909 std::vector<UsdProperty>
GetPropertiesInNamespace(const std::vector<std::string> & namespaces) const910 UsdPrim::GetPropertiesInNamespace(const std::vector<std::string> &namespaces) const
911 {
912     return GetPropertiesInNamespace(SdfPath::JoinIdentifier(namespaces));
913 }
914 
915 std::vector<UsdProperty>
GetAuthoredPropertiesInNamespace(const std::vector<std::string> & namespaces) const916 UsdPrim::GetAuthoredPropertiesInNamespace(
917     const std::vector<std::string> &namespaces) const
918 {
919     return GetAuthoredPropertiesInNamespace(
920         SdfPath::JoinIdentifier(namespaces));
921 }
922 
923 UsdAttribute
CreateAttribute(const TfToken & name,const SdfValueTypeName & typeName,bool custom,SdfVariability variability) const924 UsdPrim::CreateAttribute(const TfToken& name,
925                          const SdfValueTypeName &typeName,
926                          bool custom,
927                          SdfVariability variability) const
928 {
929     UsdAttribute attr = GetAttribute(name);
930     attr._Create(typeName, custom, variability);
931     return attr;
932 }
933 
934 UsdAttribute
CreateAttribute(const TfToken & name,const SdfValueTypeName & typeName,SdfVariability variability) const935 UsdPrim::CreateAttribute(const TfToken& name,
936                          const SdfValueTypeName &typeName,
937                          SdfVariability variability) const
938 {
939     return CreateAttribute(name, typeName, /*custom=*/true, variability);
940 }
941 
942 UsdAttribute
CreateAttribute(const std::vector<std::string> & nameElts,const SdfValueTypeName & typeName,bool custom,SdfVariability variability) const943 UsdPrim::CreateAttribute(const std::vector<std::string> &nameElts,
944                          const SdfValueTypeName &typeName,
945                          bool custom,
946                          SdfVariability variability) const
947 {
948     return CreateAttribute(TfToken(SdfPath::JoinIdentifier(nameElts)),
949                            typeName, custom, variability);
950 }
951 
952 UsdAttribute
CreateAttribute(const std::vector<std::string> & nameElts,const SdfValueTypeName & typeName,SdfVariability variability) const953 UsdPrim::CreateAttribute(const std::vector<std::string> &nameElts,
954                          const SdfValueTypeName &typeName,
955                          SdfVariability variability) const
956 {
957     return CreateAttribute(nameElts, typeName, /*custom=*/true, variability);
958 }
959 
960 UsdAttributeVector
_GetAttributes(bool onlyAuthored,bool applyOrder) const961 UsdPrim::_GetAttributes(bool onlyAuthored, bool applyOrder) const
962 {
963     const TfTokenVector names = _GetPropertyNames(onlyAuthored, applyOrder);
964     UsdAttributeVector attrs;
965 
966     // PERFORMANCE: This is sloppy, since property names are a superset of
967     // attribute names, however this vector is likely short lived and worth the
968     // trade off of repeated reallocation.
969     attrs.reserve(names.size());
970     for (const auto& propName : names) {
971         if (UsdAttribute attr = GetAttribute(propName)) {
972             attrs.push_back(attr);
973         }
974     }
975 
976     return attrs;
977 }
978 
979 UsdAttributeVector
GetAttributes() const980 UsdPrim::GetAttributes() const
981 {
982     return _GetAttributes(/*onlyAuthored=*/false, /*applyOrder=*/true);
983 }
984 
985 UsdAttributeVector
GetAuthoredAttributes() const986 UsdPrim::GetAuthoredAttributes() const
987 {
988     return _GetAttributes(/*onlyAuthored=*/true, /*applyOrder=*/true);
989 }
990 
991 UsdAttribute
GetAttribute(const TfToken & attrName) const992 UsdPrim::GetAttribute(const TfToken& attrName) const
993 {
994     // An invalid prim will present a coding error, and then return an
995     // invalid attribute
996     return UsdAttribute(_Prim(), _ProxyPrimPath(), attrName);
997 }
998 
999 bool
HasAttribute(const TfToken & attrName) const1000 UsdPrim::HasAttribute(const TfToken& attrName) const
1001 {
1002     return static_cast<bool>(GetAttribute(attrName));
1003 }
1004 
1005 UsdRelationship
CreateRelationship(const TfToken & name,bool custom) const1006 UsdPrim::CreateRelationship(const TfToken& name, bool custom) const
1007 {
1008     UsdRelationship rel = GetRelationship(name);
1009     rel._Create(custom);
1010     return rel;
1011 }
1012 
1013 UsdRelationship
CreateRelationship(const std::vector<std::string> & nameElts,bool custom) const1014 UsdPrim::CreateRelationship(const std::vector<std::string> &nameElts,
1015                             bool custom) const
1016 {
1017     return CreateRelationship(TfToken(SdfPath::JoinIdentifier(nameElts)),
1018                               custom);
1019 }
1020 
1021 UsdRelationshipVector
_GetRelationships(bool onlyAuthored,bool applyOrder) const1022 UsdPrim::_GetRelationships(bool onlyAuthored, bool applyOrder) const
1023 {
1024     const TfTokenVector names = _GetPropertyNames(onlyAuthored, applyOrder);
1025     UsdRelationshipVector rels;
1026 
1027     // PERFORMANCE: This is sloppy, since property names are a superset of
1028     // relationship names, however this vector is likely short lived and worth
1029     // the trade off of repeated reallocation.
1030     rels.reserve(names.size());
1031     for (const auto& propName : names) {
1032         if (UsdRelationship rel = GetRelationship(propName)) {
1033             rels.push_back(rel);
1034         }
1035     }
1036 
1037     return rels;
1038 }
1039 
1040 UsdRelationshipVector
GetRelationships() const1041 UsdPrim::GetRelationships() const
1042 {
1043     return _GetRelationships(/*onlyAuthored=*/false, /*applyOrder=*/true);
1044 }
1045 
1046 UsdRelationshipVector
GetAuthoredRelationships() const1047 UsdPrim::GetAuthoredRelationships() const
1048 {
1049     return _GetRelationships(/*onlyAuthored=*/true, /*applyOrder=*/true);
1050 }
1051 
1052 UsdRelationship
GetRelationship(const TfToken & relName) const1053 UsdPrim::GetRelationship(const TfToken& relName) const
1054 {
1055     return UsdRelationship(_Prim(), _ProxyPrimPath(), relName);
1056 }
1057 
1058 bool
HasRelationship(const TfToken & relName) const1059 UsdPrim::HasRelationship(const TfToken& relName) const
1060 {
1061     return static_cast<bool>(GetRelationship(relName));
1062 }
1063 
1064 template <class PropertyType, class Derived>
1065 struct UsdPrim_TargetFinder
1066 {
1067     using Predicate = std::function<bool (PropertyType const &)>;
1068 
1069     static SdfPathVector
FindUsdPrim_TargetFinder1070     Find(UsdPrim const &prim, Predicate const &pred, bool recurse) {
1071         UsdPrim_TargetFinder tf(prim, pred, recurse);
1072         tf._Find();
1073         return std::move(tf._result);
1074     }
1075 
1076 private:
UsdPrim_TargetFinderUsdPrim_TargetFinder1077     explicit UsdPrim_TargetFinder(
1078         UsdPrim const &prim, Predicate const &pred, bool recurse)
1079         : _prim(prim)
1080         , _consumerTask(_dispatcher, [this]() { _ConsumerTask(); })
1081         , _predicate(pred)
1082         , _recurse(recurse) {}
1083 
_VisitUsdPrim_TargetFinder1084     void _Visit(UsdRelationship const &rel) {
1085         SdfPathVector targets;
1086         rel._GetForwardedTargets(&targets,
1087                                  /*includeForwardingRels=*/true);
1088         _VisitImpl(targets);
1089     }
1090 
_VisitUsdPrim_TargetFinder1091     void _Visit(UsdAttribute const &attr) {
1092         SdfPathVector sources;
1093         attr.GetConnections(&sources);
1094         _VisitImpl(sources);
1095     }
1096 
_VisitImplUsdPrim_TargetFinder1097     void _VisitImpl(SdfPathVector const &paths) {
1098         if (!paths.empty()) {
1099             for (SdfPath const &p: paths) {
1100                 _workQueue.push(p);
1101             }
1102             _consumerTask.Wake();
1103         }
1104 
1105         if (_recurse) {
1106             WorkParallelForEach(
1107                 paths.begin(), paths.end(),
1108                 [this](SdfPath const &path) {
1109                     if (!path.HasPrefix(_prim.GetPath())) {
1110                         if (UsdPrim owningPrim = _prim.GetStage()->
1111                             GetPrimAtPath(path.GetPrimPath())) {
1112                             _VisitSubtree(owningPrim);
1113                         }
1114                     }
1115                 });
1116         }
1117     }
1118 
_VisitPrimUsdPrim_TargetFinder1119     void _VisitPrim(UsdPrim const &prim) {
1120         if (_seenPrims.insert(prim).second) {
1121             auto props = static_cast<Derived *>(this)->_GetProperties(prim);
1122             for (auto const &prop: props) {
1123                 if (!_predicate || _predicate(prop)) {
1124                     _dispatcher.Run([this, prop]() { _Visit(prop); });
1125                 }
1126             }
1127         }
1128     };
1129 
_VisitSubtreeUsdPrim_TargetFinder1130     void _VisitSubtree(UsdPrim const &prim) {
1131         _VisitPrim(prim);
1132         auto range = prim.GetDescendants();
1133         WorkParallelForEach(range.begin(), range.end(),
1134                             [this](UsdPrim const &desc) { _VisitPrim(desc); });
1135     }
1136 
_FindUsdPrim_TargetFinder1137     void _Find() {
1138         TF_PY_ALLOW_THREADS_IN_SCOPE();
1139 
1140         WorkWithScopedParallelism([this]() {
1141                 _VisitSubtree(_prim);
1142                 _dispatcher.Wait();
1143                 tbb::parallel_sort(_result.begin(), _result.end(),
1144                                    SdfPath::FastLessThan());
1145             });
1146 
1147         _result.erase(unique(_result.begin(), _result.end()), _result.end());
1148     }
1149 
_ConsumerTaskUsdPrim_TargetFinder1150     void _ConsumerTask() {
1151         SdfPath path;
1152         while (_workQueue.try_pop(path)) {
1153             _result.push_back(path);
1154         }
1155     }
1156 
1157     UsdPrim _prim;
1158     WorkDispatcher _dispatcher;
1159     WorkSingularTask _consumerTask;
1160     Predicate const &_predicate;
1161     tbb::concurrent_queue<SdfPath> _workQueue;
1162     tbb::concurrent_unordered_set<UsdPrim, boost::hash<UsdPrim> > _seenPrims;
1163     SdfPathVector _result;
1164     bool _recurse;
1165 };
1166 
1167 struct UsdPrim_RelTargetFinder
1168     : public UsdPrim_TargetFinder<UsdRelationship, UsdPrim_RelTargetFinder>
1169 {
_GetPropertiesUsdPrim_RelTargetFinder1170     std::vector<UsdRelationship> _GetProperties(UsdPrim const &prim) const {
1171         return prim._GetRelationships(/*onlyAuthored=*/true,
1172                                       /*applyOrder=*/false);
1173     }
1174 };
1175 
1176 struct UsdPrim_AttrConnectionFinder
1177     : public UsdPrim_TargetFinder<UsdAttribute, UsdPrim_AttrConnectionFinder>
1178 {
_GetPropertiesUsdPrim_AttrConnectionFinder1179     std::vector<UsdAttribute> _GetProperties(UsdPrim const &prim) const {
1180         return prim._GetAttributes(/*onlyAuthored=*/true,
1181                                    /*applyOrder=*/false);
1182     }
1183 };
1184 
1185 USD_API
1186 SdfPathVector
FindAllAttributeConnectionPaths(std::function<bool (UsdAttribute const &)> const & predicate,bool recurseOnSources) const1187 UsdPrim::FindAllAttributeConnectionPaths(
1188     std::function<bool (UsdAttribute const &)> const &predicate,
1189     bool recurseOnSources) const
1190 {
1191     return UsdPrim_AttrConnectionFinder
1192         ::Find(*this, predicate, recurseOnSources);
1193 }
1194 
1195 SdfPathVector
FindAllRelationshipTargetPaths(std::function<bool (UsdRelationship const &)> const & predicate,bool recurseOnTargets) const1196 UsdPrim::FindAllRelationshipTargetPaths(
1197     std::function<bool (UsdRelationship const &)> const &predicate,
1198     bool recurseOnTargets) const
1199 {
1200     return UsdPrim_RelTargetFinder::Find(*this, predicate, recurseOnTargets);
1201 }
1202 
1203 bool
HasVariantSets() const1204 UsdPrim::HasVariantSets() const
1205 {
1206     // Variant sets can't be defined in schema fallbacks as of yet so we only
1207     // need to check for authored variant sets.
1208     return HasAuthoredMetadata(SdfFieldKeys->VariantSetNames);
1209 }
1210 
1211 UsdVariantSets
GetVariantSets() const1212 UsdPrim::GetVariantSets() const
1213 {
1214     return UsdVariantSets(*this);
1215 }
1216 
1217 UsdVariantSet
GetVariantSet(const std::string & variantSetName) const1218 UsdPrim::GetVariantSet(const std::string& variantSetName) const
1219 {
1220     return UsdVariantSet(*this, variantSetName);
1221 }
1222 
1223 
1224 UsdInherits
GetInherits() const1225 UsdPrim::GetInherits() const
1226 {
1227     return UsdInherits(*this);
1228 }
1229 
1230 bool
HasAuthoredInherits() const1231 UsdPrim::HasAuthoredInherits() const
1232 {
1233     return HasAuthoredMetadata(SdfFieldKeys->InheritPaths);
1234 }
1235 
1236 UsdSpecializes
GetSpecializes() const1237 UsdPrim::GetSpecializes() const
1238 {
1239     return UsdSpecializes(*this);
1240 }
1241 
1242 bool
HasAuthoredSpecializes() const1243 UsdPrim::HasAuthoredSpecializes() const
1244 {
1245     return HasAuthoredMetadata(SdfFieldKeys->Specializes);
1246 }
1247 
1248 UsdReferences
GetReferences() const1249 UsdPrim::GetReferences() const
1250 {
1251     return UsdReferences(*this);
1252 }
1253 
1254 bool
HasAuthoredReferences() const1255 UsdPrim::HasAuthoredReferences() const
1256 {
1257     return HasAuthoredMetadata(SdfFieldKeys->References);
1258 }
1259 
1260 // --------------------------------------------------------------------- //
1261 /// \name Payloads, Load and Unload
1262 // --------------------------------------------------------------------- //
1263 
1264 bool
HasPayload() const1265 UsdPrim::HasPayload() const
1266 {
1267     return HasAuthoredPayloads();
1268 }
1269 
1270 bool
SetPayload(const SdfPayload & payload) const1271 UsdPrim::SetPayload(const SdfPayload& payload) const
1272 {
1273     UsdPayloads payloads = GetPayloads();
1274     payloads.ClearPayloads();
1275     return payloads.SetPayloads(SdfPayloadVector{payload});
1276 }
1277 
1278 bool
SetPayload(const std::string & assetPath,const SdfPath & primPath) const1279 UsdPrim::SetPayload(const std::string& assetPath, const SdfPath& primPath) const
1280 {
1281     return SetPayload(SdfPayload(assetPath, primPath));
1282 }
1283 
1284 bool
SetPayload(const SdfLayerHandle & layer,const SdfPath & primPath) const1285 UsdPrim::SetPayload(const SdfLayerHandle& layer, const SdfPath& primPath) const
1286 {
1287     return SetPayload(SdfPayload(layer->GetIdentifier(), primPath));
1288 }
1289 
1290 bool
ClearPayload() const1291 UsdPrim::ClearPayload() const
1292 {
1293     return GetPayloads().ClearPayloads();
1294 }
1295 
1296 UsdPayloads
GetPayloads() const1297 UsdPrim::GetPayloads() const
1298 {
1299     return UsdPayloads(*this);
1300 }
1301 
1302 bool
HasAuthoredPayloads() const1303 UsdPrim::HasAuthoredPayloads() const
1304 {
1305     // Unlike the equivalent function for references, we query the prim data
1306     // for the cached value of HasPayload computed by Pcp instead of querying
1307     // the composed metadata. This is necessary as this function is called by
1308     // _IncludeNewlyDiscoveredPayloadsPredicate in UsdStage which can't safely
1309     // call back into the querying the composed metatdata.
1310     return _Prim()->HasPayload();
1311 }
1312 
1313 void
Load(UsdLoadPolicy policy) const1314 UsdPrim::Load(UsdLoadPolicy policy) const
1315 {
1316     if (IsInPrototype()) {
1317         TF_CODING_ERROR("Attempted to load a prim in a prototype <%s>",
1318                         GetPath().GetText());
1319         return;
1320     }
1321     _GetStage()->Load(GetPath(), policy);
1322 }
1323 
1324 void
Unload() const1325 UsdPrim::Unload() const
1326 {
1327     if (IsInPrototype()) {
1328         TF_CODING_ERROR("Attempted to unload a prim in a prototype <%s>",
1329                         GetPath().GetText());
1330         return;
1331     }
1332     _GetStage()->Unload(GetPath());
1333 }
1334 
1335 TfTokenVector
GetChildrenNames() const1336 UsdPrim::GetChildrenNames() const
1337 {
1338     TfTokenVector names;
1339     for (const auto &child : GetChildren()) {
1340         names.push_back(child.GetName());
1341     }
1342     return names;
1343 }
1344 
1345 TfTokenVector
GetAllChildrenNames() const1346 UsdPrim::GetAllChildrenNames() const
1347 {
1348     TfTokenVector names;
1349     for (const auto &child : GetAllChildren()) {
1350         names.push_back(child.GetName());
1351     }
1352     return names;
1353 }
1354 
1355 TfTokenVector
GetFilteredChildrenNames(const Usd_PrimFlagsPredicate & predicate) const1356 UsdPrim::GetFilteredChildrenNames(const Usd_PrimFlagsPredicate &predicate) const
1357 {
1358     TfTokenVector names;
1359     for (const auto &child : GetFilteredChildren(predicate)) {
1360         names.push_back(child.GetName());
1361     }
1362     return names;
1363 }
1364 
1365 TfTokenVector
GetChildrenReorder() const1366 UsdPrim::GetChildrenReorder() const
1367 {
1368     TfTokenVector reorder;
1369     GetMetadata(SdfFieldKeys->PrimOrder, &reorder);
1370     return reorder;
1371 }
1372 
1373 UsdPrim
GetNextSibling() const1374 UsdPrim::GetNextSibling() const
1375 {
1376     return GetFilteredNextSibling(UsdPrimDefaultPredicate);
1377 }
1378 
1379 UsdPrim
GetFilteredNextSibling(const Usd_PrimFlagsPredicate & inPred) const1380 UsdPrim::GetFilteredNextSibling(const Usd_PrimFlagsPredicate &inPred) const
1381 {
1382     Usd_PrimDataConstPtr sibling = get_pointer(_Prim());
1383     SdfPath siblingPath = _ProxyPrimPath();
1384     const Usd_PrimFlagsPredicate pred =
1385         Usd_CreatePredicateForTraversal(sibling, siblingPath, inPred);
1386 
1387     if (Usd_MoveToNextSiblingOrParent(sibling, siblingPath, pred)) {
1388         return UsdPrim();
1389     }
1390     return UsdPrim(sibling, siblingPath);
1391 }
1392 
1393 bool
IsPseudoRoot() const1394 UsdPrim::IsPseudoRoot() const
1395 {
1396     return GetPath() == SdfPath::AbsoluteRootPath();
1397 }
1398 
1399 bool
IsPrototypePath(const SdfPath & path)1400 UsdPrim::IsPrototypePath(const SdfPath& path)
1401 {
1402     return Usd_InstanceCache::IsPrototypePath(path);
1403 }
1404 
1405 bool
IsPathInPrototype(const SdfPath & path)1406 UsdPrim::IsPathInPrototype(const SdfPath& path)
1407 {
1408     return Usd_InstanceCache::IsPathInPrototype(path);
1409 }
1410 
1411 UsdPrim
GetPrototype() const1412 UsdPrim::GetPrototype() const
1413 {
1414     Usd_PrimDataConstPtr protoPrimData =
1415         _GetStage()->_GetPrototypeForInstance(get_pointer(_Prim()));
1416     return UsdPrim(protoPrimData, SdfPath());
1417 }
1418 
1419 std::vector<UsdPrim>
GetInstances() const1420 UsdPrim::GetInstances() const
1421 {
1422     return _GetStage()->_GetInstancesForPrototype(*this);
1423 }
1424 
1425 SdfPrimSpecHandleVector
GetPrimStack() const1426 UsdPrim::GetPrimStack() const
1427 {
1428     SdfPrimSpecHandleVector primStack;
1429 
1430     for (Usd_Resolver resolver(&(_Prim()->GetPrimIndex()));
1431                       resolver.IsValid(); resolver.NextLayer()) {
1432 
1433         auto primSpec = resolver.GetLayer()
1434             ->GetPrimAtPath(resolver.GetLocalPath());
1435 
1436         if (primSpec) {
1437             primStack.push_back(primSpec);
1438         }
1439     }
1440 
1441     return primStack;
1442 }
1443 
1444 PcpPrimIndex
ComputeExpandedPrimIndex() const1445 UsdPrim::ComputeExpandedPrimIndex() const
1446 {
1447     // Get the prim index path to compute from the index stored in the prim
1448     // data. This ensures we get consistent behavior when dealing with
1449     // instancing and instance proxies.
1450     const PcpPrimIndex& cachedPrimIndex = _Prim()->GetPrimIndex();
1451     if (!cachedPrimIndex.IsValid()) {
1452         return PcpPrimIndex();
1453     }
1454 
1455     const SdfPath& primIndexPath = cachedPrimIndex.GetPath();
1456     PcpCache* cache = _GetStage()->_GetPcpCache();
1457 
1458     PcpPrimIndexOutputs outputs;
1459     PcpComputePrimIndex(
1460         primIndexPath, cache->GetLayerStack(),
1461         cache->GetPrimIndexInputs().Cull(false),
1462         &outputs);
1463 
1464     _GetStage()->_ReportPcpErrors(
1465         outputs.allErrors,
1466         TfStringPrintf(
1467             "computing expanded prim index for <%s>", GetPath().GetText()));
1468 
1469     return outputs.primIndex;
1470 }
1471 
1472 UsdPrim
GetPrimAtPath(const SdfPath & path) const1473 UsdPrim::GetPrimAtPath(const SdfPath& path) const{
1474     const SdfPath absolutePath = path.MakeAbsolutePath(GetPath());
1475     return GetStage()->GetPrimAtPath(absolutePath);
1476 }
1477 
1478 UsdObject
GetObjectAtPath(const SdfPath & path) const1479 UsdPrim::GetObjectAtPath(const SdfPath& path) const{
1480     const SdfPath absolutePath = path.MakeAbsolutePath(GetPath());
1481     return GetStage()->GetObjectAtPath(absolutePath);
1482 }
1483 
1484 UsdProperty
GetPropertyAtPath(const SdfPath & path) const1485 UsdPrim::GetPropertyAtPath(const SdfPath& path) const{
1486     return GetObjectAtPath(path).As<UsdProperty>();
1487 }
1488 
1489 UsdAttribute
GetAttributeAtPath(const SdfPath & path) const1490 UsdPrim::GetAttributeAtPath(const SdfPath& path) const{
1491     return GetObjectAtPath(path).As<UsdAttribute>();
1492 }
1493 
1494 
1495 UsdRelationship
GetRelationshipAtPath(const SdfPath & path) const1496 UsdPrim::GetRelationshipAtPath(const SdfPath& path) const{
1497     return GetObjectAtPath(path).As<UsdRelationship>();
1498 }
1499 
1500 PXR_NAMESPACE_CLOSE_SCOPE
1501 
1502