]> granicus.if.org Git - icinga2/blob - lib/base/serializer.cpp
Merge pull request #7002 from Icinga/bugfix/check_network-percent-6155
[icinga2] / lib / base / serializer.cpp
1 /* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
2
3 #include "base/serializer.hpp"
4 #include "base/type.hpp"
5 #include "base/application.hpp"
6 #include "base/objectlock.hpp"
7 #include "base/convert.hpp"
8 #include "base/exception.hpp"
9 #include "base/namespace.hpp"
10 #include <boost/algorithm/string/join.hpp>
11 #include <deque>
12
13 using namespace icinga;
14
15 struct SerializeStackEntry
16 {
17         String Name;
18         Value Val;
19 };
20
21 CircularReferenceError::CircularReferenceError(String message, std::vector<String> path)
22         : m_Message(message), m_Path(path)
23 { }
24
25 const char *CircularReferenceError::what(void) const throw()
26 {
27         return m_Message.CStr();
28 }
29
30 std::vector<String> CircularReferenceError::GetPath() const
31 {
32         return m_Path;
33 }
34
35 struct SerializeStack
36 {
37         std::deque<SerializeStackEntry> Entries;
38
39         inline void Push(const String& name, const Value& val)
40         {
41                 Object::Ptr obj;
42
43                 if (val.IsObject())
44                         obj = val;
45
46                 if (obj) {
47                         for (const auto& entry : Entries) {
48                                 if (entry.Val == obj) {
49                                         std::vector<String> path;
50                                         for (const auto& entry : Entries)
51                                                 path.push_back(entry.Name);
52                                         path.push_back(name);
53                                         BOOST_THROW_EXCEPTION(CircularReferenceError("Cannot serialize object which recursively refers to itself. Attribute path which leads to the cycle: " + boost::algorithm::join(path, " -> "), path));
54                                 }
55                         }
56                 }
57
58                 Entries.push_back({ name, obj });
59         }
60
61         inline void Pop()
62         {
63                 Entries.pop_back();
64         }
65 };
66
67 static Value SerializeInternal(const Value& value, int attributeTypes, SerializeStack& stack);
68
69 static Array::Ptr SerializeArray(const Array::Ptr& input, int attributeTypes, SerializeStack& stack)
70 {
71         ArrayData result;
72
73         result.reserve(input->GetLength());
74
75         ObjectLock olock(input);
76
77         int index = 0;
78
79         for (const Value& value : input) {
80                 stack.Push(Convert::ToString(index), value);
81                 result.emplace_back(SerializeInternal(value, attributeTypes, stack));
82                 stack.Pop();
83                 index++;
84         }
85
86         return new Array(std::move(result));
87 }
88
89 static Dictionary::Ptr SerializeDictionary(const Dictionary::Ptr& input, int attributeTypes, SerializeStack& stack)
90 {
91         DictionaryData result;
92
93         result.reserve(input->GetLength());
94
95         ObjectLock olock(input);
96
97         for (const Dictionary::Pair& kv : input) {
98                 stack.Push(kv.first, kv.second);
99                 result.emplace_back(kv.first, SerializeInternal(kv.second, attributeTypes, stack));
100                 stack.Pop();
101         }
102
103         return new Dictionary(std::move(result));
104 }
105
106 static Dictionary::Ptr SerializeNamespace(const Namespace::Ptr& input, int attributeTypes, SerializeStack& stack)
107 {
108         DictionaryData result;
109
110         ObjectLock olock(input);
111
112         for (const Namespace::Pair& kv : input) {
113                 Value val = kv.second->Get();
114                 stack.Push(kv.first, val);
115                 result.emplace_back(kv.first, Serialize(val, attributeTypes));
116                 stack.Pop();
117         }
118
119         return new Dictionary(std::move(result));
120 }
121
122 static Object::Ptr SerializeObject(const Object::Ptr& input, int attributeTypes, SerializeStack& stack)
123 {
124         Type::Ptr type = input->GetReflectionType();
125
126         if (!type)
127                 return nullptr;
128
129         DictionaryData fields;
130         fields.reserve(type->GetFieldCount() + 1);
131
132         for (int i = 0; i < type->GetFieldCount(); i++) {
133                 Field field = type->GetFieldInfo(i);
134
135                 if (attributeTypes != 0 && (field.Attributes & attributeTypes) == 0)
136                         continue;
137
138                 if (strcmp(field.Name, "type") == 0)
139                         continue;
140
141                 Value value = input->GetField(i);
142                 stack.Push(field.Name, value);
143                 fields.emplace_back(field.Name, SerializeInternal(input->GetField(i), attributeTypes, stack));
144                 stack.Pop();
145         }
146
147         fields.emplace_back("type", type->GetName());
148
149         return new Dictionary(std::move(fields));
150 }
151
152 static Array::Ptr DeserializeArray(const Array::Ptr& input, bool safe_mode, int attributeTypes)
153 {
154         ArrayData result;
155
156         result.reserve(input->GetLength());
157
158         ObjectLock olock(input);
159
160         for (const Value& value : input) {
161                 result.emplace_back(Deserialize(value, safe_mode, attributeTypes));
162         }
163
164         return new Array(std::move(result));
165 }
166
167 static Dictionary::Ptr DeserializeDictionary(const Dictionary::Ptr& input, bool safe_mode, int attributeTypes)
168 {
169         DictionaryData result;
170
171         result.reserve(input->GetLength());
172
173         ObjectLock olock(input);
174
175         for (const Dictionary::Pair& kv : input) {
176                 result.emplace_back(kv.first, Deserialize(kv.second, safe_mode, attributeTypes));
177         }
178
179         return new Dictionary(std::move(result));
180 }
181
182 static Object::Ptr DeserializeObject(const Object::Ptr& object, const Dictionary::Ptr& input, bool safe_mode, int attributeTypes)
183 {
184         if (!object && safe_mode)
185                 BOOST_THROW_EXCEPTION(std::runtime_error("Tried to instantiate object while safe mode is enabled."));
186
187         Type::Ptr type;
188
189         if (object)
190                 type = object->GetReflectionType();
191         else
192                 type = Type::GetByName(input->Get("type"));
193
194         if (!type)
195                 return object;
196
197         Object::Ptr instance;
198
199         if (object)
200                 instance = object;
201         else
202                 instance = type->Instantiate(std::vector<Value>());
203
204         ObjectLock olock(input);
205         for (const Dictionary::Pair& kv : input) {
206                 if (kv.first.IsEmpty())
207                         continue;
208
209                 int fid = type->GetFieldId(kv.first);
210
211                 if (fid < 0)
212                         continue;
213
214                 Field field = type->GetFieldInfo(fid);
215
216                 if ((field.Attributes & attributeTypes) == 0)
217                         continue;
218
219                 try {
220                         instance->SetField(fid, Deserialize(kv.second, safe_mode, attributeTypes), true);
221                 } catch (const std::exception&) {
222                         instance->SetField(fid, Empty);
223                 }
224         }
225
226         return instance;
227 }
228
229 static Value SerializeInternal(const Value& value, int attributeTypes, SerializeStack& stack)
230 {
231         if (!value.IsObject())
232                 return value;
233
234         Object::Ptr input = value;
235
236         Array::Ptr array = dynamic_pointer_cast<Array>(input);
237
238         if (array)
239                 return SerializeArray(array, attributeTypes, stack);
240
241         Dictionary::Ptr dict = dynamic_pointer_cast<Dictionary>(input);
242
243         if (dict)
244                 return SerializeDictionary(dict, attributeTypes, stack);
245
246         Namespace::Ptr ns = dynamic_pointer_cast<Namespace>(input);
247
248         if (ns)
249                 return SerializeNamespace(ns, attributeTypes, stack);
250
251         return SerializeObject(input, attributeTypes, stack);
252 }
253
254 Value icinga::Serialize(const Value& value, int attributeTypes)
255 {
256         SerializeStack stack;
257         return SerializeInternal(value, attributeTypes, stack);
258 }
259
260 Value icinga::Deserialize(const Value& value, bool safe_mode, int attributeTypes)
261 {
262         return Deserialize(nullptr, value, safe_mode, attributeTypes);
263 }
264
265 Value icinga::Deserialize(const Object::Ptr& object, const Value& value, bool safe_mode, int attributeTypes)
266 {
267         if (!value.IsObject())
268                 return value;
269
270         Object::Ptr input = value;
271
272         Array::Ptr array = dynamic_pointer_cast<Array>(input);
273
274         if (array)
275                 return DeserializeArray(array, safe_mode, attributeTypes);
276
277         Dictionary::Ptr dict = dynamic_pointer_cast<Dictionary>(input);
278
279         ASSERT(dict);
280
281         if ((safe_mode && !object) || !dict->Contains("type"))
282                 return DeserializeDictionary(dict, safe_mode, attributeTypes);
283         else
284                 return DeserializeObject(object, dict, safe_mode, attributeTypes);
285 }