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/property.h"
26 #include "pxr/usd/usd/resolver.h"
27 #include "pxr/usd/usd/stage.h"
28 #include "pxr/usd/pcp/targetIndex.h"
29 
30 #include <boost/iterator/transform_iterator.hpp>
31 
32 PXR_NAMESPACE_OPEN_SCOPE
33 
34 
35 SdfPropertySpecHandleVector
GetPropertyStack(UsdTimeCode time) const36 UsdProperty::GetPropertyStack(UsdTimeCode time) const
37 {
38     return _GetStage()->_GetPropertyStack(*this, time);
39 }
40 
41 TfToken
GetBaseName() const42 UsdProperty::GetBaseName() const
43 {
44     std::string const &fullName = _PropName().GetString();
45     size_t delim = fullName.rfind(GetNamespaceDelimiter());
46 
47     if (!TF_VERIFY(delim != fullName.size()-1))
48         return TfToken();
49 
50     return ((delim == std::string::npos) ?
51             _PropName() :
52             TfToken(fullName.c_str() + delim+1));
53 }
54 
55 TfToken
GetNamespace() const56 UsdProperty::GetNamespace() const
57 {
58     std::string const &fullName = _PropName().GetString();
59     size_t delim = fullName.rfind(GetNamespaceDelimiter());
60 
61     if (!TF_VERIFY(delim != fullName.size()-1))
62         return TfToken();
63 
64     return ((delim == std::string::npos) ?
65             TfToken() :
66             TfToken(fullName.substr(0, delim)));
67 }
68 
69 std::vector<std::string>
SplitName() const70 UsdProperty::SplitName() const
71 {
72     return SdfPath::TokenizeIdentifier(_PropName());
73 }
74 
75 std::string
GetDisplayGroup() const76 UsdProperty::GetDisplayGroup() const
77 {
78     std::string result;
79     GetMetadata(SdfFieldKeys->DisplayGroup, &result);
80     return result;
81 }
82 
83 bool
SetDisplayGroup(const std::string & displayGroup) const84 UsdProperty::SetDisplayGroup(const std::string& displayGroup) const
85 {
86     return SetMetadata(SdfFieldKeys->DisplayGroup, displayGroup);
87 }
88 
89 bool
ClearDisplayGroup() const90 UsdProperty::ClearDisplayGroup() const
91 {
92     return ClearMetadata(SdfFieldKeys->DisplayGroup);
93 }
94 
95 bool
HasAuthoredDisplayGroup() const96 UsdProperty::HasAuthoredDisplayGroup() const
97 {
98     return HasAuthoredMetadata(SdfFieldKeys->DisplayGroup);
99 }
100 
101 std::vector<std::string>
GetNestedDisplayGroups() const102 UsdProperty::GetNestedDisplayGroups() const
103 {
104     return TfStringTokenize(GetDisplayGroup(), ":");
105 }
106 
107 bool
SetNestedDisplayGroups(const std::vector<std::string> & nestedGroups) const108 UsdProperty::SetNestedDisplayGroups(const std::vector<std::string>& nestedGroups) const
109 {
110     return SetDisplayGroup(SdfPath::JoinIdentifier(nestedGroups));
111 }
112 
113 
114 std::string
GetDisplayName() const115 UsdProperty::GetDisplayName() const
116 {
117     std::string result;
118     GetMetadata(SdfFieldKeys->DisplayName, &result);
119     return result;
120 }
121 
122 bool
SetDisplayName(const std::string & newDisplayName) const123 UsdProperty::SetDisplayName(const std::string& newDisplayName) const
124 {
125     return SetMetadata(SdfFieldKeys->DisplayName, newDisplayName);
126 }
127 
128 bool
ClearDisplayName() const129 UsdProperty::ClearDisplayName() const
130 {
131     return ClearMetadata(SdfFieldKeys->DisplayName);
132 }
133 
134 bool
HasAuthoredDisplayName() const135 UsdProperty::HasAuthoredDisplayName() const
136 {
137     return HasAuthoredMetadata(SdfFieldKeys->DisplayName);
138 }
139 
140 bool
IsCustom() const141 UsdProperty::IsCustom() const
142 {
143     return _GetStage()->_IsCustom(*this);
144 }
145 
146 bool
SetCustom(bool isCustom) const147 UsdProperty::SetCustom(bool isCustom) const
148 {
149     return SetMetadata(SdfFieldKeys->Custom, isCustom);
150 }
151 
152 bool
IsDefined() const153 UsdProperty::IsDefined() const
154 {
155     return IsValid();
156 }
157 
158 bool
IsAuthored() const159 UsdProperty::IsAuthored() const
160 {
161     // Look for the strongest authored property spec.
162     for (Usd_Resolver res(
163              &GetPrim().GetPrimIndex()); res.IsValid(); res.NextLayer()) {
164         if (res.GetLayer()->HasSpec(
165                 res.GetLocalPath().AppendProperty(_PropName()))) {
166             return true;
167         }
168     }
169     return false;
170 }
171 
172 bool
IsAuthoredAt(const UsdEditTarget & editTarget) const173 UsdProperty::IsAuthoredAt(const UsdEditTarget &editTarget) const
174 {
175     if (editTarget.IsValid()) {
176         SdfPath mappedPath = editTarget.MapToSpecPath(GetPath());
177         return !mappedPath.IsEmpty() &&
178             editTarget.GetLayer()->HasSpec(mappedPath);
179     }
180     return false;
181 }
182 
183 UsdProperty
FlattenTo(const UsdPrim & parent) const184 UsdProperty::FlattenTo(const UsdPrim &parent) const
185 {
186     return _GetStage()->_FlattenProperty(*this, parent, GetName());
187 }
188 
189 UsdProperty
FlattenTo(const UsdPrim & parent,const TfToken & propName) const190 UsdProperty::FlattenTo(const UsdPrim &parent, const TfToken &propName) const
191 {
192     return _GetStage()->_FlattenProperty(*this, parent, propName);
193 }
194 
195 UsdProperty
FlattenTo(const UsdProperty & property) const196 UsdProperty::FlattenTo(const UsdProperty &property) const
197 {
198     return _GetStage()->_FlattenProperty(
199         *this, property.GetPrim(), property.GetName());
200 }
201 
202 // Map from path to replacement for remapping target paths during flattening.
203 using _PathMap = std::vector<std::pair<SdfPath, SdfPath>>;
204 
205 // Apply path remappings to a list of target paths.
206 static SdfPath
_MapPath(_PathMap const & map,SdfPath const & path)207 _MapPath(_PathMap const &map, SdfPath const &path)
208 {
209     using boost::make_transform_iterator;
210 
211     if (map.empty()) {
212         return path;
213     }
214 
215     auto it = SdfPathFindLongestPrefix(
216         make_transform_iterator(map.begin(), TfGet<0>()),
217         make_transform_iterator(map.end(), TfGet<0>()), path);
218     if (it.base() != map.end()) {
219         return path.ReplacePrefix(it.base()->first, it.base()->second);
220     }
221     return path;
222 }
223 
224 bool
_GetTargets(SdfSpecType specType,SdfPathVector * out,bool * foundErrors) const225 UsdProperty::_GetTargets(SdfSpecType specType, SdfPathVector *out,
226                          bool *foundErrors) const
227 {
228     if (!TF_VERIFY(specType == SdfSpecTypeAttribute ||
229                    specType == SdfSpecTypeRelationship)) {
230         return false;
231     }
232 
233     TRACE_FUNCTION();
234 
235     UsdStage *stage = _GetStage();
236     PcpErrorVector pcpErrors;
237     PcpTargetIndex targetIndex;
238     {
239         // Our intention is that the following code requires read-only
240         // access to the PcpCache, so use a const-ref.
241         const PcpCache& pcpCache(*stage->_GetPcpCache());
242         // In USD mode, Pcp does not cache property indexes, so we
243         // compute one here ourselves and use that.  First, we need
244         // to get the prim index of the owning prim.
245         const PcpPrimIndex &primIndex = _Prim()->GetPrimIndex();
246         // PERFORMANCE: Here we can't avoid constructing the full property path
247         // without changing the Pcp API.  We're about to do serious
248         // composition/indexing, though, so the added expense may be neglible.
249         const PcpSite propSite(pcpCache.GetLayerStackIdentifier(), GetPath());
250         PcpPropertyIndex propIndex;
251         PcpBuildPrimPropertyIndex(propSite.path, pcpCache, primIndex,
252                                   &propIndex, &pcpErrors);
253         PcpBuildTargetIndex(propSite, propIndex, specType,
254                             &targetIndex, &pcpErrors);
255     }
256 
257     if (!targetIndex.paths.empty() && _Prim()->IsInPrototype()) {
258 
259         // Walk up to the root while we're in (nested) instance-land.  When we
260         // hit an instance or a prototype, add a mapping for the prototype
261         // source prim index path to this particular instance (proxy) path.
262         _PathMap pathMap;
263 
264         // This prim might be an instance proxy inside a prototype, if so use
265         // its prototype, but be sure to skip up to the parent if *this* prim is
266         // an instance.  Target paths on *this* prim are in the "space" of its
267         // next ancestral prototype, just as how attribute & metadata values
268         // come from the instance itself, not its prototype.
269         UsdPrim prim = GetPrim();
270         if (prim.IsInstance()) {
271             prim = prim.GetParent();
272         }
273         for (; prim; prim = prim.GetParent()) {
274             UsdPrim prototype;
275             if (prim.IsInstance()) {
276                 prototype = prim.GetPrototype();
277             } else if (prim.IsPrototype()) {
278                 prototype = prim;
279             }
280             if (prototype) {
281                 pathMap.emplace_back(prototype._GetSourcePrimIndex().GetPath(),
282                                      prim.GetPath());
283             }
284         };
285         std::sort(pathMap.begin(), pathMap.end());
286 
287         // Now map the targets.
288         for (SdfPath const &target : targetIndex.paths) {
289             out->push_back(_MapPath(pathMap, target));
290             if (out->back().IsEmpty()) {
291                 out->pop_back();
292             }
293         }
294     }
295     else {
296         out->swap(targetIndex.paths);
297     }
298 
299     // TODO: handle errors
300     const bool isClean = pcpErrors.empty();
301     if (!isClean) {
302         stage->_ReportPcpErrors(pcpErrors,
303             TfStringPrintf(specType == SdfSpecTypeAttribute ?
304                            "getting connections for attribute <%s>" :
305                            "getting targets for relationship <%s>",
306                            GetPath().GetText()));
307         if (foundErrors) {
308             *foundErrors = true;
309         }
310     }
311 
312     return isClean && targetIndex.hasTargetOpinions;
313 }
314 
315 PXR_NAMESPACE_CLOSE_SCOPE
316 
317