1 //
2 // Copyright 2019 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/stageLoadRules.h"
26 #include "pxr/base/tf/diagnostic.h"
27 #include "pxr/base/tf/ostreamMethods.h"
28 #include "pxr/base/tf/registryManager.h"
29 #include "pxr/base/tf/stl.h"
30 
31 #include <boost/functional/hash.hpp>
32 
33 #include <algorithm>
34 
35 PXR_NAMESPACE_OPEN_SCOPE
36 
TF_REGISTRY_FUNCTION(TfEnum)37 TF_REGISTRY_FUNCTION(TfEnum)
38 {
39     TF_ADD_ENUM_NAME(UsdStageLoadRules::AllRule);
40     TF_ADD_ENUM_NAME(UsdStageLoadRules::OnlyRule);
41     TF_ADD_ENUM_NAME(UsdStageLoadRules::NoneRule);
42 }
43 
44 using _GetPath = TfGet<0>;
45 
46 UsdStageLoadRules
LoadNone()47 UsdStageLoadRules::LoadNone()
48 {
49     UsdStageLoadRules ret;
50     ret._rules.emplace_back(SdfPath::AbsoluteRootPath(), NoneRule);
51     return ret;
52 }
53 
54 void
LoadWithDescendants(SdfPath const & path)55 UsdStageLoadRules::LoadWithDescendants(SdfPath const &path)
56 {
57     auto range = SdfPathFindPrefixedRange(
58         _rules.begin(), _rules.end(), path, _GetPath());
59 
60     _rules.insert(_rules.erase(range.first, range.second), { path, AllRule });
61 }
62 
63 void
LoadWithoutDescendants(SdfPath const & path)64 UsdStageLoadRules::LoadWithoutDescendants(SdfPath const &path)
65 {
66     auto range = SdfPathFindPrefixedRange(
67         _rules.begin(), _rules.end(), path, _GetPath());
68 
69     _rules.insert(_rules.erase(range.first, range.second), { path, OnlyRule });
70 }
71 
72 void
Unload(SdfPath const & path)73 UsdStageLoadRules::Unload(SdfPath const &path)
74 {
75     auto range = SdfPathFindPrefixedRange(
76         _rules.begin(), _rules.end(), path, _GetPath());
77 
78     _rules.emplace(_rules.erase(range.first, range.second), path, NoneRule);
79 }
80 
81 void
LoadAndUnload(const SdfPathSet & loadSet,const SdfPathSet & unloadSet,UsdLoadPolicy policy)82 UsdStageLoadRules::LoadAndUnload(const SdfPathSet &loadSet,
83                                  const SdfPathSet &unloadSet,
84                                  UsdLoadPolicy policy)
85 {
86     // XXX Could potentially be faster...
87     for (SdfPath const &path: unloadSet) {
88         Unload(path);
89     }
90     for (SdfPath const &path: loadSet) {
91         if (policy == UsdLoadWithDescendants) {
92             LoadWithDescendants(path);
93         }
94         else if (policy == UsdLoadWithoutDescendants) {
95             LoadWithoutDescendants(path);
96         }
97     }
98 }
99 
100 void
AddRule(SdfPath const & path,Rule rule)101 UsdStageLoadRules::AddRule(SdfPath const &path, Rule rule)
102 {
103     auto iter = _LowerBound(path);
104     if (iter != _rules.end() && iter->first == path) {
105         iter->second = rule;
106     }
107     else {
108         _rules.emplace(iter, path, rule);
109     }
110 }
111 
112 void
SetRules(std::vector<std::pair<SdfPath,Rule>> const & rules)113 UsdStageLoadRules::SetRules(std::vector<std::pair<SdfPath, Rule>> const &rules)
114 {
115     _rules = rules;
116 }
117 
118 void
Minimize()119 UsdStageLoadRules::Minimize()
120 {
121     if (_rules.empty()) {
122         return;
123     }
124 
125     // If there's an 'AllRule' for '/', remove it -- the implicit rule for '/'
126     // with no entry present is already 'AllRule'.
127     if (_rules.front().second == AllRule &&
128         _rules.front().first == SdfPath::AbsoluteRootPath()) {
129         _rules.erase(_rules.begin());
130     }
131 
132     if (_rules.size() <= 1) {
133         return;
134     }
135 
136     // Walk forward, keeping a stack of "parents" (really nearest ancestors).
137     // Any entry whose next nearest ancestral entry has the same rule can be
138     // removed as redundant.
139     std::vector<size_t> parentIdxStack;
140     SdfPath curPrefix = _rules.front().first;
141     for (size_t i = 0; i != _rules.size(); ++i) {
142         auto const &cur = _rules[i];
143 
144         // Pop ancestral rules off the stack until we find the one for cur, or
145         // until there are no more ancestral rules.
146         while (!parentIdxStack.empty() &&
147                !cur.first.HasPrefix(_rules[parentIdxStack.back()].first)) {
148             parentIdxStack.pop_back();
149         }
150 
151         // Parent rule is implicitly 'AllRule' if there is no parent.
152         Rule parentRule = parentIdxStack.empty() ?
153             AllRule : _rules[parentIdxStack.back()].second;
154         if (cur.second == parentRule) {
155             // Remove this rule.
156             _rules.erase(_rules.begin() + i);
157             --i;
158         }
159         else {
160             // This rule is kept and becomes the next parent.
161             parentIdxStack.push_back(i);
162         }
163     }
164 }
165 
166 bool
IsLoaded(SdfPath const & path) const167 UsdStageLoadRules::IsLoaded(SdfPath const &path) const
168 {
169     return GetEffectiveRuleForPath(path) != NoneRule;
170 }
171 
172 bool
IsLoadedWithAllDescendants(SdfPath const & path) const173 UsdStageLoadRules::IsLoadedWithAllDescendants(SdfPath const &path) const
174 {
175     if (_rules.empty()) {
176         // LoadAll case.
177         return true;
178     }
179 
180     // Find the longest prefix of \p path.  It must be an AllRule.
181     auto prefixIter = SdfPathFindLongestPrefix(
182         _rules.begin(), _rules.end(), path, _GetPath());
183 
184     // There must either be no prefix, or a prefix that's AllRule.
185     if (prefixIter != _rules.end() && prefixIter->second != AllRule) {
186         return false;
187     }
188 
189     // Find the range of paths prefixed by the given path.  There must either be
190     // none, or all of them must be AllRules.
191     auto range = SdfPathFindPrefixedRange(
192         _rules.begin(), _rules.end(), path, _GetPath());
193 
194     for (auto iter = range.first; iter != range.second; ++iter) {
195         if (iter->second != AllRule) {
196             return false;
197         }
198     }
199 
200     // This path and all descendants are considered loaded.
201     return true;
202 }
203 
204 bool
IsLoadedWithNoDescendants(SdfPath const & path) const205 UsdStageLoadRules::IsLoadedWithNoDescendants(SdfPath const &path) const
206 {
207     if (_rules.empty()) {
208         // LoadAll case.
209         return false;
210     }
211 
212     // Look for \p path in _rules.  It must be present and must be an OnlyRule.
213     auto compareFn = [](std::pair<SdfPath, Rule> const &entry,
214                         SdfPath const &p) {
215         return entry.first < p;
216     };
217     auto iter =
218         std::lower_bound(_rules.begin(), _rules.end(), path, compareFn);
219 
220     if (iter == _rules.end() ||
221         iter->first != path ||
222         iter->second != OnlyRule) {
223         return false;
224     }
225 
226     // Skip the entry for this path, and scan forward to the next non-NoneRule.
227     // If it has this path as prefix, return false, otherwise return true.
228     for (++iter; iter != _rules.end(); ++iter) {
229         if (iter->second != NoneRule) {
230             return !iter->first.HasPrefix(path);
231         }
232     }
233 
234     // Encountered only NoneRules: this path is loaded with no descendants.
235     return true;
236 }
237 
238 UsdStageLoadRules::Rule
GetEffectiveRuleForPath(SdfPath const & path) const239 UsdStageLoadRules::GetEffectiveRuleForPath(SdfPath const &path) const
240 {
241     if (_rules.empty()) {
242         // LoadAll case.
243         return AllRule;
244     }
245 
246     // Find the longest prefix of \p path.  If it is an AllRule, or it is an
247     // OnlyRule and its path is the same as this path, then this path is
248     // included.
249     auto prefixIter = SdfPathFindLongestPrefix(
250         _rules.begin(), _rules.end(), path, _GetPath());
251 
252     // If no prefix present, this path is included.
253     if (prefixIter == _rules.end()) {
254         return AllRule;
255     }
256 
257     // If the prefix path's rule is AllRule, this path is included.
258     if (prefixIter->second == AllRule) {
259         return AllRule;
260     }
261 
262     // If the prefix *is* this path and it's OnlyRule, we have the answer in
263     // hand.
264     if (prefixIter->first == path && prefixIter->second == OnlyRule) {
265         return OnlyRule;
266     }
267 
268     // Otherwise find all the "direct child"-type paths of \p path.  That is,
269     // subsequent paths that are prefixed _by_ \p path, but skipping deeper such
270     // paths.  For example, if path is /Foo/Bar, then we want to consider rules
271     // for /Foo/Bar/Baz and /Foo/Bar/Qux, but not /Foo/Bar/Baz/Child.  If one of
272     // these exists and it is an AllRule or OnlyRule, then this path is included
273     // as 'OnlyRule', since it's part of the ancestor chain.  Otherwise this
274     // path is excluded.
275 
276     ++prefixIter;
277     auto range = SdfPathFindPrefixedRange(
278         prefixIter, _rules.end(), path, _GetPath());
279 
280     // If there are no such paths, this path is a NoneRule.
281     if (range.first == range.second) {
282         return NoneRule;
283     }
284 
285     auto iter = range.first;
286     while (iter != range.second) {
287         if (iter->second == OnlyRule || iter->second == AllRule) {
288             return OnlyRule;
289         }
290         // Skip anything prefixed by iter->first.
291         auto next = iter + 1;
292         while (next != range.second && next->first.HasPrefix(iter->first)) {
293             ++next;
294         }
295         iter = next;
296     }
297     return NoneRule;
298 }
299 
300 bool
operator ==(UsdStageLoadRules const & other) const301 UsdStageLoadRules::operator==(UsdStageLoadRules const &other) const {
302     return _rules == other._rules;
303 }
304 
305 std::vector<std::pair<SdfPath, UsdStageLoadRules::Rule> >::const_iterator
_LowerBound(SdfPath const & path) const306 UsdStageLoadRules::_LowerBound(SdfPath const &path) const
307 {
308     return std::lower_bound(
309         _rules.begin(), _rules.end(), path,
310         [](std::pair<SdfPath, Rule> const &elem, SdfPath const &path) {
311             return elem.first < path;
312         });
313 }
314 
315 std::vector<std::pair<SdfPath, UsdStageLoadRules::Rule> >::iterator
_LowerBound(SdfPath const & path)316 UsdStageLoadRules::_LowerBound(SdfPath const &path)
317 {
318     return std::lower_bound(
319         _rules.begin(), _rules.end(), path,
320         [](std::pair<SdfPath, Rule> const &elem, SdfPath const &path) {
321             return elem.first < path;
322         });
323 }
324 
325 std::ostream &
operator <<(std::ostream & os,std::pair<SdfPath,UsdStageLoadRules::Rule> const & p)326 operator<<(std::ostream &os,
327            std::pair<SdfPath, UsdStageLoadRules::Rule> const &p) {
328     return os << "(<" << p.first << ">, " <<
329         (p.second == UsdStageLoadRules::AllRule ? "AllRule" :
330          p.second == UsdStageLoadRules::OnlyRule ? "OnlyRule" :
331          p.second == UsdStageLoadRules::NoneRule ? "NoneRule" :
332          "<invalid value>") << ")";
333 }
334 
335 std::ostream &
operator <<(std::ostream & os,UsdStageLoadRules const & rules)336 operator<<(std::ostream &os, UsdStageLoadRules const &rules)
337 {
338     return os << "UsdStageLoadRules(" << rules._rules << ")";
339 }
340 
341 size_t
hash_value(UsdStageLoadRules const & rules)342 hash_value(UsdStageLoadRules const &rules)
343 {
344     boost::hash<std::vector<std::pair<SdfPath, UsdStageLoadRules::Rule> > > h;
345     return h(rules._rules);
346 }
347 
348 PXR_NAMESPACE_CLOSE_SCOPE
349 
350