Merge "Implemented JSON Serialization using Cereal Library"
[platform/upstream/iotivity.git] / resource / src / OCRepresentation.cpp
1 //******************************************************************
2 //
3 // Copyright 2014 Intel Mobile Communications GmbH All Rights Reserved.
4 //
5 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
6 //
7 // Licensed under the Apache License, Version 2.0 (the "License");
8 // you may not use this file except in compliance with the License.
9 // You may obtain a copy of the License at
10 //
11 //      http://www.apache.org/licenses/LICENSE-2.0
12 //
13 // Unless required by applicable law or agreed to in writing, software
14 // distributed under the License is distributed on an "AS IS" BASIS,
15 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 // See the License for the specific language governing permissions and
17 // limitations under the License.
18 //
19 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
20
21 /// @file OCRepresentation.cpp
22
23 /// @brief  This file contains the implementation of classes and its members
24 ///         related to OCRepresentation
25
26 #include <cereal/cereal.hpp>
27 #include <cereal/types/map.hpp>
28 #include <cereal/types/vector.hpp>
29 #include <cereal/types/utility.hpp>
30 #include <algorithm>
31 #include <OCRepresentation.h>
32 namespace OC
33 {
34     typedef cereal::JSONOutputArchive OutputArchiveType;
35     typedef cereal::JSONInputArchive InputArchiveType;
36
37     void MessageContainer::setJSONRepresentation(const std::string& payload)
38     {
39         std::stringstream os(payload);
40         {
41             InputArchiveType archive(os);
42             archive(cereal::make_nvp(OC::Key::OCKEY, m_reps));
43         }
44     }
45
46     void MessageContainer::setJSONRepresentation(const unsigned char* payload)
47     {
48         setJSONRepresentation(std::string(reinterpret_cast<const char*>(payload)));
49     }
50
51     std::string MessageContainer::getJSONRepresentation(OCInfoFormat f) const
52     {
53         std::stringstream os;
54
55         // note: the block is required because cereal closes the JSON string
56         // upon destruction, so the archive needs to be destroyed before accessing
57         // the data
58         {
59             if(f == OCInfoFormat::IncludeOC)
60             {
61                 OutputArchiveType archive(os);
62                 archive(cereal::make_nvp(OC::Key::OCKEY, m_reps));
63             }
64             else if(f== OCInfoFormat::ExcludeOC)
65             {
66                 bool firstPrinted = false;
67                 for(std::vector<OCRepresentation>::size_type i = 0; i< m_reps.size();++i)
68                 {
69                     if(!m_reps[i].empty())
70                     {
71                         if(firstPrinted)
72                         {
73                             os<<',';
74                         }
75                         firstPrinted=true;
76                         os << m_reps[i].getJSONRepresentation();
77                     }
78                 }
79             }
80         }
81         return os.str();
82     }
83
84     const std::vector<OCRepresentation>& MessageContainer::representations() const
85     {
86         return m_reps;
87     }
88
89     void MessageContainer::addRepresentation(const OCRepresentation& rep)
90     {
91         m_reps.push_back(rep);
92     }
93 }
94
95 namespace OC
96 {
97     OCRepresentation::OCRepresentation()
98     :m_interfaceType(InterfaceType::None)
99     { }
100     std::string OCRepresentation::getJSONRepresentation() const
101     {
102         std::stringstream os;
103
104         // note: the block is required because cereal closes the JSON string
105         // upon destruction, so the archive needs to be destroyed before accessing
106         // the data
107         {
108             OutputArchiveType archive (os);
109             save(archive);
110         }
111
112         return os.str();
113     }
114
115     void OCRepresentation::addChild(const OCRepresentation& rep)
116     {
117         m_children.push_back(rep);
118     }
119
120     void OCRepresentation::clearChildren()
121     {
122         m_children.clear();
123     }
124
125     const std::vector<OCRepresentation>& OCRepresentation::getChildren() const
126     {
127         return m_children;
128     }
129
130     void OCRepresentation::setUri(const std::string& uri)
131     {
132         m_uri = uri;
133     }
134
135     std::string OCRepresentation::getUri() const
136     {
137         return m_uri;
138     }
139
140     const std::vector<std::string>& OCRepresentation::getResourceTypes() const
141     {
142         return m_resourceTypes;
143     }
144
145     void OCRepresentation::setResourceTypes(const std::vector<std::string>& resourceTypes)
146     {
147         m_resourceTypes = resourceTypes;
148     }
149
150     const std::vector<std::string>& OCRepresentation::getResourceInterfaces() const
151     {
152         return m_interfaces;
153     }
154
155     void OCRepresentation::setResourceInterfaces(const std::vector<std::string>& resourceInterfaces)
156     {
157         m_interfaces = resourceInterfaces;
158     }
159
160     bool OCRepresentation::hasAttribute(const std::string& str) const
161     {
162         return m_values.find(str) != m_values.end();
163     }
164
165     bool OCRepresentation::empty() const
166     {
167         // This logic is meant to determine whether based on the JSON serialization rules
168         // if this object will result in empty JSON.  URI is only serialized if there is valid
169         // data, ResourceType and Interfaces are only serialized if we are a nothing, a
170         // child of a default or link item.
171         // Our values array is only printed in the if we are the child of a Batch resource,
172         // the parent in a 'default' situation, or not in a child/parent relationship.
173         if(!m_uri.empty())
174         {
175             return false;
176         }
177         else if ((m_interfaceType == InterfaceType::None
178                         || m_interfaceType==InterfaceType::DefaultChild
179                         || m_interfaceType==InterfaceType::LinkChild)
180                     && (m_resourceTypes.size()>0 || m_interfaces.size()>0))
181         {
182             return false;
183         }
184         else if((m_interfaceType == InterfaceType::None
185                         || m_interfaceType == InterfaceType::BatchChild
186                         || m_interfaceType == InterfaceType::DefaultParent)
187                     && m_values.size()>0)
188         {
189             return false;
190         }
191
192         return true;
193     }
194
195     int OCRepresentation::numberOfAttributes() const
196     {
197         return m_values.size();
198     }
199
200     bool OCRepresentation::erase(const std::string& str)
201     {
202         return m_values.erase(str);
203     }
204
205     void OCRepresentation::setNULL(const std::string& str)
206     {
207         m_values[str] = OC::NullType();
208     }
209
210     bool OCRepresentation::isNULL(const std::string& str) const
211     {
212         auto x = m_values.find(str);
213
214         if(m_values.end() != x)
215         {
216             return x->second.which() == AttributeValueNullIndex;
217         }
218         else
219         {
220             throw OCException(OC::Exception::INVALID_ATTRIBUTE+ str);
221         }
222     }
223 }
224
225 // note: the below is used to load an AttributeValue map out of JSON
226 namespace OC
227 {
228     namespace detail
229     {
230         enum class typeTag:uint8_t
231         {
232             NOTHING = 0,
233             _string,
234             _int,
235             _double,
236             _bool,
237             _representation
238         };
239
240         typedef rapidjson::Document::GenericValue GenericValue;
241
242         AttributeValue parseAttributeValue(const GenericValue& v);
243         AttributeValue parseAttributeValue(const GenericValue& v,
244                 const unsigned int curLevel, unsigned int& maxDepth, typeTag& t);
245         AttributeValue parseAttributeValueObject(const GenericValue& v, typeTag& t);
246         AttributeValue parseAttributeValueArray(const GenericValue& v,
247                 const unsigned int curLevel, unsigned int& maxDepth, typeTag& t);
248         AttributeValue parseAttributeValuePrimitive(const GenericValue& v, typeTag& t);
249
250         AttributeValue parseAttributeValue(const GenericValue& v)
251         {
252             // base entrance, start everything at '0'
253             unsigned int max_depth {0};
254             typeTag t {typeTag::NOTHING};
255
256             return parseAttributeValue(v, 0, max_depth, t);
257         }
258
259         AttributeValue parseAttributeValue(const GenericValue& v,
260                 const unsigned int curLevel, unsigned int& maxDepth, typeTag& t)
261         {
262             if(v.IsObject())
263             {
264                 return parseAttributeValueObject(v, t);
265             }
266             else if(v.IsArray())
267             {
268                 return parseAttributeValueArray(v, curLevel + 1, maxDepth, t);
269             }
270             else
271             {
272                 return parseAttributeValuePrimitive(v,t);
273             }
274         }
275
276         AttributeValue parseAttributeValueObject(const GenericValue& v, typeTag& t)
277         {
278             typedef rapidjson::Value::ConstMemberIterator CMI;
279             t = typeTag::_representation;
280             OC::OCRepresentation rep;
281
282             for(CMI itr = v.MemberBegin(); itr!= v.MemberEnd(); ++itr)
283             {
284                 std::string keyName = itr->name.GetString();
285
286                 if(keyName == OC::Key::URIKEY)
287                 {
288                     rep.setUri(boost::get<std::string>(parseAttributeValue(itr->value)));
289                 }
290                 else if (keyName == OC::Key::PROPERTYKEY)
291                 {
292                     for(CMI itr2 = itr->value.MemberBegin();
293                             itr->value.MemberEnd()!=itr2;
294                             ++itr2)
295                     {
296                         if(keyName == OC::Key::RESOURCETYPESKEY)
297                         {
298                             rep.setResourceTypes(
299                                     boost::get<std::vector<std::string>>(
300                                         parseAttributeValue(itr->value)));
301                         }
302                         else if(keyName == OC::Key::PROPERTYKEY)
303                         {
304                             rep.setResourceInterfaces(
305                                     boost::get<std::vector<std::string>>(
306                                         parseAttributeValue(itr->value)));
307                         }
308                     }
309                 }
310                 else if (keyName == OC::Key::REPKEY)
311                 {
312                     for(CMI itr2 = itr->value.MemberBegin();
313                             itr->value.MemberEnd()!=itr2;
314                             ++itr2)
315                     {
316                         rep.setValue(itr2->name.GetString(),
317                                 parseAttributeValue(itr2->value));
318                     }
319                 }
320             }
321
322             return rep;
323         }
324
325         AttributeValue parseAttributeValuePrimitive(const GenericValue& v, typeTag& t)
326         {
327             if(v.IsString())
328             {
329                 t = typeTag::_string;
330                 return std::string(v.GetString());
331             }
332             else if (v.IsNumber())
333             {
334                 if(v.IsDouble())
335                 {
336                     t = typeTag::_double;
337                     return double(v.GetDouble());
338                 }
339                 else if (v.IsInt())
340                 {
341                     t = typeTag::_int;
342                     return int(v.GetInt());
343                 }
344                 else
345                 {
346                     throw OC::OCException(OC::Exception::INVALID_JSON_NUMERIC
347                             + std::to_string(v.GetType()));
348                 }
349             }
350             else if(v.IsBool_())
351             {
352                 t=typeTag::_bool;
353                 return bool(v.GetBool_());
354             }
355             else if(v.IsNull_())
356             {
357                 return OC::NullType();
358             }
359             else
360             {
361                 throw OC::OCException(OC::Exception::INVALID_JSON_TYPE
362                         + std::to_string(v.GetType()));
363             }
364         }
365
366         std::vector<AttributeValue> gatherArrayContents(const GenericValue& v,
367                 const unsigned int curLevel, unsigned int& maxDepth, typeTag& t)
368         {
369             std::vector<AttributeValue> out;
370
371             std::transform(v.Begin(), v.End(), back_inserter(out),
372                     [curLevel, &maxDepth, &t](const GenericValue& x)
373                     {
374                         return parseAttributeValue(x, curLevel, maxDepth, t);
375                     });
376             return out;
377         }
378
379         template<class OutT>
380         struct valueToConcrete
381         {
382             OutT operator()(const AttributeValue& v)
383             {
384                 return boost::get<OutT>(v);
385             }
386
387         };
388
389         template <class OutSeqT>
390         OutSeqT valuesToConcreteVectors(const std::vector<AttributeValue>& vs)
391         {
392             OutSeqT ret;
393
394             std::transform(begin(vs),end(vs), back_inserter(ret),
395                 valueToConcrete<typename OutSeqT::value_type>());
396             return ret;
397         }
398
399         template<class valueType>
400         AttributeValue remapArrayDepth(const unsigned int curLevel,
401                 const std::vector<OC::AttributeValue>& vs)
402         {
403             switch(curLevel)
404             {
405                 default:
406                     throw OC::OCException(OC::Exception::INVALID_JSON_ARRAY_DEPTH);
407                     break;
408                 case 1:
409                     return valuesToConcreteVectors<std::vector<valueType>>(vs);
410                     break;
411                 case 2:
412                     return valuesToConcreteVectors<std::vector<std::vector<valueType>>>(vs);
413                     break;
414                 case 3:
415                     return valuesToConcreteVectors
416                         <std::vector<std::vector<std::vector<valueType>>>>(vs);
417                     break;
418             }
419         }
420
421         AttributeValue convertArrayToConcretes(const typeTag t,
422                 const unsigned int curLevel, const std::vector<OC::AttributeValue>& vs)
423         {
424             // This function converts a std::vector of AttributeValue to a std::vector
425             // of concrete types.  Since we don't use a recursive Variant, we need
426             // to get back to a 'base' primitive type
427             switch(t)
428             {
429                 default:
430                 case typeTag::NOTHING:
431                     throw OC::OCException(OC::Exception::INVALID_JSON_TYPE_TAG);
432                     break;
433                 case typeTag::_string:
434                     return remapArrayDepth<std::string>(curLevel, vs);
435                     break;
436                 case typeTag::_int:
437                     return remapArrayDepth<int>(curLevel, vs);
438                     break;
439                 case typeTag::_double:
440                     return remapArrayDepth<double>(curLevel, vs);
441                     break;
442                 case typeTag::_bool:
443                     return remapArrayDepth<bool>(curLevel, vs);
444                     break;
445                 case typeTag::_representation:
446                     return remapArrayDepth<OCRepresentation>(curLevel, vs);
447                     break;
448             }
449         }
450
451         AttributeValue parseAttributeValueArray(const GenericValue& v,
452                 const unsigned int curLevel, unsigned int& maxDepth, typeTag& t)
453         {
454             const unsigned int max_level = 3;
455
456             if(curLevel > max_level)
457             {
458                 throw OC::OCException(OC::Exception::INVALID_JSON_ARRAY_DEPTH);
459             }
460
461             if(curLevel > maxDepth)
462             {
463                 maxDepth = curLevel;
464             }
465
466             auto arrayItems = gatherArrayContents(v, curLevel, maxDepth, t);
467             const int remapLevel = maxDepth - (curLevel -1);
468             return convertArrayToConcretes(t, remapLevel, arrayItems);
469         }
470     }
471 }
472
473 namespace cereal
474 {
475    void JSONInputArchive::loadAttributeValues(std::map<std::string, OC::AttributeValue>& map)
476    {
477        for(auto&b = itsIteratorStack.back();
478            b.Member && b.itsMemberItEnd != b.itsMemberItBegin+b.itsIndex;
479            ++b)
480        {
481            std::string key = b.itsMemberItBegin[b.itsIndex].name.GetString();
482            const GenericValue& v = itsIteratorStack.back().value();
483            map[key] = OC::detail::parseAttributeValue(v);
484        }
485    }
486 }