1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #ifndef SCENE_PLUGIN_JSON_H
17 #define SCENE_PLUGIN_JSON_H
18 
19 #include <core/json/json.h>
20 
21 CORE_BEGIN_NAMESPACE()
22 
23 constexpr int JSON_DEFAULT_INDENTATION = 2;
24 
25 template<typename T>
26 BASE_NS::string to_formatted_string(const CORE_NS::json::value_t<T>& value,
27     const int indentation = JSON_DEFAULT_INDENTATION, const int currentIndentation = 0);
28 
29 #ifdef JSON_IMPL
30 using namespace CORE_NS::json;
31 
32 template<typename T>
append(BASE_NS::string & out,const typename value_t<T>::string & string)33 void append(BASE_NS::string& out, const typename value_t<T>::string& string)
34 {
35     out += '"';
36     out.append(escape(string));
37     out += '"';
38 }
39 
40 template<typename T>
append(BASE_NS::string & out,const typename value_t<T>::object & object,const int indentation,int currentIndentation)41 void append(
42     BASE_NS::string& out, const typename value_t<T>::object& object, const int indentation, int currentIndentation)
43 {
44     if (object.empty()) {
45         // Keep empty objects on one row.
46         out.append("{}");
47         return;
48     }
49 
50     out += "{\n";
51     currentIndentation += indentation;
52     out.append(currentIndentation, ' ');
53 
54     int count = 0;
55     for (const auto& v : object) {
56         if (count++) {
57             out += ",\n";
58             out.append(currentIndentation, ' ');
59         }
60         CORE_NS::append<T>(out, v.key);
61         out += ": ";
62         out += to_formatted_string(v.value, indentation, currentIndentation);
63     }
64     currentIndentation -= indentation;
65     out += '\n';
66     out.append(currentIndentation, ' ');
67     out += '}';
68 }
69 
70 template<typename T>
append(BASE_NS::string & out,const typename value_t<T>::array & array,const int indentation,int currentIndentation)71 void append(
72     BASE_NS::string& out, const typename value_t<T>::array& array, const int indentation, int currentIndentation)
73 {
74     if (array.empty()) {
75         // Keep empty arrays on one row.
76         out.append("[]");
77         return;
78     }
79 
80     out += "[\n";
81     currentIndentation += indentation;
82     out.append(currentIndentation, ' ');
83     int count = 0;
84     for (const auto& v : array) {
85         if (count++) {
86             out += ",\n";
87             out.append(currentIndentation, ' ');
88         }
89         out += to_formatted_string(v, indentation, currentIndentation);
90     }
91     currentIndentation -= indentation;
92     out += '\n';
93     out.append(currentIndentation, ' ');
94     out += ']';
95 }
96 
97 template<typename T>
append(BASE_NS::string & out,const double floatingPoint)98 void append(BASE_NS::string& out, const double floatingPoint)
99 {
100     const char* FLOATING_FORMAT_STR = "%.17g";
101     const int size = snprintf(nullptr, 0, FLOATING_FORMAT_STR, floatingPoint);
102     const size_t oldSize = out.size();
103     out.resize(oldSize + size);
104     const size_t newSize = out.size();
105     // "At most bufsz - 1 characters are written." string has size() characters + 1 for null so use size() +
106     // 1 as the total size. If resize() failed string size() hasn't changed, buffer will point to the null
107     // character and bufsz will be 1 i.e. only the null character will be written.
108     snprintf_s(out.data() + oldSize, newSize + 1 - oldSize, size, FLOATING_FORMAT_STR, floatingPoint);
109 }
110 
111 template<typename T>
to_formatted_string(const value_t<T> & value,const int indentation,const int currentIndentation)112 BASE_NS::string to_formatted_string(const value_t<T>& value, const int indentation, const int currentIndentation)
113 {
114     BASE_NS::string out;
115     switch (value.type) {
116         case type::uninitialized:
117             out += "{}";
118             break;
119 
120         case type::object:
121             append<T>(out, value.object_, indentation, currentIndentation);
122             break;
123 
124         case type::array:
125             append<T>(out, value.array_, indentation, currentIndentation);
126             break;
127 
128         case type::string:
129             CORE_NS::append<T>(out, value.string_);
130             break;
131 
132         case type::floating_point:
133             CORE_NS::append<T>(out, value.float_);
134             break;
135 
136         case type::signed_int:
137             out += BASE_NS::to_string(value.signed_);
138             break;
139 
140         case type::unsigned_int:
141             out += BASE_NS::to_string(value.unsigned_);
142             break;
143 
144         case type::boolean:
145             if (value.boolean_) {
146                 out += "true";
147             } else {
148                 out += "false";
149             }
150             break;
151 
152         case type::null:
153             out += "null";
154             break;
155 
156         default:
157             break;
158     }
159     return out;
160 }
161 
162 // Explicit template instantiation for the needed types.
163 template BASE_NS::string to_formatted_string(const value& value, const int indentation, const int currentIndentation);
164 template BASE_NS::string to_formatted_string(
165     const standalone_value& value, const int indentation, const int currentIndentation);
166 
167 #endif // JSON_IMPL
168 
169 CORE_END_NAMESPACE()
170 
171 #endif // SCENE_PLUGIN_JSON_H
172