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 #ifndef PXR_USD_SDF_ABSTRACT_DATA_H
25 #define PXR_USD_SDF_ABSTRACT_DATA_H
26
27 #include "pxr/pxr.h"
28 #include "pxr/usd/sdf/path.h"
29 #include "pxr/usd/sdf/types.h"
30
31 #include "pxr/base/vt/value.h"
32
33 #include "pxr/base/tf/staticTokens.h"
34 #include "pxr/base/tf/token.h"
35 #include "pxr/base/tf/refBase.h"
36 #include "pxr/base/tf/weakBase.h"
37 #include "pxr/base/tf/declarePtrs.h"
38
39 #include <vector>
40 #include <type_traits>
41
42 PXR_NAMESPACE_OPEN_SCOPE
43
44 TF_DECLARE_WEAK_AND_REF_PTRS(SdfAbstractData);
45 class SdfAbstractDataSpecVisitor;
46 class SdfAbstractDataConstValue;
47 class SdfAbstractDataValue;
48
49
50 #define SDF_DATA_TOKENS \
51 ((TimeSamples, "timeSamples"))
52
53 TF_DECLARE_PUBLIC_TOKENS(SdfDataTokens, SDF_API, SDF_DATA_TOKENS);
54
55
56 /// \class SdfAbstractData
57 ///
58 /// Interface for scene description data storage.
59 ///
60 /// This is not a layer. SdfAbstractData is an anonymous container holding
61 /// scene description values. It is like an STL container, but specialized
62 /// for holding scene description.
63 ///
64 /// For any given SdfPath, an SdfAbstractData can hold one or more key/value
65 /// pairs which we call Fields. Most of the API on SdfAbstractData accesses
66 /// or modifies the value stored in a Field for a particular path and field
67 /// name.
68 ///
69 /// SdfAbstractData does not provide undo, change notification, or any strong
70 /// consistency guarantees about the scene description it contains.
71 /// Instead, it is a basis for building those things.
72 ///
73 class SdfAbstractData : public TfRefBase, public TfWeakBase
74 {
75 public:
SdfAbstractData()76 SdfAbstractData() {}
77 SDF_API
78 virtual ~SdfAbstractData();
79
80 /// Copy the data in \p source into this data object.
81 ///
82 /// The default implementation does a spec-by-spec, field-by-field
83 /// copy of \p source into this object.
84 SDF_API
85 virtual void CopyFrom(const SdfAbstractDataConstPtr& source);
86
87 /// Returns true if this data object streams its data to and from its
88 /// serialized data store on demand.
89 ///
90 /// Sdf will treat layers with streaming data differently to avoid pulling
91 /// in data unnecessarily. For example, reloading a streaming layer
92 /// will not perform fine-grained change notification, since doing
93 /// so would require the full contents of the layer to be loaded.
94 SDF_API
95 virtual bool StreamsData() const = 0;
96
97 /// Returns true if this data object has no specs, false otherwise.
98 ///
99 /// The default implementation uses a visitor to check if any specs
100 /// exist.
101 SDF_API
102 virtual bool IsEmpty() const;
103
104 /// Returns true if this data object contains the same specs and fields
105 /// as \a lhs, false otherwise.
106 ///
107 /// The default implementation does a spec-by-spec, field-by-field
108 /// comparison.
109 // XXX: What are the right semantics for this?
110 // Does it matter if the underlying implementation matches?
111 SDF_API
112 virtual bool Equals(const SdfAbstractDataRefPtr &rhs) const;
113
114 /// Writes the contents of this data object to \p out. This is primarily
115 /// for debugging purposes.
116 ///
117 /// The default implementation writes out each field for each spec.
118 SDF_API
119 virtual void WriteToStream(std::ostream& out) const;
120
121 /// \name Spec API
122 /// @{
123
124 /// Create a new spec at \a path with the given \a specType. If the spec
125 /// already exists the spec type will be changed.
126 SDF_API
127 virtual void CreateSpec(const SdfPath &path,
128 SdfSpecType specType) = 0;
129
130 /// Return true if this data has a spec for \a path.
131 SDF_API
132 virtual bool HasSpec(const SdfPath &path) const = 0;
133
134 /// Erase the spec at \a path and any fields that are on it.
135 /// Note that this does not erase child specs.
136 SDF_API
137 virtual void EraseSpec(const SdfPath &path) = 0;
138
139 /// Move the spec at \a oldPath to \a newPath, including all the
140 /// fields that are on it. This does not move any child specs.
141 SDF_API
142 virtual void MoveSpec(const SdfPath &oldPath,
143 const SdfPath &newPath) = 0;
144
145 /// Return the spec type for the spec at \a path. Returns SdfSpecTypeUnknown
146 /// if the spec doesn't exist.
147 virtual SdfSpecType GetSpecType(const SdfPath &path) const = 0;
148
149 /// Visits every spec in this SdfAbstractData object with the given
150 /// \p visitor. The order in which specs are visited is undefined.
151 /// The visitor may not modify the SdfAbstractData object it is visiting.
152 /// \sa SdfAbstractDataSpecVisitor
153 SDF_API
154 void VisitSpecs(SdfAbstractDataSpecVisitor* visitor) const;
155
156 /// @}
157
158 /// \name Field API
159 /// @{
160
161 /// Returns whether a value exists for the given \a path and \a fieldName.
162 /// Optionally returns the value if it exists.
163 SDF_API
164 virtual bool Has(const SdfPath& path, const TfToken& fieldName,
165 SdfAbstractDataValue* value) const = 0;
166
167 /// Return whether a value exists for the given \a path and \a fieldName.
168 /// Optionally returns the value if it exists.
169 SDF_API
170 virtual bool Has(const SdfPath& path, const TfToken &fieldName,
171 VtValue *value = NULL) const = 0;
172
173 /// Fill \p specType (which cannot be nullptr) as if by a call to
174 /// GetSpecType(path). If the resulting specType is not SdfSpecTypeUnknown,
175 /// then act as if Has(path, fieldName, value) was called and return its
176 /// result. In other words, the semantics of this function must be
177 /// identical to this sequence:
178 ///
179 /// \code
180 /// *specType = GetSpecType(path);
181 /// return *specType != SdfSpecTypeUnknown && Has(path, fieldName, value);
182 /// \endcode
183 SDF_API
184 virtual bool
185 HasSpecAndField(const SdfPath &path, const TfToken &fieldName,
186 SdfAbstractDataValue *value, SdfSpecType *specType) const;
187
188 /// Fill \p specType (which cannot be nullptr) as if by a call to
189 /// GetSpecType(path). If the resulting specType is not SdfSpecTypeUnknown,
190 /// then act as if Has(path, fieldName, value) was called and return its
191 /// result. In other words, the semantics of this function must be
192 /// identical to this sequence:
193 ///
194 /// \code
195 /// *specType = GetSpecType(path);
196 /// return *specType != SdfSpecTypeUnknown && Has(path, fieldName, value);
197 /// \endcode
198 SDF_API
199 virtual bool
200 HasSpecAndField(const SdfPath &path, const TfToken &fieldName,
201 VtValue *value, SdfSpecType *specType) const;
202
203 /// Return the value for the given \a path and \a fieldName. Returns an
204 /// empty value if none is set.
205 SDF_API
206 virtual VtValue Get(const SdfPath& path,
207 const TfToken& fieldName) const = 0;
208
209 /// Return the type of the value for \p fieldName on spec \p path. If no
210 /// such field exists, return typeid(void). Derived classes may optionally
211 /// override this for performance. The base implementation is equivalent
212 /// to:
213 ///
214 /// \code
215 /// return Get(path, fieldName).GetTypeid();
216 /// \endcode
217 SDF_API
218 virtual std::type_info const &
219 GetTypeid(const SdfPath &path, const TfToken &fieldName) const;
220
221 /// Set the value of the given \a path and \a fieldName.
222 ///
223 /// It's an error to set a field on a spec that does not exist. Setting a
224 /// field to an empty VtValue is the same as calling Erase() on it.
225 SDF_API
226 virtual void Set(const SdfPath &path, const TfToken &fieldName,
227 const VtValue &value) = 0;
228
229 /// Set the value of the given \a path and \a fieldName.
230 ///
231 /// It's an error to set a field on a spec that does not exist.
232 SDF_API
233 virtual void Set(const SdfPath &path, const TfToken &fieldName,
234 const SdfAbstractDataConstValue& value) = 0;
235
236 /// Remove the field at \p path and \p fieldName, if one exists.
237 SDF_API
238 virtual void Erase(const SdfPath& path,
239 const TfToken& fieldName) = 0;
240
241 /// Return the names of all the fields that are set at \p path.
242 SDF_API
243 virtual std::vector<TfToken> List(const SdfPath& path) const = 0;
244
245 /// Return the value for the given \a path and \a fieldName. Returns the
246 /// provided \a defaultValue value if none is set.
247 template <class T>
248 inline T GetAs(const SdfPath& path, const TfToken& fieldName,
249 const T& defaultValue = T()) const;
250
251 /// @}
252
253
254 /// \name Dict key access API
255 /// @{
256
257 // Return true and set \p value (if non null) if the field identified by
258 // \p path and \p fieldName is dictionary-valued, and if there is an element
259 // at \p keyPath in that dictionary. Return false otherwise. If
260 // \p keyPath names an entire sub-dictionary, set \p value to that entire
261 // sub-dictionary and return true.
262 SDF_API
263 virtual bool HasDictKey(const SdfPath& path,
264 const TfToken &fieldName,
265 const TfToken &keyPath,
266 SdfAbstractDataValue* value) const;
267 SDF_API
268 virtual bool HasDictKey(const SdfPath& path,
269 const TfToken &fieldName,
270 const TfToken &keyPath,
271 VtValue *value = NULL) const;
272
273 // Same as HasDictKey but return empty VtValue on failure.
274 SDF_API
275 virtual VtValue GetDictValueByKey(const SdfPath& path,
276 const TfToken &fieldName,
277 const TfToken &keyPath) const;
278
279 // Set the element at \p keyPath in the dictionary-valued field identified
280 // by \p path and \p fieldName. If the field itself is not
281 // dictionary-valued, replace the field with a new dictionary and set the
282 // element at \p keyPath in it. If \p value is empty, invoke
283 // EraseDictValueByKey instead.
284 SDF_API
285 virtual void SetDictValueByKey(const SdfPath& path,
286 const TfToken &fieldName,
287 const TfToken &keyPath,
288 const VtValue &value);
289 SDF_API
290 virtual void SetDictValueByKey(const SdfPath& path,
291 const TfToken &fieldName,
292 const TfToken &keyPath,
293 const SdfAbstractDataConstValue& value);
294
295 // If \p path and \p fieldName identify a dictionary-valued field with an
296 // element at \p keyPath, remove that element from the dictionary. If this
297 // leaves the dictionary empty, Erase() the entire field.
298 SDF_API
299 virtual void EraseDictValueByKey(const SdfPath& path,
300 const TfToken &fieldName,
301 const TfToken &keyPath);
302
303 // If \p path, \p fieldName, and \p keyPath identify a (sub) dictionary,
304 // return a vector of the keys in that dictionary, otherwise return an empty
305 // vector.
306 SDF_API
307 virtual std::vector<TfToken> ListDictKeys(const SdfPath& path,
308 const TfToken &fieldName,
309 const TfToken &keyPath) const;
310
311
312 /// @}
313
314
315 /// \name Time-sample API
316 ///
317 /// This API supports narrowly-targeted queries against the
318 /// "timeSamples" key of properties. In particular, it enables
319 /// asking for single time samples without pulling on the entire
320 /// set of time samples, as well as asking about the set of sample
321 /// times without pulling on the actual values at those times.
322 ///
323 /// @{
324
325 SDF_API
326 virtual std::set<double>
327 ListAllTimeSamples() const = 0;
328
329 SDF_API
330 virtual std::set<double>
331 ListTimeSamplesForPath(const SdfPath& path) const = 0;
332
333 SDF_API
334 virtual bool
335 GetBracketingTimeSamples(double time, double* tLower, double* tUpper) const = 0;
336
337 SDF_API
338 virtual size_t
339 GetNumTimeSamplesForPath(const SdfPath& path) const = 0;
340
341 SDF_API
342 virtual bool
343 GetBracketingTimeSamplesForPath(const SdfPath& path,
344 double time,
345 double* tLower, double* tUpper) const = 0;
346
347 SDF_API
348 virtual bool
349 QueryTimeSample(const SdfPath& path, double time,
350 VtValue *optionalValue = NULL) const = 0;
351 SDF_API
352 virtual bool
353 QueryTimeSample(const SdfPath& path, double time,
354 SdfAbstractDataValue *optionalValue) const = 0;
355
356 SDF_API
357 virtual void
358 SetTimeSample(const SdfPath& path, double time,
359 const VtValue & value) = 0;
360
361 SDF_API
362 virtual void
363 EraseTimeSample(const SdfPath& path, double time) = 0;
364
365 /// @}
366
367 protected:
368 /// Visits every spec in this SdfAbstractData object with the given
369 /// \p visitor. The order in which specs are visited is undefined.
370 /// The visitor may not modify the SdfAbstractData object it is visiting.
371 /// This method should \b not call \c Done() on the visitor.
372 /// \sa SdfAbstractDataSpecVisitor
373 SDF_API
374 virtual void _VisitSpecs(SdfAbstractDataSpecVisitor* visitor) const = 0;
375 };
376
377 template <class T>
GetAs(const SdfPath & path,const TfToken & field,const T & defaultVal)378 inline T SdfAbstractData::GetAs(
379 const SdfPath& path,
380 const TfToken& field, const T& defaultVal) const
381 {
382 VtValue val = Get(path, field);
383 if (val.IsHolding<T>()) {
384 return val.UncheckedGet<T>();
385 }
386 return defaultVal;
387 }
388
389 /// \class SdfAbstractDataValue
390 ///
391 /// A type-erased container for a field value in an SdfAbstractData.
392 ///
393 /// \sa SdfAbstractDataTypedValue
394 class SdfAbstractDataValue
395 {
396 public:
397 virtual bool StoreValue(const VtValue& value) = 0;
398
399 template <class T>
StoreValue(const T & v)400 bool StoreValue(const T& v)
401 {
402 if (TfSafeTypeCompare(typeid(T), valueType)) {
403 *static_cast<T*>(value) = v;
404 return true;
405 }
406 typeMismatch = true;
407 return false;
408 }
409
StoreValue(const SdfValueBlock & block)410 bool StoreValue(const SdfValueBlock& block)
411 {
412 isValueBlock = true;
413 return true;
414 }
415
416 void* value;
417 const std::type_info& valueType;
418 bool isValueBlock;
419 bool typeMismatch;
420
421 protected:
SdfAbstractDataValue(void * value_,const std::type_info & valueType_)422 SdfAbstractDataValue(void* value_, const std::type_info& valueType_)
423 : value(value_)
424 , valueType(valueType_)
425 , isValueBlock(false)
426 , typeMismatch(false)
427 { }
428 };
429
430 /// \class SdfAbstractDataTypedValue
431 ///
432 /// The fully-typed container for a field value in an \c SdfAbstractData.
433 /// An \c SdfAbstractDataTypedValue allows a consumer to pass a pointer to
434 /// an object through the virtual \c SdfAbstractData interface along with
435 /// information about that object's type. That information may allow
436 /// implementations of \c SdfAbstractData to populate the contained object
437 /// in a more efficient way, avoiding unnecessary boxing/unboxing of data.
438 ///
439 /// SdfAbstractDataTypedValue objects are intended to be transient; they
440 /// are solely used to get pointer information into and out of an
441 /// SdfAbstractData container.
442 ///
443 template <class T>
444 class SdfAbstractDataTypedValue : public SdfAbstractDataValue
445 {
446 public:
SdfAbstractDataTypedValue(T * value)447 SdfAbstractDataTypedValue(T* value)
448 : SdfAbstractDataValue(value, typeid(T))
449 { }
450
StoreValue(const VtValue & v)451 virtual bool StoreValue(const VtValue& v)
452 {
453 if (ARCH_LIKELY(v.IsHolding<T>())) {
454 *static_cast<T*>(value) = v.UncheckedGet<T>();
455 if (std::is_same<T, SdfValueBlock>::value) {
456 isValueBlock = true;
457 }
458 return true;
459 }
460
461 if (v.IsHolding<SdfValueBlock>()) {
462 isValueBlock = true;
463 return true;
464 }
465
466 typeMismatch = true;
467
468 return false;
469 }
470 };
471
472 /// \class SdfAbstractDataConstValue
473 ///
474 /// A type-erased container for a const field value in an SdfAbstractData.
475 ///
476 /// \sa SdfAbstractDataConstTypedValue
477 class SdfAbstractDataConstValue
478 {
479 public:
480 virtual bool GetValue(VtValue* value) const = 0;
481
GetValue(T * v)482 template <class T> bool GetValue(T* v) const
483 {
484 if (TfSafeTypeCompare(typeid(T), valueType)) {
485 *v = *static_cast<const T*>(value);
486 return true;
487 }
488 return false;
489 }
490
491 virtual bool IsEqual(const VtValue& value) const = 0;
492
493 const void* value;
494 const std::type_info& valueType;
495
496 protected:
SdfAbstractDataConstValue(const void * value_,const std::type_info & valueType_)497 SdfAbstractDataConstValue(const void* value_,
498 const std::type_info& valueType_)
499 : value(value_)
500 , valueType(valueType_)
501 {
502 }
503 };
504
505 /// \class SdfAbstractDataConstTypedValue
506 ///
507 /// The fully-typed container for a field value in an \c SdfAbstractData.
508 /// An \c SdfAbstractDataConstTypedValue allows a consumer to pass a pointer to
509 /// an object through the virtual \c SdfAbstractData interface along with
510 /// information about that object's type. That information may allow
511 /// implementations of \c SdfAbstractData to store the contained object
512 /// in a more efficient way, avoiding unnecessary boxing/unboxing of data.
513 ///
514 /// SdfAbstractDataConstTypedValue objects are intended to be transient; they
515 /// are solely used to get pointer information into an SdfAbstractData
516 /// container.
517 ///
518 template <class T>
519 class SdfAbstractDataConstTypedValue : public SdfAbstractDataConstValue
520 {
521 public:
SdfAbstractDataConstTypedValue(const T * value)522 SdfAbstractDataConstTypedValue(const T* value)
523 : SdfAbstractDataConstValue(value, typeid(T))
524 { }
525
GetValue(VtValue * v)526 virtual bool GetValue(VtValue* v) const
527 {
528 *v = _GetValue();
529 return true;
530 }
531
IsEqual(const VtValue & v)532 virtual bool IsEqual(const VtValue& v) const
533 {
534 return (v.IsHolding<T>() && v.UncheckedGet<T>() == _GetValue());
535 }
536
537 private:
_GetValue()538 const T& _GetValue() const
539 {
540 return *static_cast<const T*>(value);
541 }
542 };
543
544 // Specialization of SdAbstractDataConstTypedValue that converts character
545 // arrays to std::string.
546 template <int N>
547 class SdfAbstractDataConstTypedValue<char[N]>
548 : public SdfAbstractDataConstTypedValue<std::string>
549 {
550 public:
551 typedef char CharArray[N];
SdfAbstractDataConstTypedValue(const CharArray * value)552 SdfAbstractDataConstTypedValue(const CharArray* value)
553 : SdfAbstractDataConstTypedValue<std::string>(&_str)
554 , _str(*value)
555 { }
556
557 private:
558 std::string _str;
559 };
560
561 /// \class SdfAbstractDataSpecVisitor
562 ///
563 /// Base class for objects used to visit specs in an SdfAbstractData object.
564 ///
565 /// \sa SdfAbstractData::VisitSpecs.
566 class SdfAbstractDataSpecVisitor
567 {
568 public:
569 SDF_API
570 virtual ~SdfAbstractDataSpecVisitor();
571
572 /// \c SdfAbstractData::VisitSpecs calls this function for every entry it
573 /// contains, passing itself as \p data and the entry's \p path. Return
574 /// false to stop iteration early, true to continue.
575 SDF_API
576 virtual bool VisitSpec(const SdfAbstractData& data,
577 const SdfPath& path) = 0;
578
579 /// \c SdfAbstractData::VisitSpecs will call this after visitation is
580 /// complete, even if some \c VisitSpec() returned \c false.
581 SDF_API
582 virtual void Done(const SdfAbstractData& data) = 0;
583 };
584
585 PXR_NAMESPACE_CLOSE_SCOPE
586
587 #endif // PXR_USD_SDF_ABSTRACT_DATA_H
588