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_BASE_JS_VALUE_H
25 #define PXR_BASE_JS_VALUE_H
26 
27 /// \file js/value.h
28 
29 #include "pxr/pxr.h"
30 #include "pxr/base/js/api.h"
31 #include "pxr/base/js/types.h"
32 
33 #include <algorithm>
34 #include <cstdint>
35 #include <memory>
36 #include <string>
37 #include <type_traits>
38 #include <vector>
39 
40 PXR_NAMESPACE_OPEN_SCOPE
41 
42 // Value API Version
43 // 1 (or undefined) - Initial version.
44 // 2 - Changed Get{Array,Object} to GetJs{Array,Object}.
45 #define JS_VALUE_API_VERSION 2
46 
47 /// \class JsValue
48 ///
49 /// A discriminated union type for JSON values. A JsValue may contain one of
50 /// the following types:
51 ///
52 /// \li JsObject, a dictionary type
53 /// \li JsArray, a vector type
54 /// \li std::string
55 /// \li bool
56 /// \li int64_t
57 /// \li uint64_t
58 /// \li double
59 /// \li null
60 ///
61 class JsValue
62 {
63 public:
64     /// Type held by this JSON value.
65     enum Type {
66         ObjectType,
67         ArrayType,
68         StringType,
69         BoolType,
70         IntType,
71         RealType,
72         NullType
73     };
74 
75     /// Constructs a null value.
76     JS_API JsValue();
77 
78     /// Constructs a value holding the given object.
79     JS_API JsValue(const JsObject& value);
80 
81     /// Constructs a value holding the given object rvalue reference.
82     JS_API JsValue(JsObject&& value);
83 
84     /// Constructs a value holding the given array.
85     JS_API JsValue(const JsArray& value);
86 
87     /// Constructs a value holding the given array rvalue reference.
88     JS_API JsValue(JsArray&& value);
89 
90     /// Constructs a value holding the given char array as a std::string.
91     JS_API explicit JsValue(const char* value);
92 
93     /// Constructs a value holding the given std::string.
94     JS_API explicit JsValue(const std::string& value);
95 
96     /// Constructs a value holding the given std::string rvalue reference.
97     JS_API explicit JsValue(std::string&& value);
98 
99     /// Constructs a value holding a bool.
100     JS_API explicit JsValue(bool value);
101 
102     /// Constructs a value holding a signed integer.
103     JS_API explicit JsValue(int value);
104 
105     /// Constructs a value holding a 64-bit signed integer.
106     JS_API explicit JsValue(int64_t value);
107 
108     /// Constructs a value holding a 64-bit unsigned integer.
109     JS_API explicit JsValue(uint64_t value);
110 
111     /// Constructs a value holding a double.
112     JS_API explicit JsValue(double value);
113 
114     /// Returns the object held by this value. If this value is not holding an
115     /// object, this method raises a coding error and an empty object is
116     /// returned.
117     JS_API const JsObject& GetJsObject() const;
118 
119     /// Returns the array held by this value. If this value is not holding an
120     /// array, this method raises a coding error and an empty array is
121     /// returned.
122     JS_API const JsArray& GetJsArray() const;
123 
124     /// Returns the string held by this value. If this value is not holding a
125     /// string, this method raises a coding error and an empty string is
126     /// returned.
127     JS_API const std::string& GetString() const;
128 
129     /// Returns the bool held by this value. If this value is not holding a
130     /// bool, this method raises a coding error and false is returned.
131     JS_API bool GetBool() const;
132 
133     /// Returns the integer held by this value. If this value is not holding
134     /// an int, this method raises a coding error and zero is returned. If the
135     /// value is holding a 64-bit integer larger than the platform int may
136     /// hold, the value is truncated.
137     JS_API int GetInt() const;
138 
139     /// Returns the 64-bit integer held by this value. If this value is not
140     /// holding a 64-bit integer, this method raises a coding error and zero
141     /// is returned.
142     JS_API int64_t GetInt64() const;
143 
144     /// Returns the 64-bit unsigned integer held by this value. If this value
145     /// is not holding a 64-bit unsigned integer, this method raises a coding
146     /// error and zero is returned.
147     JS_API uint64_t GetUInt64() const;
148 
149     /// Returns the double held by this value. If this value is not holding a
150     /// double, this method raises a coding error and zero is returned.
151     JS_API double GetReal() const;
152 
153     /// Returns the value corresponding to the C++ type specified in the
154     /// template parameter if it is holding such a value. Calling this
155     /// function with C++ type T is equivalent to calling the specific Get
156     /// function above that returns a value or reference to a type T.
157     ///
158     /// If a value corresponding to the C++ type is not being held, this
159     /// method raises a coding error. See Get functions above for default
160     /// value returned in this case.
161     template <typename T,
162               typename ReturnType = typename std::conditional<
163                   std::is_same<T, JsObject>::value ||
164                   std::is_same<T, JsArray>::value ||
165                   std::is_same<T, std::string>::value,
166                   const T&, T>::type>
Get()167     ReturnType Get() const {
168         return _Get(static_cast<T*>(nullptr));
169     }
170 
171     /// Returns a vector holding the elements of this value's array that
172     /// correspond to the C++ type specified as the template parameter.
173     /// If this value is not holding an array, an empty vector is returned.
174     /// If any of the array's elements does not correspond to the C++ type,
175     /// it is replaced with the default value used by the Get functions above.
176     /// In both cases, a coding error will be raised.
177     template <typename T>
178     std::vector<T> GetArrayOf() const;
179 
180     /// Returns the type of this value.
181     JS_API Type GetType() const;
182 
183     /// Returns a display name for the type of this value.
184     JS_API std::string GetTypeName() const;
185 
186     /// Returns true if this value is holding an object type.
187     JS_API bool IsObject() const;
188 
189     /// Returns true if this value is holding an array type.
190     JS_API bool IsArray() const;
191 
192     /// Returns true if this value is holding a string type.
193     JS_API bool IsString() const;
194 
195     /// Returns true if this value is holding a boolean type.
196     JS_API bool IsBool() const;
197 
198     /// Returns true if this value is holding an integer type.
199     JS_API bool IsInt() const;
200 
201     /// Returns true if this value is holding a real type.
202     JS_API bool IsReal() const;
203 
204     /// Returns true if this value is holding a 64-bit unsigned integer.
205     JS_API bool IsUInt64() const;
206 
207     /// Returns true if this value is holding a type that corresponds
208     /// to the C++ type specified as the template parameter.
209     template <typename T>
Is()210     bool Is() const {
211         return _Is(static_cast<T*>(nullptr));
212     }
213 
214     /// Returns true if this value is holding an array whose elements all
215     /// correspond to the C++ type specified as the template parameter.
216     template <typename T>
217     bool IsArrayOf() const;
218 
219     /// Returns true if this value is null, false otherwise.
220     JS_API bool IsNull() const;
221 
222     /// Evaluates to true if this value is not null.
223     JS_API explicit operator bool() const;
224 
225     /// Returns true of both values hold the same type and the underlying held
226     /// values are equal.
227     JS_API bool operator==(const JsValue& other) const;
228 
229     /// Returns true if values are of different type, or the underlying held
230     /// values are not equal.
231     JS_API bool operator!=(const JsValue& other) const;
232 
233 private:
234     template <typename T>
235     struct _InvalidTypeHelper : public std::false_type { };
236 
237     template <class T>
_Get(T *)238     T _Get(T*) const {
239         static_assert(_InvalidTypeHelper<T>::value,
240                       "Invalid type for JsValue");
241         return T();
242     }
243 
_Get(JsObject *)244     const JsObject& _Get(JsObject*) const { return GetJsObject(); }
_Get(JsArray *)245     const JsArray& _Get(JsArray*) const { return GetJsArray(); }
_Get(std::string *)246     const std::string& _Get(std::string*) const { return GetString(); }
_Get(bool *)247     bool _Get(bool*) const { return GetBool(); }
_Get(int *)248     int _Get(int*) const { return GetInt(); }
_Get(int64_t *)249     int64_t _Get(int64_t*) const { return GetInt64(); }
_Get(uint64_t *)250     uint64_t _Get(uint64_t*) const { return GetUInt64(); }
_Get(double *)251     double _Get(double*) const { return GetReal(); }
252 
253     template <class T>
_Is(T *)254     bool _Is(T*) const {
255         static_assert(_InvalidTypeHelper<T>::value,
256                       "Invalid type for JsValue");
257         return false;
258     }
259 
_Is(JsObject *)260     bool _Is(JsObject*) const { return IsObject(); }
_Is(JsArray *)261     bool _Is(JsArray*) const { return IsArray(); }
_Is(std::string *)262     bool _Is(std::string*) const { return IsString(); }
_Is(bool *)263     bool _Is(bool*) const { return IsBool(); }
_Is(int *)264     bool _Is(int*) const { return IsInt(); }
_Is(int64_t *)265     bool _Is(int64_t*) const { return IsInt(); }
_Is(uint64_t *)266     bool _Is(uint64_t*) const { return IsUInt64(); }
_Is(double *)267     bool _Is(double*) const { return IsReal(); }
268 
269     struct _Holder;
270     std::shared_ptr<_Holder> _holder;
271 };
272 
273 template <typename T>
GetArrayOf()274 inline std::vector<T> JsValue::GetArrayOf() const
275 {
276     const JsArray& array = GetJsArray();
277     std::vector<T> result(array.size());
278     std::transform(array.begin(), array.end(), result.begin(),
279                    [](const JsValue& v) { return v.Get<T>(); });
280     return result;
281 }
282 
283 template <typename T>
IsArrayOf()284 inline bool JsValue::IsArrayOf() const
285 {
286     if (!IsArray()) {
287         return false;
288     }
289     const JsArray& array = GetJsArray();
290     return std::all_of(array.begin(), array.end(),
291                        [](const JsValue& v) { return v.Is<T>(); });
292 }
293 
294 PXR_NAMESPACE_CLOSE_SCOPE
295 
296 #endif // PXR_BASE_JS_VALUE_H
297