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
25 #include "pxr/pxr.h"
26 #include "pxr/base/tf/enum.h"
27
28 #include "pxr/base/tf/diagnostic.h"
29 #include "pxr/base/tf/errorMark.h"
30 #include "pxr/base/tf/hash.h"
31 #include "pxr/base/tf/instantiateSingleton.h"
32 #include "pxr/base/tf/mallocTag.h"
33 #include "pxr/base/tf/registryManager.h"
34 #include "pxr/base/tf/safeTypeCompare.h"
35 #include "pxr/base/tf/stringUtils.h"
36 #include "pxr/base/tf/type.h"
37
38 #include "pxr/base/arch/demangle.h"
39
40 #include <boost/noncopyable.hpp>
41 #include "pxr/base/tf/hashmap.h"
42
43 #include <tbb/spin_mutex.h>
44
45 #include <iostream>
46 #include <set>
47
48 using std::string;
49 using std::vector;
50 using std::type_info;
51
52 PXR_NAMESPACE_OPEN_SCOPE
53
TF_REGISTRY_FUNCTION(TfType)54 TF_REGISTRY_FUNCTION(TfType)
55 {
56 TfType::Define<TfEnum>();
57 }
58
59 // Convenience typedefs for value/name tables.
60 typedef TfHashMap<TfEnum, string, TfHash> _EnumToNameTableType;
61 typedef TfHashMap<string, TfEnum, TfHash> _NameToEnumTableType;
62 typedef TfHashMap<string, vector<string>, TfHash> _TypeNameToNameVectorTableType;
63 typedef TfHashMap<string, const type_info *, TfHash> _TypeNameToTypeTableType;
64
65 class Tf_EnumRegistry : boost::noncopyable {
66 private:
_GetInstance()67 static Tf_EnumRegistry& _GetInstance() {
68 return TfSingleton<Tf_EnumRegistry>::GetInstance();
69 }
70
Tf_EnumRegistry()71 Tf_EnumRegistry() {
72 TfSingleton<Tf_EnumRegistry>::SetInstanceConstructed(*this);
73 TfRegistryManager::GetInstance().SubscribeTo<TfEnum>();
74 }
75
~Tf_EnumRegistry()76 ~Tf_EnumRegistry() {
77 TfRegistryManager::GetInstance().UnsubscribeFrom<TfEnum>();
78 }
79
_Remove(TfEnum val)80 void _Remove(TfEnum val) {
81 tbb::spin_mutex::scoped_lock lock(_tableLock);
82
83 _typeNameToType.erase(ArchGetDemangled(val.GetType()));
84
85 vector<string>& v = _typeNameToNameVector[val.GetType().name()];
86 vector<string> original = v;
87 string name = _enumToName[val];
88
89 v.clear();
90 for (size_t i = 0; i < original.size(); i++)
91 if (original[i] != name)
92 v.push_back(original[i]);
93
94 _fullNameToEnum.erase(_enumToFullName[val]);
95 _enumToFullName.erase(val);
96 _enumToName.erase(val);
97 _enumToDisplayName.erase(val);
98 }
99
100 tbb::spin_mutex _tableLock;
101 _EnumToNameTableType _enumToName;
102 _EnumToNameTableType _enumToFullName;
103 _EnumToNameTableType _enumToDisplayName;
104 _NameToEnumTableType _fullNameToEnum;
105 _TypeNameToNameVectorTableType _typeNameToNameVector;
106 _TypeNameToTypeTableType _typeNameToType;
107
108 friend class TfEnum;
109 friend class TfSingleton<Tf_EnumRegistry>;
110 };
111
112 TF_INSTANTIATE_SINGLETON(Tf_EnumRegistry);
113
114 void
_AddName(TfEnum val,const string & valName,const string & displayName)115 TfEnum::_AddName(TfEnum val, const string &valName, const string &displayName)
116 {
117 TfAutoMallocTag2 tag("Tf", "TfEnum::_AddName");
118 string typeName = ArchGetDemangled(val.GetType());
119
120 /*
121 * In case valName looks like "stuff::VALUE", strip off the leading
122 * prefix.
123 */
124 size_t i = valName.rfind(':');
125 string shortName = (i == string::npos) ? valName : valName.substr(i+1);
126
127 if (shortName.empty())
128 return;
129
130 Tf_EnumRegistry& r = Tf_EnumRegistry::_GetInstance();
131 tbb::spin_mutex::scoped_lock lock(r._tableLock);
132
133 string fullName = typeName + "::" + shortName;
134
135 r._enumToName[val] = shortName;
136 r._enumToFullName[val] = fullName;
137 r._enumToDisplayName[val] = displayName.empty() ? shortName : displayName;
138 r._fullNameToEnum[fullName] = val;
139 r._typeNameToNameVector[val.GetType().name()].push_back(shortName);
140 r._typeNameToType[typeName] = &val.GetType();
141
142 TfRegistryManager::GetInstance().AddFunctionForUnload(
143 [&r, val]() { r._Remove(val); });
144 }
145
146 string
GetName(TfEnum val)147 TfEnum::GetName(TfEnum val)
148 {
149 if (TfSafeTypeCompare(val.GetType(), typeid(int)))
150 return TfIntToString(val.GetValueAsInt());
151
152 Tf_EnumRegistry& r = Tf_EnumRegistry::_GetInstance();
153 tbb::spin_mutex::scoped_lock lock(r._tableLock);
154
155 _EnumToNameTableType::iterator i = r._enumToName.find(val);
156 return (i != r._enumToName.end() ? i->second : "");
157 }
158
159 string
GetFullName(TfEnum val)160 TfEnum::GetFullName(TfEnum val)
161 {
162 if (TfSafeTypeCompare(val.GetType(), typeid(int)))
163 return TfStringPrintf("int::%d", val.GetValueAsInt());
164
165 Tf_EnumRegistry& r = Tf_EnumRegistry::_GetInstance();
166 tbb::spin_mutex::scoped_lock lock(r._tableLock);
167
168 _EnumToNameTableType::iterator i = r._enumToFullName.find(val);
169 return (i != r._enumToFullName.end() ? i->second : "");
170 }
171
172 string
GetDisplayName(TfEnum val)173 TfEnum::GetDisplayName(TfEnum val)
174 {
175 if (TfSafeTypeCompare(val.GetType(), typeid(int)))
176 return TfIntToString(val.GetValueAsInt());
177
178 Tf_EnumRegistry& r = Tf_EnumRegistry::_GetInstance();
179 tbb::spin_mutex::scoped_lock lock(r._tableLock);
180
181 _EnumToNameTableType::iterator i = r._enumToDisplayName.find(val);
182 return (i != r._enumToDisplayName.end() ? i->second : "");
183 }
184
185 vector<string>
GetAllNames(const type_info & ti)186 TfEnum::GetAllNames(const type_info &ti)
187 {
188 if (TfSafeTypeCompare(ti, typeid(int)))
189 return vector<string>();
190
191 Tf_EnumRegistry& r = Tf_EnumRegistry::_GetInstance();
192 tbb::spin_mutex::scoped_lock lock(r._tableLock);
193
194 _TypeNameToNameVectorTableType::iterator i = r._typeNameToNameVector.find(ti.name());
195 return (i != r._typeNameToNameVector.end() ? i->second : vector<string>());
196 }
197
198 const type_info *
GetTypeFromName(const string & typeName)199 TfEnum::GetTypeFromName(const string& typeName)
200 {
201 Tf_EnumRegistry& r = Tf_EnumRegistry::_GetInstance();
202 tbb::spin_mutex::scoped_lock lock(r._tableLock);
203
204 _TypeNameToTypeTableType::iterator i = r._typeNameToType.find(typeName);
205 if (i == r._typeNameToType.end()) return NULL;
206 return i->second;
207 }
208
209 TfEnum
GetValueFromName(const type_info & ti,const string & name,bool * foundIt)210 TfEnum::GetValueFromName(const type_info& ti, const string &name, bool *foundIt)
211 {
212 bool found = false;
213 TfEnum value = GetValueFromFullName(
214 ArchGetDemangled(ti) + "::" + name, &found);
215
216 // Make sure that the found enum is the correct type.
217 found = found && TfSafeTypeCompare(*(value._typeInfo), ti);
218 if (foundIt)
219 *foundIt = found;
220 return found ? value : TfEnum(-1);
221 }
222
223 TfEnum
GetValueFromFullName(const string & fullname,bool * foundIt)224 TfEnum::GetValueFromFullName(const string &fullname, bool *foundIt)
225 {
226 Tf_EnumRegistry& r = Tf_EnumRegistry::_GetInstance();
227 tbb::spin_mutex::scoped_lock lock(r._tableLock);
228
229 _NameToEnumTableType::iterator i = r._fullNameToEnum.find(fullname);
230 if (i != r._fullNameToEnum.end()) {
231 if (foundIt)
232 *foundIt = true;
233 return TfEnum(i->second);
234 }
235 else if (fullname.find("int::") == 0) {
236 if (foundIt)
237 *foundIt = true;
238 return TfEnum(atoi(fullname.c_str() + 5));
239 }
240 else {
241 if (foundIt)
242 *foundIt = false;
243 return TfEnum(-1);
244 }
245 }
246
247 void
_FatalGetValueError(std::type_info const & typeInfo) const248 TfEnum::_FatalGetValueError(std::type_info const& typeInfo) const
249 {
250 TF_FATAL_ERROR("Attempted to get a '%s' from a TfEnum holding "
251 "a '%s'.",
252 ArchGetDemangled(typeInfo).c_str(),
253 _typeInfo->name());
254 }
255
256 bool
IsKnownEnumType(const std::string & typeName)257 TfEnum::IsKnownEnumType(const std::string& typeName)
258 {
259 Tf_EnumRegistry& r = Tf_EnumRegistry::_GetInstance();
260 tbb::spin_mutex::scoped_lock lock(r._tableLock);
261
262 _TypeNameToTypeTableType::iterator i = r._typeNameToType.find(typeName);
263 if (i == r._typeNameToType.end()) return false;
264 return true;
265 }
266
267 std::ostream &
operator <<(std::ostream & out,const TfEnum & e)268 operator<<(std::ostream& out, const TfEnum& e)
269 {
270 return out << TfEnum::GetFullName(e);
271 }
272
273 PXR_NAMESPACE_CLOSE_SCOPE
274