// // Copyright 2016 Pixar // // Licensed under the Apache License, Version 2.0 (the "Apache License") // with the following modification; you may not use this file except in // compliance with the Apache License and the following modification to it: // Section 6. Trademarks. is deleted and replaced with: // // 6. Trademarks. This License does not grant permission to use the trade // names, trademarks, service marks, or product names of the Licensor // and its affiliates, except as required to comply with Section 4(c) of // the License and to reproduce the content of the NOTICE file. // // You may obtain a copy of the Apache License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the Apache License with the above modification is // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the Apache License for the specific // language governing permissions and limitations under the Apache License. // #ifndef PXR_USD_SDF_PATH_H #define PXR_USD_SDF_PATH_H #include "pxr/pxr.h" #include "pxr/usd/sdf/api.h" #include "pxr/usd/sdf/pool.h" #include "pxr/usd/sdf/tokens.h" #include "pxr/base/tf/stl.h" #include "pxr/base/tf/token.h" #include "pxr/base/vt/traits.h" #include #include #include #include #include #include #include #include #include PXR_NAMESPACE_OPEN_SCOPE class Sdf_PathNode; class SdfPathAncestorsRange; // Ref-counting pointer to a path node. // Intrusive ref-counts are used to keep the size of SdfPath // the same as a raw pointer. (shared_ptr, by comparison, // is the size of two pointers.) typedef boost::intrusive_ptr Sdf_PathNodeConstRefPtr; void intrusive_ptr_add_ref(Sdf_PathNode const *); void intrusive_ptr_release(Sdf_PathNode const *); // Tags used for the pools of path nodes. struct Sdf_PathPrimTag; struct Sdf_PathPropTag; // These are validated below. static constexpr size_t Sdf_SizeofPrimPathNode = sizeof(void *) * 3; static constexpr size_t Sdf_SizeofPropPathNode = sizeof(void *) * 3; using Sdf_PathPrimPartPool = Sdf_Pool< Sdf_PathPrimTag, Sdf_SizeofPrimPathNode, /*regionBits=*/8>; using Sdf_PathPropPartPool = Sdf_Pool< Sdf_PathPropTag, Sdf_SizeofPropPathNode, /*regionBits=*/8>; using Sdf_PathPrimHandle = Sdf_PathPrimPartPool::Handle; using Sdf_PathPropHandle = Sdf_PathPropPartPool::Handle; // This handle class wraps up the raw Prim/PropPartPool handles. template struct Sdf_PathNodeHandleImpl { private: typedef Sdf_PathNodeHandleImpl this_type; public: constexpr Sdf_PathNodeHandleImpl() noexcept {}; explicit Sdf_PathNodeHandleImpl(Sdf_PathNode const *p, bool add_ref = true) : _poolHandle(Handle::GetHandle(reinterpret_cast(p))) { if (p && add_ref) { _AddRef(p); } } explicit Sdf_PathNodeHandleImpl(Handle h, bool add_ref = true) : _poolHandle(h) { if (h && add_ref) { _AddRef(); } } Sdf_PathNodeHandleImpl(Sdf_PathNodeHandleImpl const &rhs) : _poolHandle(rhs._poolHandle) { if (_poolHandle) { _AddRef(); } } ~Sdf_PathNodeHandleImpl() { if (_poolHandle) { _DecRef(); } } Sdf_PathNodeHandleImpl & operator=(Sdf_PathNodeHandleImpl const &rhs) { if (Counted && *this == rhs) { return *this; } this_type(rhs).swap(*this); return *this; } Sdf_PathNodeHandleImpl(Sdf_PathNodeHandleImpl &&rhs) noexcept : _poolHandle(rhs._poolHandle) { rhs._poolHandle = nullptr; } Sdf_PathNodeHandleImpl & operator=(Sdf_PathNodeHandleImpl &&rhs) noexcept { this_type(std::move(rhs)).swap(*this); return *this; } Sdf_PathNodeHandleImpl & operator=(Sdf_PathNode const *rhs) noexcept { this_type(rhs).swap(*this); return *this; } void reset() noexcept { _poolHandle = Handle { nullptr }; } Sdf_PathNode const * get() const noexcept { return reinterpret_cast(_poolHandle.GetPtr()); } Sdf_PathNode const & operator*() const { return *get(); } Sdf_PathNode const * operator->() const { return get(); } explicit operator bool() const noexcept { return static_cast(_poolHandle); } void swap(Sdf_PathNodeHandleImpl &rhs) noexcept { _poolHandle.swap(rhs._poolHandle); } inline bool operator==(Sdf_PathNodeHandleImpl const &rhs) const noexcept { return _poolHandle == rhs._poolHandle; } inline bool operator!=(Sdf_PathNodeHandleImpl const &rhs) const noexcept { return _poolHandle != rhs._poolHandle; } inline bool operator<(Sdf_PathNodeHandleImpl const &rhs) const noexcept { return _poolHandle < rhs._poolHandle; } private: void _AddRef(Sdf_PathNode const *p) const { if (Counted) { intrusive_ptr_add_ref(p); } } void _AddRef() const { _AddRef(get()); } void _DecRef() const { if (Counted) { intrusive_ptr_release(get()); } } Handle _poolHandle { nullptr }; }; using Sdf_PathPrimNodeHandle = Sdf_PathNodeHandleImpl; using Sdf_PathPropNodeHandle = Sdf_PathNodeHandleImpl; /// A set of SdfPaths. typedef std::set SdfPathSet; /// A vector of SdfPaths. typedef std::vector SdfPathVector; // Tell VtValue that SdfPath is cheap to copy. VT_TYPE_IS_CHEAP_TO_COPY(class SdfPath); /// \class SdfPath /// /// A path value used to locate objects in layers or scenegraphs. /// /// \section sec_SdfPath_Overview Overview /// /// SdfPath is used in several ways: /// \li As a storage key for addressing and accessing values held in a SdfLayer /// \li As a namespace identity for scenegraph objects /// \li As a way to refer to other scenegraph objects through relative paths /// /// The paths represented by an SdfPath class may be either relative or /// absolute. Relative paths are relative to the prim object that contains them /// (that is, if an SdfRelationshipSpec target is relative, it is relative to /// the SdfPrimSpec object that owns the SdfRelationshipSpec object). /// /// SdfPath objects can be readily created from and converted back to strings, /// but as SdfPath objects, they have behaviors that make it easy and efficient /// to work with them. The SdfPath class provides a full range of methods for /// manipulating scene paths by appending a namespace child, appending a /// relationship target, getting the parent path, /// and so on. Since the SdfPath class uses a node-based representation /// internally, you should use the editing functions rather than converting to /// and from strings if possible. /// /// \section sec_SdfPath_Syntax Path Syntax /// /// Like a filesystem path, an SdfPath is conceptually just a sequence of /// path components. Unlike a filesystem path, each component has a type, /// and the type is indicated by the syntax. /// /// Two separators are used between parts of a path. A slash ("/") following an /// identifier is used to introduce a namespace child. A period (".") following /// an identifier is used to introduce a property. A property may also have /// several non-sequential colons (':') in its name to provide a rudimentary /// namespace within properties but may not end or begin with a colon. /// /// A leading slash in the string representation of an SdfPath object indicates /// an absolute path. Two adjacent periods indicate the parent namespace. /// /// Brackets ("[" and "]") are used to indicate relationship target paths for /// relational attributes. /// /// The first part in a path is assumed to be a namespace child unless /// it is preceded by a period. That means: /// \li /Foo is an absolute path specifying the root prim Foo. /// \li /Foo/Bar is an absolute path specifying namespace child Bar /// of root prim Foo. /// \li /Foo/Bar.baz is an absolute path specifying property \c baz of /// namespace child Bar of root prim Foo. /// \li Foo is a relative path specifying namespace child Foo of /// the current prim. /// \li Foo/Bar is a relative path specifying namespace child Bar of /// namespace child Foo of the current prim. /// \li Foo/Bar.baz is a relative path specifying property \c baz of /// namespace child Bar of namespace child Foo of the current prim. /// \li .foo is a relative path specifying the property \c foo of the /// current prim. /// \li /Foo.bar[/Foo.baz].attrib is a relational attribute path. The /// relationship /Foo.bar has a target /Foo.baz. There is a /// relational attribute \c attrib on that relationship->target pair. /// /// \section sec_SdfPath_ThreadSafety A Note on Thread-Safety /// /// SdfPath is strongly thread-safe, in the sense that zero additional /// synchronization is required between threads creating or using SdfPath /// values. Just like TfToken, SdfPath values are immutable. Internally, /// SdfPath uses a global prefix tree to efficiently share representations /// of paths, and provide fast equality/hashing operations, but /// modifications to this table are internally synchronized. Consequently, /// as with TfToken, for best performance it is important to minimize /// the number of values created (since it requires synchronized access to /// this table) or copied (since it requires atomic ref-counting operations). /// class SdfPath : boost::totally_ordered { public: /// The empty path value, equivalent to SdfPath(). SDF_API static const SdfPath & EmptyPath(); /// The absolute path representing the top of the /// namespace hierarchy. SDF_API static const SdfPath & AbsoluteRootPath(); /// The relative path representing "self". SDF_API static const SdfPath & ReflexiveRelativePath(); /// \name Constructors /// @{ /// Constructs the default, empty path. /// constexpr SdfPath() = default; /// Creates a path from the given string. /// /// If the given string is not a well-formed path, this will raise /// a Tf error. Note that passing an empty std::string() will also /// raise an error; the correct way to get the empty path is SdfPath(). /// /// Internal dot-dots will be resolved by removing the first dot-dot, /// the element preceding it, and repeating until no internal dot-dots /// remain. /// /// Note that most often new paths are expected to be created by /// asking existing paths to return modified versions of themselves. // // XXX We may want to revisit the behavior when constructing // a path with an empty string ("") to accept it without error and // return EmptyPath. SDF_API explicit SdfPath(const std::string &path); /// @} /// \name Querying paths /// @{ /// Returns the number of path elements in this path. SDF_API size_t GetPathElementCount() const; /// Returns whether the path is absolute. SDF_API bool IsAbsolutePath() const; /// Return true if this path is the AbsoluteRootPath(). SDF_API bool IsAbsoluteRootPath() const; /// Returns whether the path identifies a prim. SDF_API bool IsPrimPath() const; /// Returns whether the path identifies a prim or the absolute root. SDF_API bool IsAbsoluteRootOrPrimPath() const; /// Returns whether the path identifies a root prim. /// /// the path must be absolute and have a single element /// (for example /foo). SDF_API bool IsRootPrimPath() const; /// Returns whether the path identifies a property. /// /// A relational attribute is considered to be a property, so this /// method will return true for relational attributes as well /// as properties of prims. SDF_API bool IsPropertyPath() const; /// Returns whether the path identifies a prim's property. /// /// A relational attribute is not a prim property. SDF_API bool IsPrimPropertyPath() const; /// Returns whether the path identifies a namespaced property. /// /// A namespaced property has colon embedded in its name. SDF_API bool IsNamespacedPropertyPath() const; /// Returns whether the path identifies a variant selection for a /// prim. SDF_API bool IsPrimVariantSelectionPath() const; /// Return true if this path is a prim path or is a prim variant /// selection path. SDF_API bool IsPrimOrPrimVariantSelectionPath() const; /// Returns whether the path or any of its parent paths identifies /// a variant selection for a prim. SDF_API bool ContainsPrimVariantSelection() const; /// Return true if this path contains any property elements, false /// otherwise. A false return indicates a prim-like path, specifically a /// root path, a prim path, or a prim variant selection path. A true return /// indicates a property-like path: a prim property path, a target path, a /// relational attribute path, etc. bool ContainsPropertyElements() const { return static_cast(_propPart); } /// Return true if this path is or has a prefix that's a target path or a /// mapper path. SDF_API bool ContainsTargetPath() const; /// Returns whether the path identifies a relational attribute. /// /// If this is true, IsPropertyPath() will also be true. SDF_API bool IsRelationalAttributePath() const; /// Returns whether the path identifies a relationship or /// connection target. SDF_API bool IsTargetPath() const; /// Returns whether the path identifies a connection mapper. SDF_API bool IsMapperPath() const; /// Returns whether the path identifies a connection mapper arg. SDF_API bool IsMapperArgPath() const; /// Returns whether the path identifies a connection expression. SDF_API bool IsExpressionPath() const; /// Returns true if this is the empty path (SdfPath::EmptyPath()). inline bool IsEmpty() const noexcept { // No need to check _propPart, because it can only be non-null if // _primPart is non-null. return !_primPart; } /// Returns the string representation of this path as a TfToken. SDF_API TfToken const &GetToken() const; /// Returns the string representation of this path as a std::string. SDF_API const std::string &GetString() const; /// Returns the string representation of this path as a c string. SDF_API const char *GetText() const; /// Returns the prefix paths of this path. /// /// Prefixes are returned in order of shortest to longest. The path /// itself is returned as the last prefix. /// Note that if the prefix order does not need to be from shortest to /// longest, it is more efficient to use GetAncestorsRange, which /// produces an equivalent set of paths, ordered from longest to shortest. SDF_API SdfPathVector GetPrefixes() const; /// Fills prefixes with prefixes of this path. /// /// This avoids copy constructing the return value. /// /// Prefixes are returned in order of shortest to longest. The path /// itself is returned as the last prefix. /// Note that if the prefix order does not need to be from shortest to /// longest, it is more efficient to use GetAncestorsRange, which /// produces an equivalent set of paths, ordered from longest to shortest. SDF_API void GetPrefixes(SdfPathVector *prefixes) const; /// Return a range for iterating over the ancestors of this path. /// /// The range provides iteration over the prefixes of a path, ordered /// from longest to shortest (the opposite of the order of the prefixes /// returned by GetPrefixes). SDF_API SdfPathAncestorsRange GetAncestorsRange() const; /// Returns the name of the prim, property or relational /// attribute identified by the path. /// /// Returns EmptyPath if this path is a target or mapper path. /// ///
    ///
  • Returns "" for EmptyPath.
  • ///
  • Returns "." for ReflexiveRelativePath.
  • ///
  • Returns ".." for a path ending in ParentPathElement.
  • ///
