]> granicus.if.org Git - icinga2/blob - lib/base/serializer.cpp
Merge pull request #7185 from Icinga/bugfix/gelfwriter-wrong-log-facility
[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         ObjectLock olock(input);
133
134         for (int i = 0; i < type->GetFieldCount(); i++) {
135                 Field field = type->GetFieldInfo(i);
136
137                 if (attributeTypes != 0 && (field.Attributes & attributeTypes) == 0)
138                         continue;
139
140                 if (strcmp(field.Name, "type") == 0)
141                         continue;
142
143                 Value value = input->GetField(i);
144                 stack.Push(field.Name, value);
145                 fields.emplace_back(field.Name, SerializeInternal(value, attributeTypes, stack));
146                 stack.Pop();
147         }
148
149         fields.emplace_back("type", type->GetName());
150
151         return new Dictionary(std::move(fields));
152 }
153
154 static Array::Ptr DeserializeArray(const Array::Ptr& input, bool safe_mode, int attributeTypes)
155 {
156         ArrayData result;
157
158         result.reserve(input->GetLength());
159
160         ObjectLock olock(input);
161
162         for (const Value& value : input) {
163                 result.emplace_back(Deserialize(value, safe_mode, attributeTypes));
164         }
165
166         return new Array(std::move(result));
167 }
168
169 static Dictionary::Ptr DeserializeDictionary(const Dictionary::Ptr& input, bool safe_mode, int attributeTypes)
170 {
171         DictionaryData result;
172
173         result.reserve(input->GetLength());
174
175         ObjectLock olock(input);
176
177         for (const Dictionary::Pair& kv : input) {
178                 result.emplace_back(kv.first, Deserialize(kv.second, safe_mode, attributeTypes));
179         }
180
181         return new Dictionary(std::move(result));
182 }
183
184 static Object::Ptr DeserializeObject(const Object::Ptr& object, const Dictionary::Ptr& input, bool safe_mode, int attributeTypes)
185 {
186         if (!object && safe_mode)
187                 BOOST_THROW_EXCEPTION(std::runtime_error("Tried to instantiate object while safe mode is enabled."));
188
189         Type::Ptr type;
190
191         if (object)
192                 type = object->GetReflectionType();
193         else
194                 type = Type::GetByName(input->Get("type"));
195
196         if (!type)
197                 return object;
198
199         Object::Ptr instance;
200
201         if (object)
202                 instance = object;
203         else
204                 instance = type->Instantiate(std::vector<Value>());
205
206         ObjectLock olock(input);
207         for (const Dictionary::Pair& kv : input) {
208                 if (kv.first.IsEmpty())
209                         continue;
210
211                 int fid = type->GetFieldId(kv.first);
212
213                 if (fid < 0)
214                         continue;
215
216                 Field field = type->GetFieldInfo(fid);
217
218                 if ((field.Attributes & attributeTypes) == 0)
219                         continue;
220
221                 try {
222                         instance->SetField(fid, Deserialize(kv.second, safe_mode, attributeTypes), true);
223                 } catch (const std::exception&) {
224                         instance->SetField(fid, Empty);
225                 }
226         }
227
228         return instance;
229 }
230
231 static Value SerializeInternal(const Value& value, int attributeTypes, SerializeStack& stack)
232 {
233         if (!value.IsObject())
234                 return value;
235
236         Object::Ptr input = value;
237
238         Array::Ptr array = dynamic_pointer_cast<Array>(input);
239
240         if (array)
241                 return SerializeArray(array, attributeTypes, stack);
242
243         Dictionary::Ptr dict = dynamic_pointer_cast<Dictionary>(input);
244
245         if (dict)
246                 return SerializeDictionary(dict, attributeTypes, stack);
247
248         Namespace::Ptr ns = dynamic_pointer_cast<Namespace>(input);
249
250         if (ns)
251                 return SerializeNamespace(ns, attributeTypes, stack);
252
253         return SerializeObject(input, attributeTypes, stack);
254 }
255
256 Value icinga::Serialize(const Value& value, int attributeTypes)
257 {
258         SerializeStack stack;
259         return SerializeInternal(value, attributeTypes, stack);
260 }
261
262 Value icinga::Deserialize(const Value& value, bool safe_mode, int attributeTypes)
263 {
264         return Deserialize(nullptr, value, safe_mode, attributeTypes);
265 }
266
267 Value icinga::Deserialize(const Object::Ptr& object, const Value& value, bool safe_mode, int attributeTypes)
268 {
269         if (!value.IsObject())
270                 return value;
271
272         Object::Ptr input = value;
273
274         Array::Ptr array = dynamic_pointer_cast<Array>(input);
275
276         if (array)
277                 return DeserializeArray(array, safe_mode, attributeTypes);
278
279         Dictionary::Ptr dict = dynamic_pointer_cast<Dictionary>(input);
280
281         ASSERT(dict);
282
283         if ((safe_mode && !object) || !dict->Contains("type"))
284                 return DeserializeDictionary(dict, safe_mode, attributeTypes);
285         else
286                 return DeserializeObject(object, dict, safe_mode, attributeTypes);
287 }