1 // Copyright Contributors to the OpenVDB Project
2 // SPDX-License-Identifier: MPL-2.0
3 
4 #ifndef OPENVDB_METADATA_METAMAP_HAS_BEEN_INCLUDED
5 #define OPENVDB_METADATA_METAMAP_HAS_BEEN_INCLUDED
6 
7 #include "Metadata.h"
8 #include "Types.h"
9 #include "Exceptions.h"
10 #include <iosfwd>
11 #include <map>
12 
13 
14 namespace openvdb {
15 OPENVDB_USE_VERSION_NAMESPACE
16 namespace OPENVDB_VERSION_NAME {
17 
18 /// Container that maps names (strings) to values of arbitrary types
19 class OPENVDB_API MetaMap
20 {
21 public:
22     using Ptr = SharedPtr<MetaMap>;
23     using ConstPtr = SharedPtr<const MetaMap>;
24 
25     using MetadataMap = std::map<Name, Metadata::Ptr>;
26     using MetaIterator = MetadataMap::iterator;
27     using ConstMetaIterator = MetadataMap::const_iterator;
28         ///< @todo this should really iterate over a map of Metadata::ConstPtrs
29 
MetaMap()30     MetaMap() {}
31     MetaMap(const MetaMap& other);
~MetaMap()32     virtual ~MetaMap() {}
33 
34     /// Return a copy of this map whose fields are shared with this map.
35     MetaMap::Ptr copyMeta() const;
36     /// Return a deep copy of this map that shares no data with this map.
37     MetaMap::Ptr deepCopyMeta() const;
38 
39     /// Assign a deep copy of another map to this map.
40     MetaMap& operator=(const MetaMap&);
41 
42     /// Unserialize metadata from the given stream.
43     void readMeta(std::istream&);
44     /// Serialize metadata to the given stream.
45     void writeMeta(std::ostream&) const;
46 
47     /// @brief Insert a new metadata field or overwrite the value of an existing field.
48     /// @details If a field with the given name doesn't already exist, add a new field.
49     /// Otherwise, if the new value's type is the same as the existing field's value type,
50     /// overwrite the existing value with new value.
51     /// @throw TypeError if a field with the given name already exists, but its value type
52     /// is not the same as the new value's
53     /// @throw ValueError if the given field name is empty.
54     void insertMeta(const Name&, const Metadata& value);
55     /// @brief Deep copy all of the metadata fields from the given map into this map.
56     /// @throw TypeError if any field in the given map has the same name as
57     /// but a different value type than one of this map's fields.
58     void insertMeta(const MetaMap&);
59 
60     /// Remove the given metadata field if it exists.
61     void removeMeta(const Name&);
62 
63     //@{
64     /// @brief Return a pointer to the metadata with the given name.
65     /// If no such field exists, return a null pointer.
66     Metadata::Ptr operator[](const Name&);
67     Metadata::ConstPtr operator[](const Name&) const;
68     //@}
69 
70     //@{
71     /// @brief Return a pointer to a TypedMetadata object of type @c T and with the given name.
72     /// If no such field exists or if there is a type mismatch, return a null pointer.
73     template<typename T> typename T::Ptr getMetadata(const Name&);
74     template<typename T> typename T::ConstPtr getMetadata(const Name&) const;
75     //@}
76 
77     /// @brief Return a reference to the value of type @c T stored in the given metadata field.
78     /// @throw LookupError if no field with the given name exists.
79     /// @throw TypeError if the given field is not of type @c T.
80     template<typename T> T& metaValue(const Name&);
81     template<typename T> const T& metaValue(const Name&) const;
82 
83     // Functions for iterating over the metadata
beginMeta()84     MetaIterator beginMeta() { return mMeta.begin(); }
endMeta()85     MetaIterator endMeta() { return mMeta.end(); }
beginMeta()86     ConstMetaIterator beginMeta() const { return mMeta.begin(); }
endMeta()87     ConstMetaIterator endMeta() const { return mMeta.end(); }
88 
clearMetadata()89     void clearMetadata() { mMeta.clear(); }
90 
metaCount()91     size_t metaCount() const { return mMeta.size(); }
92 
93     /// Return a string describing this metadata map.  Prefix each line with @a indent.
94     std::string str(const std::string& indent = "") const;
95 
96     /// Return @c true if the given map is equivalent to this map.
97     bool operator==(const MetaMap& other) const;
98     /// Return @c true if the given map is different from this map.
99     bool operator!=(const MetaMap& other) const { return !(*this == other); }
100 
101 private:
102     /// @brief Return a pointer to TypedMetadata with the given template parameter.
103     /// @throw LookupError if no field with the given name is found.
104     /// @throw TypeError if the given field is not of type T.
105     template<typename T>
106     typename TypedMetadata<T>::Ptr getValidTypedMetadata(const Name&) const;
107 
108     MetadataMap mMeta;
109 };
110 
111 /// Write a MetaMap to an output stream
112 std::ostream& operator<<(std::ostream&, const MetaMap&);
113 
114 
115 ////////////////////////////////////////
116 
117 
118 inline Metadata::Ptr
119 MetaMap::operator[](const Name& name)
120 {
121     MetaIterator iter = mMeta.find(name);
122     return (iter == mMeta.end() ? Metadata::Ptr() : iter->second);
123 }
124 
125 inline Metadata::ConstPtr
126 MetaMap::operator[](const Name &name) const
127 {
128     ConstMetaIterator iter = mMeta.find(name);
129     return (iter == mMeta.end() ? Metadata::Ptr() : iter->second);
130 }
131 
132 
133 ////////////////////////////////////////
134 
135 
136 template<typename T>
137 inline typename T::Ptr
getMetadata(const Name & name)138 MetaMap::getMetadata(const Name &name)
139 {
140     ConstMetaIterator iter = mMeta.find(name);
141     if (iter == mMeta.end()) return typename T::Ptr{};
142 
143     // To ensure that we get valid conversion if the metadata pointers cross dso
144     // boundaries, we have to check the qualified typename and then do a static
145     // cast. This is slower than doing a dynamic_pointer_cast, but is safer when
146     // pointers cross dso boundaries.
147     if (iter->second->typeName() == T::staticTypeName()) {
148         return StaticPtrCast<T, Metadata>(iter->second);
149     } // else
150     return typename T::Ptr{};
151 }
152 
153 template<typename T>
154 inline typename T::ConstPtr
getMetadata(const Name & name)155 MetaMap::getMetadata(const Name &name) const
156 {
157     ConstMetaIterator iter = mMeta.find(name);
158     if (iter == mMeta.end()) return typename T::ConstPtr{};
159 
160     // To ensure that we get valid conversion if the metadata pointers cross dso
161     // boundaries, we have to check the qualified typename and then do a static
162     // cast. This is slower than doing a dynamic_pointer_cast, but is safer when
163     // pointers cross dso boundaries.
164     if (iter->second->typeName() == T::staticTypeName()) {
165         return StaticPtrCast<const T, const Metadata>(iter->second);
166     } // else
167     return typename T::ConstPtr{};
168 }
169 
170 
171 ////////////////////////////////////////
172 
173 
174 template<typename T>
175 inline typename TypedMetadata<T>::Ptr
getValidTypedMetadata(const Name & name)176 MetaMap::getValidTypedMetadata(const Name &name) const
177 {
178     ConstMetaIterator iter = mMeta.find(name);
179     if (iter == mMeta.end()) OPENVDB_THROW(LookupError, "Cannot find metadata " << name);
180 
181     // To ensure that we get valid conversion if the metadata pointers cross dso
182     // boundaries, we have to check the qualified typename and then do a static
183     // cast. This is slower than doing a dynamic_pointer_cast, but is safer when
184     // pointers cross dso boundaries.
185     typename TypedMetadata<T>::Ptr m;
186     if (iter->second->typeName() == TypedMetadata<T>::staticTypeName()) {
187         m = StaticPtrCast<TypedMetadata<T>, Metadata>(iter->second);
188     }
189     if (!m) OPENVDB_THROW(TypeError, "Invalid type for metadata " << name);
190     return m;
191 }
192 
193 
194 ////////////////////////////////////////
195 
196 
197 template<typename T>
198 inline T&
metaValue(const Name & name)199 MetaMap::metaValue(const Name &name)
200 {
201     typename TypedMetadata<T>::Ptr m = getValidTypedMetadata<T>(name);
202     return m->value();
203 }
204 
205 
206 template<typename T>
207 inline const T&
metaValue(const Name & name)208 MetaMap::metaValue(const Name &name) const
209 {
210     typename TypedMetadata<T>::Ptr m = getValidTypedMetadata<T>(name);
211     return m->value();
212 }
213 
214 } // namespace OPENVDB_VERSION_NAME
215 } // namespace openvdb
216 
217 #endif // OPENVDB_METADATA_METAMAP_HAS_BEEN_INCLUDED
218