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