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