SDF_API const std::string &GetName() const; /// Returns the name of the prim, property or relational /// attribute identified by the path, as a token. SDF_API const TfToken &GetNameToken() const; /// Returns an ascii representation of the "terminal" element /// of this path, which can be used to reconstruct the path using /// \c AppendElementString() on its parent. /// /// EmptyPath(), AbsoluteRootPath(), and ReflexiveRelativePath() are /// \em not considered elements (one of the defining properties of /// elements is that they have a parent), so \c GetElementString() will /// return the empty string for these paths. /// /// Unlike \c GetName() and \c GetTargetPath(), which provide you "some" /// information about the terminal element, this provides a complete /// representation of the element, for all element types. /// /// Also note that whereas \c GetName(), \c GetNameToken(), \c GetText(), /// \c GetString(), and \c GetTargetPath() return cached results, /// \c GetElementString() always performs some amount of string /// manipulation, which you should keep in mind if performance is a concern. SDF_API std::string GetElementString() const; /// Like GetElementString() but return the value as a TfToken. SDF_API TfToken GetElementToken() const; /// Return a copy of this path with its final component changed to /// \a newName. This path must be a prim or property path. /// /// This method is shorthand for path.GetParentPath().AppendChild(newName) /// for prim paths, path.GetParentPath().AppendProperty(newName) for /// prim property paths, and /// path.GetParentPath().AppendRelationalAttribute(newName) for relational /// attribute paths. /// /// Note that only the final path component is ever changed. If the name of /// the final path component appears elsewhere in the path, it will not be /// modified. /// /// Some examples: /// /// ReplaceName('/chars/MeridaGroup', 'AngusGroup') -> '/chars/AngusGroup' /// ReplaceName('/Merida.tx', 'ty') -> '/Merida.ty' /// ReplaceName('/Merida.tx[targ].tx', 'ty') -> '/Merida.tx[targ].ty' /// SDF_API SdfPath ReplaceName(TfToken const &newName) const; /// Returns the relational attribute or mapper target path /// for this path. /// /// Returns EmptyPath if this is not a target, relational attribute or /// mapper path. /// /// Note that it is possible for a path to have multiple "target" paths. /// For example a path that identifies a connection target for a /// relational attribute includes the target of the connection as well /// as the target of the relational attribute. In these cases, the /// "deepest" or right-most target path will be returned (the connection /// target in this example). SDF_API const SdfPath &GetTargetPath() const; /// Returns all the relationship target or connection target /// paths contained in this path, and recursively all the target paths /// contained in those target paths in reverse depth-first order. /// /// For example, given the path: '/A/B.a[/C/D.a[/E/F.a]].a[/A/B.a[/C/D.a]]' /// this method produces: '/A/B.a[/C/D.a]', '/C/D.a', '/C/D.a[/E/F.a]', /// '/E/F.a' SDF_API void GetAllTargetPathsRecursively(SdfPathVector *result) const; /// Returns the variant selection for this path, if this is a variant /// selection path. /// Returns a pair of empty strings if this path is not a variant /// selection path. SDF_API std::pair GetVariantSelection() const; /// Return true if both this path and \a prefix are not the empty /// path and this path has \a prefix as a prefix. Return false otherwise. SDF_API bool HasPrefix( const SdfPath &prefix ) const; /// @} /// \name Creating new paths by modifying existing paths /// @{ /// Return the path that identifies this path's namespace parent. /// /// For a prim path (like '/foo/bar'), return the prim's parent's path /// ('/foo'). For a prim property path (like '/foo/bar.property'), return /// the prim's path ('/foo/bar'). For a target path (like /// '/foo/bar.property[/target]') return the property path /// ('/foo/bar.property'). For a relational attribute or mapper path (like /// '/foo/bar.property[/target].relAttr') return the relationship target's /// path ('/foo/bar.property[/target]'). For a prim variant selection path /// (like '/foo/bar{var=sel}') return the prim path ('/foo/bar'). For a /// root prim path (like '/rootPrim'), return AbsoluteRootPath() ('/'). For /// a single element relative prim path (like 'relativePrim'), return /// ReflexiveRelativePath() ('.'). For ReflexiveRelativePath(), return the /// relative parent path ('..'). /// /// Note that the parent path of a relative parent path ('..') is a relative /// grandparent path ('../..'). Use caution writing loops that walk to /// parent paths since relative paths have infinitely many ancestors. To /// more safely traverse ancestor paths, consider iterating over an /// SdfPathAncestorsRange instead, as returend by GetAncestorsRange(). SDF_API SdfPath GetParentPath() const; /// Creates a path by stripping all relational attributes, targets, /// properties, and variant selections from the leafmost prim path, leaving /// the nearest path for which \a IsPrimPath() returns true. /// /// See \a GetPrimOrPrimVariantSelectionPath also. /// /// If the path is already a prim path, the same path is returned. SDF_API SdfPath GetPrimPath() const; /// Creates a path by stripping all relational attributes, targets, /// and properties, leaving the nearest path for which /// \a IsPrimOrPrimVariantSelectionPath() returns true. /// /// See \a GetPrimPath also. /// /// If the path is already a prim or a prim variant selection path, the same /// path is returned. SDF_API SdfPath GetPrimOrPrimVariantSelectionPath() const; /// Creates a path by stripping all properties and relational /// attributes from this path, leaving the path to the containing prim. /// /// If the path is already a prim or absolute root path, the same /// path is returned. SDF_API SdfPath GetAbsoluteRootOrPrimPath() const; /// Create a path by stripping all variant selections from all /// components of this path, leaving a path with no embedded variant /// selections. SDF_API SdfPath StripAllVariantSelections() const; /// Creates a path by appending a given relative path to this path. /// /// If the newSuffix is a prim path, then this path must be a prim path /// or a root path. /// /// If the newSuffix is a prim property path, then this path must be /// a prim path or the ReflexiveRelativePath. SDF_API SdfPath AppendPath(const SdfPath &newSuffix) const; /// Creates a path by appending an element for \p childName /// to this path. /// /// This path must be a prim path, the AbsoluteRootPath /// or the ReflexiveRelativePath. SDF_API SdfPath AppendChild(TfToken const &childName) const; /// Creates a path by appending an element for \p propName /// to this path. /// /// This path must be a prim path or the ReflexiveRelativePath. SDF_API SdfPath AppendProperty(TfToken const &propName) const; /// Creates a path by appending an element for \p variantSet /// and \p variant to this path. /// /// This path must be a prim path. SDF_API SdfPath AppendVariantSelection(const std::string &variantSet, const std::string &variant) const; /// Creates a path by appending an element for /// \p targetPath. /// /// This path must be a prim property or relational attribute path. SDF_API SdfPath AppendTarget(const SdfPath &targetPath) const; /// Creates a path by appending an element for /// \p attrName to this path. /// /// This path must be a target path. SDF_API SdfPath AppendRelationalAttribute(TfToken const &attrName) const; /// Replaces the relational attribute's target path /// /// The path must be a relational attribute path. SDF_API SdfPath ReplaceTargetPath( const SdfPath &newTargetPath ) const; /// Creates a path by appending a mapper element for /// \p targetPath. /// /// This path must be a prim property or relational attribute path. SDF_API SdfPath AppendMapper(const SdfPath &targetPath) const; /// Creates a path by appending an element for /// \p argName. /// /// This path must be a mapper path. SDF_API SdfPath AppendMapperArg(TfToken const &argName) const; /// Creates a path by appending an expression element. /// /// This path must be a prim property or relational attribute path. SDF_API SdfPath AppendExpression() const; /// Creates a path by extracting and appending an element /// from the given ascii element encoding. /// /// Attempting to append a root or empty path (or malformed path) /// or attempting to append \em to the EmptyPath will raise an /// error and return the EmptyPath. /// /// May also fail and return EmptyPath if this path's type cannot /// possess a child of the type encoded in \p element. SDF_API SdfPath AppendElementString(const std::string &element) const; /// Like AppendElementString() but take the element as a TfToken. SDF_API SdfPath AppendElementToken(const TfToken &elementTok) const; /// Returns a path with all occurrences of the prefix path /// \p oldPrefix replaced with the prefix path \p newPrefix. /// /// If fixTargetPaths is true, any embedded target paths will also /// have their paths replaced. This is the default. /// /// If this is not a target, relational attribute or mapper path this /// will do zero or one path prefix replacements, if not the number of /// replacements can be greater than one. SDF_API SdfPath ReplacePrefix(const SdfPath &oldPrefix, const SdfPath &newPrefix, bool fixTargetPaths=true) const; /// Returns a path with maximal length that is a prefix path of /// both this path and \p path. SDF_API SdfPath GetCommonPrefix(const SdfPath &path) const; /// Find and remove the longest common suffix from two paths. /// /// Returns this path and \p otherPath with the longest common suffix /// removed (first and second, respectively). If the two paths have no /// common suffix then the paths are returned as-is. If the paths are /// equal then this returns empty paths for relative paths and absolute /// roots for absolute paths. The paths need not be the same length. /// /// If \p stopAtRootPrim is \c true then neither returned path will be /// the root path. That, in turn, means that some common suffixes will /// not be removed. For example, if \p stopAtRootPrim is \c true then /// the paths /A/B and /B will be returned as is. Were it \c false /// then the result would be /A and /. Similarly paths /A/B/C and /// /B/C would return /A/B and /B if \p stopAtRootPrim is \c true but /// /A and / if it's \c false. SDF_API std::pair RemoveCommonSuffix(const SdfPath& otherPath, bool stopAtRootPrim = false) const; /// Returns the absolute form of this path using \p anchor /// as the relative basis. /// /// \p anchor must be an absolute prim path. /// /// If this path is a relative path, resolve it using \p anchor as the /// relative basis. /// /// If this path is already an absolute path, just return a copy. SDF_API SdfPath MakeAbsolutePath(const SdfPath & anchor) const; /// Returns the relative form of this path using \p anchor /// as the relative basis. /// /// \p anchor must be an absolute prim path. /// /// If this path is an absolute path, return the corresponding relative path /// that is relative to the absolute path given by \p anchor. /// /// If this path is a relative path, return the optimal relative /// path to the absolute path given by \p anchor. (The optimal /// relative path from a given prim path is the relative path /// with the least leading dot-dots. SDF_API SdfPath MakeRelativePath(const SdfPath & anchor) const; /// @} /// \name Valid path strings, prim and property names /// @{ /// Returns whether \p name is a legal identifier for any /// path component. SDF_API static bool IsValidIdentifier(const std::string &name); /// Returns whether \p name is a legal namespaced identifier. /// This returns \c true if IsValidIdentifier() does. SDF_API static bool IsValidNamespacedIdentifier(const std::string &name); /// Tokenizes \p name by the namespace delimiter. /// Returns the empty vector if \p name is not a valid namespaced /// identifier. SDF_API static std::vector TokenizeIdentifier(const std::string &name); /// Tokenizes \p name by the namespace delimiter. /// Returns the empty vector if \p name is not a valid namespaced /// identifier. SDF_API static TfTokenVector TokenizeIdentifierAsTokens(const std::string &name); /// Join \p names into a single identifier using the namespace delimiter. /// Any empty strings present in \p names are ignored when joining. SDF_API static std::string JoinIdentifier(const std::vector &names); /// Join \p names into a single identifier using the namespace delimiter. /// Any empty strings present in \p names are ignored when joining. SDF_API static std::string JoinIdentifier(const TfTokenVector& names); /// Join \p lhs and \p rhs into a single identifier using the /// namespace delimiter. /// Returns \p lhs if \p rhs is empty and vice verse. /// Returns an empty string if both \p lhs and \p rhs are empty. SDF_API static std::string JoinIdentifier(const std::string &lhs, const std::string &rhs); /// Join \p lhs and \p rhs into a single identifier using the /// namespace delimiter. /// Returns \p lhs if \p rhs is empty and vice verse. /// Returns an empty string if both \p lhs and \p rhs are empty. SDF_API static std::string JoinIdentifier(const TfToken &lhs, const TfToken &rhs); /// Returns \p name stripped of any namespaces. /// This does not check the validity of the name; it just attempts /// to remove anything that looks like a namespace. SDF_API static std::string StripNamespace(const std::string &name); /// Returns \p name stripped of any namespaces. /// This does not check the validity of the name; it just attempts /// to remove anything that looks like a namespace. SDF_API static TfToken StripNamespace(const TfToken &name); /// Returns (\p name, \c true) where \p name is stripped of the prefix /// specified by \p matchNamespace if \p name indeed starts with /// \p matchNamespace. Returns (\p name, \c false) otherwise, with \p name /// unmodified. /// /// This function deals with both the case where \p matchNamespace contains /// the trailing namespace delimiter ':' or not. /// SDF_API static std::pair StripPrefixNamespace(const std::string &name, const std::string &matchNamespace); /// Return true if \p pathString is a valid path string, meaning that /// passing the string to the \a SdfPath constructor will result in a valid, /// non-empty SdfPath. Otherwise, return false and if \p errMsg is not NULL, /// set the pointed-to string to the parse error. SDF_API static bool IsValidPathString(const std::string &pathString, std::string *errMsg = 0); /// @} /// \name Operators /// @{ /// Equality operator. /// (Boost provides inequality from this.) inline bool operator==(const SdfPath &rhs) const { return _AsInt() == rhs._AsInt(); } /// Comparison operator. /// /// This orders paths lexicographically, aka dictionary-style. /// inline bool operator<(const SdfPath &rhs) const { if (_AsInt() == rhs._AsInt()) { return false; } if (!_primPart || !rhs._primPart) { return !_primPart && rhs._primPart; } // Valid prim parts -- must walk node structure, etc. return _LessThanInternal(*this, rhs); } // For hash maps and sets struct Hash { inline size_t operator()(const SdfPath& path) const { // The hash function is pretty sensitive performance-wise. Be // careful making changes here, and run tests. uint32_t primPart, propPart; memcpy(&primPart, &path._primPart, sizeof(primPart)); memcpy(&propPart, &path._propPart, sizeof(propPart)); // Important considerations here: // - It must be fast to execute. // - It must do well in hash tables that find indexes by taking // the remainder divided by a prime number of buckets. // - It must do well in hash tables that find indexes by taking // just the low-order bits. // This hash function maps the (primPart, propPart) pair to a single // value by using triangular numbers. So the first few path hash // values would look like this, for primPart as X increasing // left-to-right and for propPart as Y increasing top-to-bottom. // // 0 2 5 9 14 20 // 1 4 8 13 19 26 // 3 7 12 18 25 33 // 6 11 17 24 32 41 // 10 16 23 31 40 50 // 15 22 30 39 49 60 uint64_t x = primPart >> 8; uint64_t y = x + (propPart >> 8); return x + (y * (y + 1)) / 2; } }; inline size_t GetHash() const { return Hash()(*this); } // For cases where an unspecified total order that is not stable from // run-to-run is needed. struct FastLessThan { inline bool operator()(const SdfPath& a, const SdfPath& b) const { return a._AsInt() < b._AsInt(); } }; /// @} /// \name Utilities /// @{ /// Given some vector of paths, get a vector of concise unambiguous /// relative paths. /// /// GetConciseRelativePaths requires a vector of absolute paths. It /// finds a set of relative paths such that each relative path is /// unique. SDF_API static SdfPathVector GetConciseRelativePaths(const SdfPathVector& paths); /// Remove all elements of \a paths that are prefixed by other /// elements in \a paths. As a side-effect, the result is left in sorted /// order. SDF_API static void RemoveDescendentPaths(SdfPathVector *paths); /// Remove all elements of \a paths that prefix other elements in /// \a paths. As a side-effect, the result is left in sorted order. SDF_API static void RemoveAncestorPaths(SdfPathVector *paths); /// @} private: // This is used for all internal path construction where we do operations // via nodes and then want to return a new path with a resulting prim and // property parts. // Accept rvalues. explicit SdfPath(Sdf_PathPrimNodeHandle &&primNode) : _primPart(std::move(primNode)) {} // Construct from prim & prop parts. SdfPath(Sdf_PathPrimNodeHandle const &primPart, Sdf_PathPropNodeHandle const &propPart) : _primPart(primPart) , _propPart(propPart) {} // Construct from prim & prop node pointers. SdfPath(Sdf_PathNode const *primPart, Sdf_PathNode const *propPart) : _primPart(primPart) , _propPart(propPart) {} friend class Sdf_PathNode; friend class Sdfext_PathAccess; friend class SdfPathAncestorsRange; // converts elements to a string for parsing (unfortunate) static std::string _ElementsToString(bool absolute, const std::vector &elements); SdfPath _ReplacePrimPrefix(SdfPath const &oldPrefix, SdfPath const &newPrefix) const; SdfPath _ReplaceTargetPathPrefixes(SdfPath const &oldPrefix, SdfPath const &newPrefix) const; SdfPath _ReplacePropPrefix(SdfPath const &oldPrefix, SdfPath const &newPrefix, bool fixTargetPaths) const; // Helper to implement the uninlined portion of operator<. SDF_API static bool _LessThanInternal(SdfPath const &lhs, SdfPath const &rhs); inline uint64_t _AsInt() const { static_assert(sizeof(*this) == sizeof(uint64_t), ""); uint64_t ret; std::memcpy(&ret, this, sizeof(*this)); return ret; } friend void swap(SdfPath &lhs, SdfPath &rhs) { lhs._primPart.swap(rhs._primPart); lhs._propPart.swap(rhs._propPart); } Sdf_PathPrimNodeHandle _primPart; Sdf_PathPropNodeHandle _propPart; }; /// \class SdfPathAncestorsRange /// /// Range representing a path and ancestors, and providing methods for /// iterating over them. /// /// An ancestor range represents a path and all of its ancestors ordered from /// nearest to furthest (root-most). /// For example, given a path like `/a/b.prop`, the range represents paths /// `/a/b.prop`, `/a/b` and `/a`, in that order. /// A range accepts relative paths as well: For path `a/b.prop`, the range /// represents paths 'a/b.prop`, `a/b` and `a`. /// If a path contains parent path elements, (`..`), those elements are treated /// as elements of the range. For instance, given path `../a/b`, the range /// represents paths `../a/b`, `../a` and `..`. /// This represents the same of set of `prefix` paths as SdfPath::GetPrefixes, /// but in reverse order. class SdfPathAncestorsRange { public: SdfPathAncestorsRange(const SdfPath& path) : _path(path) {} const SdfPath& GetPath() const { return _path; } struct iterator { using iterator_category = std::forward_iterator_tag; using value_type = SdfPath; using difference_type = std::ptrdiff_t; using reference = const SdfPath&; using pointer = const SdfPath*; iterator(const SdfPath& path) : _path(path) {} iterator() = default; SDF_API iterator& operator++(); const SdfPath& operator*() const { return _path; } const SdfPath* operator->() const { return &_path; } bool operator==(const iterator& o) const { return _path == o._path; } bool operator!=(const iterator& o) const { return _path != o._path; } /// Return the distance between two iterators. /// It is only valid to compute the distance between paths /// that share a common prefix. SDF_API friend difference_type distance(const iterator& first, const iterator& last); private: SdfPath _path; }; iterator begin() const { return iterator(_path); } iterator end() const { return iterator(); } private: SdfPath _path; }; // Overload hash_value for SdfPath. Used by things like boost::hash. inline size_t hash_value(SdfPath const &path) { return path.GetHash(); } /// Writes the string representation of \p path to \p out. SDF_API std::ostream & operator<<( std::ostream &out, const SdfPath &path ); // Helper for SdfPathFindPrefixedRange & SdfPathFindLongestPrefix. A function // object that returns an SdfPath const & unchanged. struct Sdf_PathIdentity { inline SdfPath const &operator()(SdfPath const &arg) const { return arg; } }; /// Find the subrange of the sorted range [\a begin, \a end) that includes all /// paths prefixed by \a path. The input range must be ordered according to /// SdfPath::operator<. If your range's iterators' value_types are not SdfPath, /// but you can obtain SdfPaths from them (e.g. map::iterator), you /// can pass a function to extract the path from the dereferenced iterator in /// \p getPath. template std::pair SdfPathFindPrefixedRange(ForwardIterator begin, ForwardIterator end, SdfPath const &prefix, GetPathFn const &getPath = GetPathFn()) { using IterRef = typename std::iterator_traits::reference; struct Compare { Compare(GetPathFn const &getPath) : _getPath(getPath) {} GetPathFn const &_getPath; bool operator()(IterRef a, SdfPath const &b) const { return _getPath(a) < b; } }; std::pair result; // First, use lower_bound to find where \a prefix would go. result.first = std::lower_bound(begin, end, prefix, Compare(getPath)); // Next, find end of range starting from the lower bound, using the // prefixing condition to define the boundary. result.second = TfFindBoundary(result.first, end, [&prefix, &getPath](IterRef iterRef) { return getPath(iterRef).HasPrefix(prefix); }); return result; } template RandomAccessIterator Sdf_PathFindLongestPrefixImpl(RandomAccessIterator begin, RandomAccessIterator end, SdfPath const &path, bool strictPrefix, GetPathFn const &getPath) { using IterRef = typename std::iterator_traits::reference; struct Compare { Compare(GetPathFn const &getPath) : _getPath(getPath) {} GetPathFn const &_getPath; bool operator()(IterRef a, SdfPath const &b) const { return _getPath(a) < b; } }; // Search for the path in [begin, end). If present, return it. If not, // examine prior element in [begin, end). If none, return end. Else, is it // a prefix of path? If so, return it. Else find common prefix of that // element and path and recurse. // If empty sequence, return. if (begin == end) return end; Compare comp(getPath); // Search for where this path would lexicographically appear in the range. RandomAccessIterator result = std::lower_bound(begin, end, path, comp); // If we didn't get the end, check to see if we got the path exactly if // we're not looking for a strict prefix. if (!strictPrefix && result != end && getPath(*result) == path) { return result; } // If we got begin (and didn't match in the case of a non-strict prefix) // then there's no prefix. if (result == begin) { return end; } // If the prior element is a prefix, we're done. if (path.HasPrefix(getPath(*--result))) { return result; } // Otherwise, find the common prefix of the lexicographical predecessor and // look for its prefix in the preceding range. SdfPath newPath = path.GetCommonPrefix(getPath(*result)); auto origEnd = end; do { end = result; result = std::lower_bound(begin, end, newPath, comp); if (result != end && getPath(*result) == newPath) { return result; } if (result == begin) { return origEnd; } if (newPath.HasPrefix(getPath(*--result))) { return result; } newPath = newPath.GetCommonPrefix(getPath(*result)); } while (true); } /// Return an iterator to the element of [\a begin, \a end) that is the longest /// prefix of the given path (including the path itself), if there is such an /// element, otherwise \a end. The input range must be ordered according to /// SdfPath::operator<. If your range's iterators' value_types are not SdfPath, /// but you can obtain SdfPaths from them (e.g. vector>::iterator), you can pass a function to extract the path from the /// dereferenced iterator in \p getPath. template ::iterator_category >::value >::type > RandomAccessIterator SdfPathFindLongestPrefix(RandomAccessIterator begin, RandomAccessIterator end, SdfPath const &path, GetPathFn const &getPath = GetPathFn()) { return Sdf_PathFindLongestPrefixImpl( begin, end, path, /*strictPrefix=*/false, getPath); } /// Return an iterator to the element of [\a begin, \a end) that is the longest /// prefix of the given path (excluding the path itself), if there is such an /// element, otherwise \a end. The input range must be ordered according to /// SdfPath::operator<. If your range's iterators' value_types are not SdfPath, /// but you can obtain SdfPaths from them (e.g. vector>::iterator), you can pass a function to extract the path from the /// dereferenced iterator in \p getPath. template ::iterator_category >::value >::type > RandomAccessIterator SdfPathFindLongestStrictPrefix(RandomAccessIterator begin, RandomAccessIterator end, SdfPath const &path, GetPathFn const &getPath = GetPathFn()) { return Sdf_PathFindLongestPrefixImpl( begin, end, path, /*strictPrefix=*/true, getPath); } template Iter Sdf_PathFindLongestPrefixImpl( MapParam map, SdfPath const &path, bool strictPrefix, GetPathFn const &getPath = GetPathFn()) { // Search for the path in map. If present, return it. If not, examine // prior element in map. If none, return end. Else, is it a prefix of // path? If so, return it. Else find common prefix of that element and // path and recurse. const Iter mapEnd = map.end(); // If empty, return. if (map.empty()) return mapEnd; // Search for where this path would lexicographically appear in the range. Iter result = map.lower_bound(path); // If we didn't get the end, check to see if we got the path exactly if // we're not looking for a strict prefix. if (!strictPrefix && result != mapEnd && getPath(*result) == path) return result; // If we got begin (and didn't match in the case of a non-strict prefix) // then there's no prefix. if (result == map.begin()) return mapEnd; // If the prior element is a prefix, we're done. if (path.HasPrefix(getPath(*--result))) return result; // Otherwise, find the common prefix of the lexicographical predecessor and // recurse looking for it or its longest prefix in the preceding range. We // always pass strictPrefix=false, since now we're operating on prefixes of // the original caller's path. return Sdf_PathFindLongestPrefixImpl( map, path.GetCommonPrefix(getPath(*result)), /*strictPrefix=*/false, getPath); } /// Return an iterator pointing to the element of \a set whose key is the /// longest prefix of the given path (including the path itself). If there is /// no such element, return \a set.end(). SDF_API typename std::set::const_iterator SdfPathFindLongestPrefix(std::set const &set, SdfPath const &path); /// Return an iterator pointing to the element of \a map whose key is the /// longest prefix of the given path (including the path itself). If there is /// no such element, return \a map.end(). template typename std::map::const_iterator SdfPathFindLongestPrefix(std::map const &map, SdfPath const &path) { return Sdf_PathFindLongestPrefixImpl< typename std::map::const_iterator, std::map const &>(map, path, /*strictPrefix=*/false, TfGet<0>()); } template typename std::map::iterator SdfPathFindLongestPrefix(std::map &map, SdfPath const &path) { return Sdf_PathFindLongestPrefixImpl< typename std::map::iterator, std::map &>(map, path, /*strictPrefix=*/false, TfGet<0>()); } /// Return an iterator pointing to the element of \a set whose key is the /// longest prefix of the given path (excluding the path itself). If there is /// no such element, return \a set.end(). SDF_API typename std::set::const_iterator SdfPathFindLongestStrictPrefix(std::set const &set, SdfPath const &path); /// Return an iterator pointing to the element of \a map whose key is the /// longest prefix of the given path (excluding the path itself). If there is /// no such element, return \a map.end(). template typename std::map::const_iterator SdfPathFindLongestStrictPrefix( std::map const &map, SdfPath const &path) { return Sdf_PathFindLongestPrefixImpl< typename std::map::const_iterator, std::map const &>(map, path, /*strictPrefix=*/true, TfGet<0>()); } template typename std::map::iterator SdfPathFindLongestStrictPrefix( std::map &map, SdfPath const &path) { return Sdf_PathFindLongestPrefixImpl< typename std::map::iterator, std::map &>(map, path, /*strictPrefix=*/true, TfGet<0>()); } PXR_NAMESPACE_CLOSE_SCOPE // Sdf_PathNode is not public API, but we need to include it here // so we can inline the ref-counting operations, which must manipulate // its internal _refCount member. #include "pxr/usd/sdf/pathNode.h" PXR_NAMESPACE_OPEN_SCOPE static_assert(Sdf_SizeofPrimPathNode == sizeof(Sdf_PrimPathNode), ""); static_assert(Sdf_SizeofPropPathNode == sizeof(Sdf_PrimPropertyPathNode), ""); PXR_NAMESPACE_CLOSE_SCOPE #endif // PXR_USD_SDF_PATH_H