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
25 #include "pxr/pxr.h"
26
27 #include "pxr/usd/pcp/propertyIndex.h"
28 #include "pxr/usd/pcp/cache.h"
29 #include "pxr/usd/pcp/layerStack.h"
30 #include "pxr/usd/pcp/node.h"
31 #include "pxr/usd/pcp/node_Iterator.h"
32 #include "pxr/usd/pcp/pathTranslation.h"
33 #include "pxr/usd/pcp/primIndex.h"
34 #include "pxr/usd/pcp/site.h"
35
36 #include "pxr/usd/sdf/layer.h"
37 #include "pxr/usd/sdf/listOp.h"
38 #include "pxr/usd/sdf/path.h"
39 #include "pxr/usd/sdf/attributeSpec.h"
40 #include "pxr/usd/sdf/primSpec.h"
41 #include "pxr/usd/sdf/relationshipSpec.h"
42 #include "pxr/usd/sdf/types.h"
43 #include "pxr/base/trace/trace.h"
44 #include "pxr/base/tf/token.h"
45
46 PXR_NAMESPACE_OPEN_SCOPE
47
48 ////////////////////////////////////////////////////////////
49
PcpPropertyIndex()50 PcpPropertyIndex::PcpPropertyIndex()
51 {
52 }
53
54 bool
IsEmpty() const55 PcpPropertyIndex::IsEmpty() const
56 {
57 return _propertyStack.empty();
58 }
59
PcpPropertyIndex(const PcpPropertyIndex & rhs)60 PcpPropertyIndex::PcpPropertyIndex(const PcpPropertyIndex &rhs)
61 {
62 _propertyStack = rhs._propertyStack;
63 if (rhs._localErrors) {
64 _localErrors.reset(new PcpErrorVector(*rhs._localErrors.get()));
65 } else {
66 _localErrors.reset();
67 }
68 }
69
70 void
Swap(PcpPropertyIndex & index)71 PcpPropertyIndex::Swap(PcpPropertyIndex& index)
72 {
73 _propertyStack.swap(index._propertyStack);
74 }
75
76 PcpPropertyRange
GetPropertyRange(bool localOnly) const77 PcpPropertyIndex::GetPropertyRange(bool localOnly) const
78 {
79 if (localOnly) {
80 size_t startIdx = 0;
81 for (; startIdx < _propertyStack.size(); ++startIdx) {
82 if (_propertyStack[startIdx].originatingNode.IsRootNode())
83 break;
84 }
85
86 size_t endIdx = startIdx;
87 for (; endIdx < _propertyStack.size(); ++endIdx) {
88 if (!_propertyStack[endIdx].originatingNode.IsRootNode())
89 break;
90 }
91
92 const bool foundLocalSpecs = (startIdx != endIdx);
93
94 return PcpPropertyRange(
95 PcpPropertyIterator(*this, foundLocalSpecs ? startIdx : 0),
96 PcpPropertyIterator(*this, foundLocalSpecs ? endIdx : 0));
97 }
98 else {
99 return PcpPropertyRange(
100 PcpPropertyIterator(*this, 0),
101 PcpPropertyIterator(*this, _propertyStack.size()));
102 }
103 }
104
105 size_t
GetNumLocalSpecs() const106 PcpPropertyIndex::GetNumLocalSpecs() const
107 {
108 size_t numLocalSpecs = 0;
109 for (size_t i = 0; i < _propertyStack.size(); ++i) {
110 if (_propertyStack[i].originatingNode.IsRootNode()) {
111 ++numLocalSpecs;
112 }
113 }
114
115 return numLocalSpecs;
116 }
117
118 ////////////////////////////////////////////////////////////
119
120 struct Pcp_Permissions
121 {
Pcp_PermissionsPcp_Permissions122 Pcp_Permissions()
123 : previous(SdfPermissionPublic)
124 , current(SdfPermissionPublic) { }
125
126 SdfPermission previous;
127 SdfPermission current;
128 };
129
130 class Pcp_PropertyIndexer
131 {
132 public:
Pcp_PropertyIndexer(PcpPropertyIndex * propIndex,PcpSite propSite,PcpErrorVector * allErrors)133 Pcp_PropertyIndexer(PcpPropertyIndex *propIndex,
134 PcpSite propSite,
135 PcpErrorVector *allErrors)
136 : _propIndex(propIndex),
137 _propSite(propSite),
138 _allErrors(allErrors),
139 _var(SdfVariabilityVarying),
140 _propType(SdfSpecTypeUnknown)
141 {
142 }
143
144 void GatherPropertySpecs(const PcpPrimIndex& primIndex, bool usd);
145 void GatherRelationalAttributeSpecs( const PcpPropertyIndex& relIndex,
146 bool usd);
147
148 private:
149 // Returns the property spec with the given name if it is consistent with
150 // previously seen specs, otherwise returns NULL.
151 //
_GetPrimProperty(const SdfLayerRefPtr & layer,const SdfPath & owningPrimPath,const TfToken & name,bool usd)152 SdfPropertySpecHandle _GetPrimProperty(
153 const SdfLayerRefPtr &layer,
154 const SdfPath &owningPrimPath,
155 const TfToken &name,
156 bool usd)
157 {
158 if (!layer->HasSpec(owningPrimPath))
159 return TfNullPtr;
160
161 const SdfPath propPath = owningPrimPath.AppendProperty(name);
162 if (!layer->HasSpec(propPath))
163 return TfNullPtr;
164
165 SdfPropertySpecHandle propSpec = layer->GetPropertyAtPath(propPath);
166 if (!propSpec)
167 return TfNullPtr;
168
169 // See if it's an attribute.
170 const SdfSpecType propType = propSpec->GetSpecType();
171 if (_propType == SdfSpecTypeUnknown) {
172 // First one, just record the property type and layer
173 _firstSpec = propSpec;
174 _propType = propType;
175
176 } else if (_propType != propType) {
177 // This property spec is inconsistent with the type of the
178 // specs previously seen.
179 PcpErrorInconsistentPropertyTypePtr e =
180 PcpErrorInconsistentPropertyType::New();
181 e->rootSite = _propSite;
182 e->definingLayerIdentifier =
183 _firstSpec->GetLayer()->GetIdentifier();
184 e->definingSpecPath = _firstSpec->GetPath();
185 e->definingSpecType = _propType;
186 e->conflictingLayerIdentifier =
187 propSpec->GetLayer()->GetIdentifier();
188 e->conflictingSpecPath = propSpec->GetPath();
189 e->conflictingSpecType = propType;
190 _RecordError(e);
191 return TfNullPtr;
192 }
193
194 // For an attribute, check that its type and variability are consistent.
195 // We don't care about these mismatches in USD mode.
196 if (!usd &&
197 propType == SdfSpecTypeAttribute &&
198 !_IsConsistentAttribute(propSpec)) {
199 return TfNullPtr;
200 }
201 return propSpec;
202 }
203
204 // Returns the attribute spec with the given name if it is consistent with
205 // previously seen specs, otherwise returns NULL.
206 //
_GetRelationalAttribute(const SdfLayerHandle & layer,const SdfPath & relAttrPath)207 SdfPropertySpecHandle _GetRelationalAttribute(
208 const SdfLayerHandle& layer,
209 const SdfPath& relAttrPath)
210 {
211 SdfPropertySpecHandle attr = layer->GetAttributeAtPath(relAttrPath);
212
213 if (!attr)
214 return TfNullPtr;
215
216 if (!_firstSpec) {
217 _firstSpec = attr;
218 }
219
220 // Check that the type and variability are consistent.
221 if (!_IsConsistentAttribute(attr)) {
222 return TfNullPtr;
223 }
224 return attr;
225 }
226
_IsConsistentAttribute(const SdfPropertySpecHandle & attr)227 bool _IsConsistentAttribute(const SdfPropertySpecHandle &attr)
228 {
229 TfToken valueType;
230 SdfVariability var = SdfVariability();
231
232 // This function is performance sensitive, so as an optimization, get
233 // the underlying spec pointer to avoid excessive dormancy checks (one
234 // per dereference).
235 if (SdfSpec *specPtr = get_pointer(attr)) {
236 SdfLayer *layer = get_pointer(specPtr->GetLayer());
237 SdfPath const &path = specPtr->GetPath();
238 valueType = layer->GetFieldAs<TfToken>(path,
239 SdfFieldKeys->TypeName);
240 var = layer->GetFieldAs<SdfVariability>(
241 path, SdfFieldKeys->Variability);
242 }
243
244 if (_valueType.IsEmpty()) {
245 // First one, just record the type and variability.
246 _valueType = valueType;
247 _var = var;
248 return true;
249 }
250
251 if (_valueType != valueType) {
252 PcpErrorInconsistentAttributeTypePtr e =
253 PcpErrorInconsistentAttributeType::New();
254 e->rootSite = _propSite;
255 e->definingLayerIdentifier =
256 _firstSpec->GetLayer()->GetIdentifier();
257 e->definingSpecPath = _firstSpec->GetPath();
258 e->definingValueType = _valueType;
259 e->conflictingLayerIdentifier = attr->GetLayer()->GetIdentifier();
260 e->conflictingSpecPath = attr->GetPath();
261 e->conflictingValueType = valueType;
262 _RecordError(e);
263 return false;
264 }
265
266 if (_var != var) {
267 PcpErrorInconsistentAttributeVariabilityPtr e =
268 PcpErrorInconsistentAttributeVariability::New();
269 e->rootSite = _propSite;
270 e->definingLayerIdentifier =
271 _firstSpec->GetLayer()->GetIdentifier();
272 e->definingSpecPath = _firstSpec->GetPath();
273 e->definingVariability = _var;
274 e->conflictingLayerIdentifier = attr->GetLayer()->GetIdentifier();
275 e->conflictingSpecPath = attr->GetPath();
276 e->conflictingVariability = var;
277 _RecordError(e);
278 // Not returning false here. We will conform, not ignore.
279 }
280
281 return true;
282 }
283
284 // Convenience function to record an error both in this property
285 // index's local errors vector and the allErrors vector.
_RecordError(const PcpErrorBasePtr & err)286 void _RecordError(const PcpErrorBasePtr &err) {
287 _allErrors->push_back(err);
288 if (!_propIndex->_localErrors) {
289 _propIndex->_localErrors.reset(new PcpErrorVector);
290 }
291 _propIndex->_localErrors->push_back(err);
292 }
293
294 void _AddPropertySpecIfPermitted(
295 const SdfPropertySpecHandle& propSpec,
296 const PcpNodeRef& node,
297 Pcp_Permissions* permissions,
298 std::vector<Pcp_PropertyInfo>* propertyInfo);
299
300 private: // data
301
302 PcpPropertyIndex *_propIndex;
303 const PcpSite _propSite;
304 PcpErrorVector *_allErrors;
305 SdfPropertySpecHandle _firstSpec;
306 TfToken _valueType;
307 SdfVariability _var;
308 SdfSpecType _propType;
309 };
310
311 void
_AddPropertySpecIfPermitted(const SdfPropertySpecHandle & propSpec,const PcpNodeRef & node,Pcp_Permissions * permissions,std::vector<Pcp_PropertyInfo> * propertyInfo)312 Pcp_PropertyIndexer::_AddPropertySpecIfPermitted(
313 const SdfPropertySpecHandle& propSpec,
314 const PcpNodeRef& node,
315 Pcp_Permissions* permissions,
316 std::vector<Pcp_PropertyInfo>* propertyInfo)
317 {
318 if (permissions->previous == SdfPermissionPublic) {
319 // We're allowed to add this property.
320 propertyInfo->push_back(Pcp_PropertyInfo(propSpec, node));
321 // Accumulate permission.
322 permissions->current = propSpec->GetFieldAs(SdfFieldKeys->Permission,
323 permissions->current);
324 } else {
325 // The previous node's property permission was private, and this
326 // node also has an opinion about it. This is illegal.
327 PcpErrorPropertyPermissionDeniedPtr err =
328 PcpErrorPropertyPermissionDenied::New();
329 err->rootSite = _propSite;
330 err->propPath = propSpec->GetPath();
331 err->propType = propSpec->GetSpecType();
332 err->layerPath = propSpec->GetLayer()->GetIdentifier();
333 _RecordError(err);
334 }
335 }
336
337 void
GatherPropertySpecs(const PcpPrimIndex & primIndex,bool usd)338 Pcp_PropertyIndexer::GatherPropertySpecs(const PcpPrimIndex& primIndex,
339 bool usd)
340 {
341 const TfToken &name = _propSite.path.GetNameToken();
342
343 // Add properties in reverse strength order (weak-to-strong).
344 std::vector<Pcp_PropertyInfo> propertyInfo;
345
346 if (!usd) {
347 // We start with the permission from the last node we visited (or
348 // SdfPermissionPublic, if this is the first node). If the strongest
349 // opinion about the property's permission from this node is private,
350 // we are not allowed to add opinions from subsequent nodes.
351 PcpNodeRef prevNode;
352 Pcp_Permissions permissions;
353 TF_REVERSE_FOR_ALL(i, primIndex.GetPrimRange()) {
354 // Track & enforce permissions as we cross node boundaries.
355 PcpNodeRef curNode = i.base().GetNode();
356 if (curNode != prevNode) {
357 permissions.previous = permissions.current;
358 prevNode = curNode;
359 }
360
361 const Pcp_SdSiteRef primSite = i.base()._GetSiteRef();
362 if (SdfPropertySpecHandle propSpec =
363 _GetPrimProperty(primSite.layer, primSite.path, name, usd)) {
364 _AddPropertySpecIfPermitted(
365 propSpec, curNode, &permissions, &propertyInfo);
366 }
367 }
368
369 // At this point, the specs have been accumulated in reverse order,
370 // because we needed to do a weak-to-strong traversal for permissions.
371 // Here, we reverse the results to give us the correct order.
372 std::reverse(propertyInfo.begin(), propertyInfo.end());
373 } else {
374 for (PcpNodeRef const& node: primIndex.GetNodeRange()) {
375 if (!node.CanContributeSpecs()) {
376 continue;
377 }
378 SdfPath const& nodePath = node.GetPath();
379 for (SdfLayerRefPtr const& layer:
380 node.GetLayerStack()->GetLayers()) {
381 if (SdfPropertySpecHandle propSpec =
382 _GetPrimProperty(layer, nodePath, name, usd)) {
383 propertyInfo.emplace_back(propSpec, node);
384 }
385 }
386 }
387 }
388
389 _propIndex->_propertyStack.swap(propertyInfo);
390 }
391
392 void
GatherRelationalAttributeSpecs(const PcpPropertyIndex & relIndex,bool usd)393 Pcp_PropertyIndexer::GatherRelationalAttributeSpecs(
394 const PcpPropertyIndex& relIndex, bool usd)
395 {
396 const SdfPath& relAttrPath = _propSite.path;
397 TF_VERIFY(relAttrPath.IsRelationalAttributePath());
398
399 // Add relational attributes in reverse strength order (weak-to-strong).
400 std::vector<Pcp_PropertyInfo> propertyInfo;
401
402 // We start with the permission from the last node we visited (or
403 // SdfPermissionPublic, if this is the first node). If the strongest
404 // opinion about the property's permission from this node is private,
405 // we are not allowed to add opinions from subsequent nodes.
406 Pcp_Permissions permissions;
407
408 const PcpPropertyRange propRange = relIndex.GetPropertyRange();
409 PcpPropertyReverseIterator relIt(propRange.second);
410 const PcpPropertyReverseIterator relItEnd(propRange.first);
411
412 while (relIt != relItEnd) {
413 const PcpNodeRef curNode = relIt.GetNode();
414
415 const SdfPath relAttrPathInNodeNS =
416 PcpTranslatePathFromRootToNode(curNode, relAttrPath);
417
418 for (; relIt != relItEnd && relIt.GetNode() == curNode; ++relIt) {
419 if (relAttrPathInNodeNS.IsEmpty())
420 continue;
421
422 const SdfPropertySpecHandle relSpec = *relIt;
423 const SdfPropertySpecHandle relAttrSpec =
424 _GetRelationalAttribute(relSpec->GetLayer(),
425 relAttrPathInNodeNS);
426 if (!relAttrSpec)
427 continue;
428
429 if (usd) {
430 // USD does not enforce permissions.
431 propertyInfo.push_back(Pcp_PropertyInfo(relAttrSpec, curNode));
432 } else {
433 _AddPropertySpecIfPermitted(
434 relAttrSpec, curNode, &permissions, &propertyInfo);
435 }
436 }
437
438 // Transfer this node's attribute permission.
439 permissions.previous = permissions.current;
440 }
441
442 // At this point, the specs have been accumulated in reverse order,
443 // because we needed to do a weak-to-strong traversal for permissions.
444 // Here, we reverse the results to give us the correct order.
445 std::reverse(propertyInfo.begin(), propertyInfo.end());
446
447 _propIndex->_propertyStack.swap(propertyInfo);
448 }
449
PcpBuildPropertyIndex(const SdfPath & propertyPath,PcpCache * cache,PcpPropertyIndex * propertyIndex,PcpErrorVector * allErrors)450 void PcpBuildPropertyIndex( const SdfPath &propertyPath,
451 PcpCache *cache,
452 PcpPropertyIndex *propertyIndex,
453 PcpErrorVector *allErrors )
454 {
455 // Verify that the given path is for a property.
456 if (!TF_VERIFY(propertyPath.IsPropertyPath())) {
457 return;
458 }
459 if (!propertyIndex->IsEmpty()) {
460 TF_CODING_ERROR("Cannot build property index for %s with a non-empty "
461 "property stack.", propertyPath.GetText());
462 return;
463 }
464
465 SdfPath parentPath = propertyPath.GetParentPath();
466 if (parentPath.IsTargetPath()) {
467 // Immediate parent is a target path, so this is a relational attribute.
468 // Step up one more level to the parent relationship itself.
469 parentPath = parentPath.GetParentPath();
470 }
471
472 if (parentPath.IsPrimPath()) {
473 PcpBuildPrimPropertyIndex(
474 propertyPath,
475 *cache,
476 cache->ComputePrimIndex(parentPath, allErrors),
477 propertyIndex,
478 allErrors);
479 }
480 else if (parentPath.IsPrimPropertyPath()) {
481 const PcpSite propSite(cache->GetLayerStackIdentifier(), propertyPath);
482 Pcp_PropertyIndexer indexer(propertyIndex, propSite, allErrors);
483 // In USD mode, the PcpCache will not supply any property indexes,
484 // so we need to specifically compute one ourselves and use that.
485 //
486 // XXX: Do we need to support relational attributes in USD? Even
487 // if the USD schema doesn't contain relational attributes,
488 // should Pcp handle this for completeness?
489 if (cache->IsUsd()) {
490 PcpPropertyIndex relIndex;
491 PcpBuildPropertyIndex(parentPath, cache, &relIndex, allErrors);
492 indexer.GatherRelationalAttributeSpecs(relIndex, true);
493 }
494 else {
495 const PcpPropertyIndex& relIndex =
496 cache->ComputePropertyIndex(parentPath, allErrors);
497 indexer.GatherRelationalAttributeSpecs(relIndex, false);
498 }
499 }
500 else {
501 // CODE_COVERAGE_OFF
502 // This should not happen. Owner is not a prim or a
503 // relationship.
504 TF_CODING_ERROR(
505 "Error, the property <%s> is owned by something "
506 "that is not a prim or a relationship.",
507 propertyPath.GetText());
508 // CODE_COVERAGE_ON
509 }
510 }
511
512 void
PcpBuildPrimPropertyIndex(const SdfPath & propertyPath,const PcpCache & cache,const PcpPrimIndex & primIndex,PcpPropertyIndex * propertyIndex,PcpErrorVector * allErrors)513 PcpBuildPrimPropertyIndex( const SdfPath& propertyPath,
514 const PcpCache& cache,
515 const PcpPrimIndex& primIndex,
516 PcpPropertyIndex *propertyIndex,
517 PcpErrorVector *allErrors )
518 {
519 const PcpSite propSite(cache.GetLayerStackIdentifier(), propertyPath);
520 Pcp_PropertyIndexer indexer(propertyIndex, propSite, allErrors);
521 indexer.GatherPropertySpecs(primIndex, cache.IsUsd());
522 }
523
524 PXR_NAMESPACE_CLOSE_SCOPE
525