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 #include "json_input.h"
16 
17 #include <base/containers/vector.h>
18 #include <base/util/uid_util.h>
19 
20 #include <meta/base/namespace.h>
21 #include <meta/base/plugin.h>
22 #include <meta/base/ref_uri.h>
23 #include <meta/ext/minimal_object.h>
24 
25 #include "../ser_nodes.h"
26 
27 META_BEGIN_NAMESPACE()
28 
29 namespace Serialization {
30 
31 constexpr Version CURRENT_JSON_VERSION(2, 0);
32 constexpr int UID_SIZE = 36;
33 
ImportRef(const json::value & ref)34 ISerNode::Ptr JsonInput::ImportRef(const json::value& ref)
35 {
36     if (ref.is_string()) {
37         RefUri uri;
38         // check for backward compatibility
39         if (ref.string_.substr(0, 4) == "ref:") {
40             uri = RefUri(CORE_NS::json::unescape(ref.string_));
41         } else {
42             uri.SetBaseObjectUid(BASE_NS::StringToUid(CORE_NS::json::unescape(ref.string_)));
43         }
44         if (uri.IsValid()) {
45             return ISerNode::Ptr(new RefNode(BASE_NS::move(uri)));
46         }
47     }
48     return nullptr;
49 }
50 
ReadString(BASE_NS::string_view name,const json::value & value)51 BASE_NS::string ReadString(BASE_NS::string_view name, const json::value& value)
52 {
53     BASE_NS::string str;
54     if (const auto& v = value.find(name)) {
55         if (v->is_string()) {
56             str = CORE_NS::json::unescape(v->string_);
57         }
58     }
59     return str;
60 }
61 
ReadUid(BASE_NS::string_view name,const json::value & value)62 BASE_NS::Uid ReadUid(BASE_NS::string_view name, const json::value& value)
63 {
64     BASE_NS::Uid uid;
65     if (const auto& id = value.find(name)) {
66         if (id->is_string() && id->string_.size() == UID_SIZE) {
67             uid = BASE_NS::StringToUid(id->string_);
68         }
69     }
70     return uid;
71 }
72 
IsValidName(const BASE_NS::string_view & name)73 static bool IsValidName(const BASE_NS::string_view& name)
74 {
75     return !name.empty() && name[0] != '$';
76 }
77 
ImportObject(const json::value & value)78 ISerNode::Ptr JsonInput::ImportObject(const json::value& value)
79 {
80     BASE_NS::string className = ReadString(ClassNameName, value);
81     BASE_NS::string name = ReadString(NameName, value);
82     ObjectId oid = ReadUid(ObjectIdName, value);
83     InstanceId iid = ReadUid(InstanceIdName, value);
84 
85     BASE_NS::vector<NamedNode> members;
86     for (auto&& p : value.object_) {
87         auto key = RewriteReservedName(p.key);
88         if (!IsValidName(key)) {
89             // Skip empty keys and keys starting with $
90             continue;
91         }
92         auto n = Import(p.value);
93         if (!n) {
94             return nullptr;
95         }
96         members.push_back(NamedNode { key, BASE_NS::move(n) });
97     }
98     auto map = CreateShared<MapNode>(BASE_NS::move(members));
99     if (oid.IsValid()) {
100         return ISerNode::Ptr(
101             new ObjectNode(BASE_NS::move(className), BASE_NS::move(name), oid, iid, BASE_NS::move(map)));
102     }
103     return map;
104 }
105 
ImportArray(const json::value::array & arr)106 ISerNode::Ptr JsonInput::ImportArray(const json::value::array& arr)
107 {
108     BASE_NS::vector<ISerNode::Ptr> nodes;
109     nodes.reserve(arr.size());
110     for (auto&& value : arr) {
111         if (auto n = Import(value)) {
112             nodes.emplace_back(BASE_NS::move(n));
113         } else {
114             return nullptr;
115         }
116     }
117     return ISerNode::Ptr(new ArrayNode(BASE_NS::move(nodes)));
118 }
119 
Import(const json::value & value)120 ISerNode::Ptr JsonInput::Import(const json::value& value)
121 {
122     switch (value.type) {
123         case json::type::boolean:
124             return ISerNode::Ptr(new BoolNode(value.boolean_));
125         case json::type::floating_point:
126             return ISerNode::Ptr(new DoubleNode(value.float_));
127         case json::type::signed_int:
128             return ISerNode::Ptr(new IntNode(value.signed_));
129         case json::type::unsigned_int:
130             return ISerNode::Ptr(new UIntNode(value.unsigned_));
131         case json::type::string:
132             return ISerNode::Ptr(new StringNode(CORE_NS::json::unescape(value.string_)));
133         case json::type::object:
134             if (auto ref = value.find("$ref")) {
135                 return ImportRef(*ref);
136             }
137             return ImportObject(value);
138         case json::type::array:
139             return ImportArray(value.array_);
140         case json::type::null:
141             return ISerNode::Ptr(new NilNode);
142         default:
143             CORE_ASSERT_MSG(false, "Unhandled primitive type in Json input");
144             return nullptr;
145     }
146 }
147 
ReadMetadata(const json::value & value)148 bool JsonInput::ReadMetadata(const json::value& value)
149 {
150     if (auto v = value.find("version")) {
151         if (v->is_string()) {
152             auto ver = Version(CORE_NS::json::unescape(v->string_));
153             if (ver == Version()) {
154                 CORE_LOG_E(
155                     "Invalid file version: %s != %s", ver.ToString().c_str(), CURRENT_JSON_VERSION.ToString().c_str());
156                 return false;
157             }
158             jsonVersion_ = ver;
159         }
160     }
161     if (auto v = value.find("exporter-version")) {
162         if (v->is_string()) {
163             exporterVersion_ = Version(CORE_NS::json::unescape(v->string_));
164         }
165     }
166     return true;
167 }
168 
ImportRootObject(const json::value & value)169 ISerNode::Ptr JsonInput::ImportRootObject(const json::value& value)
170 {
171     // see if we have meta data
172     if (auto v = value.find("$meta")) {
173         if (v->is_object() && !ReadMetadata(*v)) {
174             return nullptr;
175         }
176     }
177 
178     json::value root;
179 
180     // is it legacy version?
181     if (jsonVersion_ == Version {}) {
182         jsonVersion_ = Version { 1, 0 };
183         root = value;
184     } else if (auto v = value.find("$root")) {
185         if (v->is_object()) {
186             root = *v;
187         }
188     }
189     if (jsonVersion_ < Version(2, 0)) {
190         SetMetaV1Compatibility();
191     }
192 
193     auto obj = Import(root);
194     return obj ? ISerNode::Ptr(new RootNode(exporterVersion_, obj)) : nullptr;
195 }
196 
Process(BASE_NS::string_view data)197 ISerNode::Ptr JsonInput::Process(BASE_NS::string_view data)
198 {
199     ISerNode::Ptr res;
200     auto json = CORE_NS::json::parse(data.data());
201     if (json.is_object()) {
202         res = ImportRootObject(json);
203     }
204     return res;
205 }
206 
SetMetaV1Compatibility()207 void JsonInput::SetMetaV1Compatibility()
208 {
209     CORE_LOG_I("Enabling Meta Object Version 1 compatibility");
210     ClassNameName = "$name";
211     NameName = "";
212     RewriteReservedName = [](auto name) {
213         if (name == "$properties") {
214             return BASE_NS::string("__properties");
215         }
216         if (name == "$flags") {
217             return BASE_NS::string("__flags");
218         }
219         return BASE_NS::string(name);
220     };
221 }
222 
223 } // namespace Serialization
224 
225 META_END_NAMESPACE()
226