Merge branch 'master' into connectivity-abstraction
[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
27 #include <OCRepresentation.h>
28
29 #include <cereal/cereal.hpp>
30 #include <cereal/types/map.hpp>
31 #include <cereal/types/vector.hpp>
32 #include <cereal/types/utility.hpp>
33 #include <OicJsonSerializer.hpp>
34 #include <algorithm>
35
36 // code needed to serialize a string::Attribute value map
37 namespace OC
38 {
39     namespace detail
40     {
41         template<class Archive>
42         class WriteAttributeValue : public boost::static_visitor<>
43         {
44             public:
45                 WriteAttributeValue(const std::string& name, Archive& ar)
46                     :m_name(name), m_archive(ar)
47                 {}
48
49                 template<class T>
50                 void operator()(const T& value) const
51                 {
52                     m_archive(cereal::make_nvp(m_name, value));
53                 }
54             private:
55                 std::string m_name;
56                 Archive& m_archive;
57         };
58     }
59 }
60
61 namespace cereal
62 {
63     // take no action when serializing the null type, because the 'save' below
64     // doesn't use the visitor for this type.
65     template <class Archive>
66     void serialize(Archive&, OC::NullType t)
67     {}
68
69     template<class Archive>
70     void save(Archive& ar, const std::map<std::string, OC::AttributeValue>& vals)
71     {
72         for(const auto& kv : vals)
73         {
74             const auto& k = kv.first;
75             const auto& v = kv.second;
76
77             if(v.which() != OC::AttributeValueNullIndex)
78             {
79                 OC::detail::WriteAttributeValue<Archive> writer(k,ar);
80                 boost::apply_visitor(writer, v);
81             }
82             else
83             {
84                 ar.setNextName(k.c_str());
85                 ar.writeName();
86                 ar.saveValue();
87             }
88         }
89     }
90
91     template<class Archive>
92     void load(Archive& ar, std::map<std::string, OC::AttributeValue>& vals)
93     {
94         ar.loadAttributeValues(vals);
95     }
96 }
97
98 namespace OC
99 {
100     typedef cereal::JSONOutputArchive OutputArchiveType;
101     typedef cereal::JSONInputArchive InputArchiveType;
102
103     void MessageContainer::setJSONRepresentation(const std::string& payload)
104     {
105         std::stringstream os(payload);
106         {
107             InputArchiveType archive(os);
108             archive(cereal::make_nvp(OC::Key::OCKEY, m_reps));
109         }
110     }
111
112     void MessageContainer::setJSONRepresentation(const unsigned char* payload)
113     {
114         setJSONRepresentation(std::string(reinterpret_cast<const char*>(payload)));
115     }
116
117     std::string MessageContainer::getJSONRepresentation(OCInfoFormat f) const
118     {
119         std::stringstream os;
120
121         // note: the block is required because cereal closes the JSON string
122         // upon destruction, so the archive needs to be destroyed before accessing
123         // the data
124         {
125             if(f == OCInfoFormat::IncludeOC)
126             {
127                 OutputArchiveType archive(os);
128                 archive(cereal::make_nvp(OC::Key::OCKEY, m_reps));
129             }
130             else if(f== OCInfoFormat::ExcludeOC)
131             {
132                 bool firstPrinted = false;
133                 for(std::vector<OCRepresentation>::size_type i = 0; i< m_reps.size();++i)
134                 {
135                     if(!m_reps[i].empty())
136                     {
137                         if(firstPrinted)
138                         {
139                             os<<',';
140                         }
141                         firstPrinted=true;
142                         os << m_reps[i].getJSONRepresentation();
143                     }
144                 }
145             }
146         }
147         return os.str();
148     }
149
150     const std::vector<OCRepresentation>& MessageContainer::representations() const
151     {
152         return m_reps;
153     }
154
155     void MessageContainer::addRepresentation(const OCRepresentation& rep)
156     {
157         m_reps.push_back(rep);
158     }
159 }
160
161 namespace OC
162 {
163     OCRepresentation::OCRepresentation()
164     :m_interfaceType(InterfaceType::None)
165     { }
166     std::string OCRepresentation::getJSONRepresentation() const
167     {
168         std::stringstream os;
169
170         // note: the block is required because cereal closes the JSON string
171         // upon destruction, so the archive needs to be destroyed before accessing
172         // the data
173         {
174             OutputArchiveType archive (os);
175             save(archive);
176         }
177
178         return os.str();
179     }
180
181     void OCRepresentation::addChild(const OCRepresentation& rep)
182     {
183         m_children.push_back(rep);
184     }
185
186     void OCRepresentation::clearChildren()
187     {
188         m_children.clear();
189     }
190
191     const std::vector<OCRepresentation>& OCRepresentation::getChildren() const
192     {
193         return m_children;
194     }
195
196     void OCRepresentation::setUri(const std::string& uri)
197     {
198         m_uri = uri;
199     }
200
201     std::string OCRepresentation::getUri() const
202     {
203         return m_uri;
204     }
205
206     const std::vector<std::string>& OCRepresentation::getResourceTypes() const
207     {
208         return m_resourceTypes;
209     }
210
211     void OCRepresentation::setResourceTypes(const std::vector<std::string>& resourceTypes)
212     {
213         m_resourceTypes = resourceTypes;
214     }
215
216     const std::vector<std::string>& OCRepresentation::getResourceInterfaces() const
217     {
218         return m_interfaces;
219     }
220
221     void OCRepresentation::setResourceInterfaces(const std::vector<std::string>& resourceInterfaces)
222     {
223         m_interfaces = resourceInterfaces;
224     }
225
226     bool OCRepresentation::hasAttribute(const std::string& str) const
227     {
228         return m_values.find(str) != m_values.end();
229     }
230
231     bool OCRepresentation::empty() const
232     {
233         // This logic is meant to determine whether based on the JSON serialization rules
234         // if this object will result in empty JSON.  URI is only serialized if there is valid
235         // data, ResourceType and Interfaces are only serialized if we are a nothing, a
236         // child of a default or link item.
237         // Our values array is only printed in the if we are the child of a Batch resource,
238         // the parent in a 'default' situation, or not in a child/parent relationship.
239         if(!m_uri.empty())
240         {
241             return false;
242         }
243         else if ((m_interfaceType == InterfaceType::None
244                         || m_interfaceType==InterfaceType::DefaultChild
245                         || m_interfaceType==InterfaceType::LinkChild)
246                     && (m_resourceTypes.size()>0 || m_interfaces.size()>0))
247         {
248             return false;
249         }
250         else if((m_interfaceType == InterfaceType::None
251                         || m_interfaceType == InterfaceType::BatchChild
252                         || m_interfaceType == InterfaceType::DefaultParent)
253                     && m_values.size()>0)
254         {
255             return false;
256         }
257
258         return true;
259     }
260
261     int OCRepresentation::numberOfAttributes() const
262     {
263         return m_values.size();
264     }
265
266     bool OCRepresentation::erase(const std::string& str)
267     {
268         return m_values.erase(str);
269     }
270
271     void OCRepresentation::setNULL(const std::string& str)
272     {
273         m_values[str] = OC::NullType();
274     }
275
276     bool OCRepresentation::isNULL(const std::string& str) const
277     {
278         auto x = m_values.find(str);
279
280         if(m_values.end() != x)
281         {
282             return x->second.which() == AttributeValueNullIndex;
283         }
284         else
285         {
286             throw OCException(OC::Exception::INVALID_ATTRIBUTE+ str);
287         }
288     }
289 }
290
291 namespace OC
292 {
293     template <class Archive, class Val>
294     void OCRepresentation::optional_load(Archive& ar, Val&& v)
295     {
296         try
297         {
298             ar(v);
299         }
300         catch(cereal::Exception& e)
301         {
302             ar.setNextName(nullptr);
303             // Loading a key that doesn't exist results in an exception
304             // Since "Not Found" is a valid condition for us, we swallow
305             // this exception and the archive will not load anything
306         }
307     }
308
309     template<class Archive>
310     void OCRepresentation::save(Archive& ar) const
311     {
312         // printed for all interface types
313         if(!m_uri.empty())
314         {
315             ar(cereal::make_nvp(Key::URIKEY, m_uri));
316         }
317
318         if((m_interfaceType == InterfaceType::None
319                     || m_interfaceType==InterfaceType::DefaultChild
320                     || m_interfaceType==InterfaceType::LinkChild)
321                 && (m_resourceTypes.size()>0 || m_interfaces.size()>0))
322         {
323             // The Prop object requires that it refer to non-const vectors
324             // so that it can alter them in the 'load' case.  In the save case
325             // (initiated here) it will not modify the object.  So, to keep the
326             // compiler happy, removing the 'const' context here is necessary.
327             const std::vector<std::string>& rt(m_resourceTypes);
328             const std::vector<std::string>& intf(m_interfaces);
329             Prop temp(const_cast<std::vector<std::string>&>(rt),
330                     const_cast<std::vector<std::string>&>(intf));
331             ar(cereal::make_nvp(Key::PROPERTYKEY, temp));
332         }
333
334         // printed only for BatchChildren and DefaultParent
335         if((m_interfaceType == InterfaceType::None
336                     || m_interfaceType == InterfaceType::BatchChild
337                     || m_interfaceType == InterfaceType::DefaultParent)
338                 && m_values.size()>0)
339         {
340             ar(cereal::make_nvp(Key::REPKEY, m_values));
341         }
342     }
343
344     template<class Archive>
345     void OCRepresentation::load(Archive& ar)
346     {
347         optional_load(ar, cereal::make_nvp(Key::URIKEY, m_uri));
348         {
349             Prop temp(m_resourceTypes, m_interfaces);
350             optional_load(ar, cereal::make_nvp(Key::PROPERTYKEY, temp));
351         }
352         optional_load(ar, cereal::make_nvp(Key::REPKEY, m_values));
353     }
354
355     template<class Archive>
356     void OCRepresentation::Prop::save(Archive& ar) const
357     {
358         if(m_types.size() > 0)
359         {
360             ar(cereal::make_nvp(Key::RESOURCETYPESKEY, m_types));
361         }
362
363         if(m_interfaces.size()>0)
364         {
365             ar(cereal::make_nvp(Key::INTERFACESKEY, m_interfaces));
366         }
367     }
368
369     template<class Archive>
370     void OCRepresentation::Prop::load(Archive& ar)
371     {
372         optional_load(ar, cereal::make_nvp(Key::RESOURCETYPESKEY, m_types));
373         optional_load(ar, cereal::make_nvp(Key::INTERFACESKEY, m_interfaces));
374     }
375 }
376
377 // note: the below is used to load an AttributeValue map out of JSON
378 namespace OC
379 {
380     namespace detail
381     {
382         enum class typeTag:uint8_t
383         {
384             NOTHING = 0,
385             _string,
386             _int,
387             _double,
388             _bool,
389             _representation
390         };
391
392         typedef rapidjson::Document::GenericValue GenericValue;
393
394         AttributeValue parseAttributeValue(const GenericValue& v);
395         AttributeValue parseAttributeValue(const GenericValue& v,
396                 const unsigned int curLevel, unsigned int& maxDepth, typeTag& t);
397         AttributeValue parseAttributeValueObject(const GenericValue& v, typeTag& t);
398         AttributeValue parseAttributeValueArray(const GenericValue& v,
399                 const unsigned int curLevel, unsigned int& maxDepth, typeTag& t);
400         AttributeValue parseAttributeValuePrimitive(const GenericValue& v, typeTag& t);
401
402         AttributeValue parseAttributeValue(const GenericValue& v)
403         {
404             // base entrance, start everything at '0'
405             unsigned int max_depth {0};
406             typeTag t {typeTag::NOTHING};
407
408             return parseAttributeValue(v, 0, max_depth, t);
409         }
410
411         AttributeValue parseAttributeValue(const GenericValue& v,
412                 const unsigned int curLevel, unsigned int& maxDepth, typeTag& t)
413         {
414             if(v.IsObject())
415             {
416                 return parseAttributeValueObject(v, t);
417             }
418             else if(v.IsArray())
419             {
420                 return parseAttributeValueArray(v, curLevel + 1, maxDepth, t);
421             }
422             else
423             {
424                 return parseAttributeValuePrimitive(v,t);
425             }
426         }
427
428         AttributeValue parseAttributeValueObject(const GenericValue& v, typeTag& t)
429         {
430             typedef rapidjson::Value::ConstMemberIterator CMI;
431             t = typeTag::_representation;
432             OC::OCRepresentation rep;
433
434             for(CMI itr = v.MemberBegin(); itr!= v.MemberEnd(); ++itr)
435             {
436                 std::string keyName = itr->name.GetString();
437
438                 if(keyName == OC::Key::URIKEY)
439                 {
440                     rep.setUri(boost::get<std::string>(parseAttributeValue(itr->value)));
441                 }
442                 else if (keyName == OC::Key::PROPERTYKEY)
443                 {
444                     for(CMI itr2 = itr->value.MemberBegin();
445                             itr->value.MemberEnd()!=itr2;
446                             ++itr2)
447                     {
448                         if(keyName == OC::Key::RESOURCETYPESKEY)
449                         {
450                             rep.setResourceTypes(
451                                     boost::get<std::vector<std::string>>(
452                                         parseAttributeValue(itr->value)));
453                         }
454                         else if(keyName == OC::Key::PROPERTYKEY)
455                         {
456                             rep.setResourceInterfaces(
457                                     boost::get<std::vector<std::string>>(
458                                         parseAttributeValue(itr->value)));
459                         }
460                     }
461                 }
462                 else if (keyName == OC::Key::REPKEY)
463                 {
464                     for(CMI itr2 = itr->value.MemberBegin();
465                             itr->value.MemberEnd()!=itr2;
466                             ++itr2)
467                     {
468                         rep.setValue(itr2->name.GetString(),
469                                 parseAttributeValue(itr2->value));
470                     }
471                 }
472             }
473
474             return rep;
475         }
476
477         AttributeValue parseAttributeValuePrimitive(const GenericValue& v, typeTag& t)
478         {
479             if(v.IsString())
480             {
481                 t = typeTag::_string;
482                 return std::string(v.GetString());
483             }
484             else if (v.IsNumber())
485             {
486                 if(v.IsDouble())
487                 {
488                     t = typeTag::_double;
489                     return double(v.GetDouble());
490                 }
491                 else if (v.IsInt())
492                 {
493                     t = typeTag::_int;
494                     return int(v.GetInt());
495                 }
496                 else
497                 {
498                     throw OC::OCException(OC::Exception::INVALID_JSON_NUMERIC
499                             + std::to_string(v.GetType()));
500                 }
501             }
502             else if(v.IsBool_())
503             {
504                 t=typeTag::_bool;
505                 return bool(v.GetBool_());
506             }
507             else if(v.IsNull_())
508             {
509                 return OC::NullType();
510             }
511             else
512             {
513                 throw OC::OCException(OC::Exception::INVALID_JSON_TYPE
514                         + std::to_string(v.GetType()));
515             }
516         }
517
518         std::vector<AttributeValue> gatherArrayContents(const GenericValue& v,
519                 const unsigned int curLevel, unsigned int& maxDepth, typeTag& t)
520         {
521             std::vector<AttributeValue> out;
522
523             std::transform(v.Begin(), v.End(), back_inserter(out),
524                     [curLevel, &maxDepth, &t](const GenericValue& x)
525                     {
526                         return parseAttributeValue(x, curLevel, maxDepth, t);
527                     });
528             return out;
529         }
530
531         template<class OutT>
532         struct valueToConcrete
533         {
534             OutT operator()(const AttributeValue& v)
535             {
536                 return boost::get<OutT>(v);
537             }
538
539         };
540
541         template <class OutSeqT>
542         OutSeqT valuesToConcreteVectors(const std::vector<AttributeValue>& vs)
543         {
544             OutSeqT ret;
545
546             std::transform(begin(vs),end(vs), back_inserter(ret),
547                 valueToConcrete<typename OutSeqT::value_type>());
548             return ret;
549         }
550
551         template<class valueType>
552         AttributeValue remapArrayDepth(const unsigned int curLevel,
553                 const std::vector<OC::AttributeValue>& vs)
554         {
555             switch(curLevel)
556             {
557                 default:
558                     throw OC::OCException(OC::Exception::INVALID_JSON_ARRAY_DEPTH);
559                     break;
560                 case 1:
561                     return valuesToConcreteVectors<std::vector<valueType>>(vs);
562                     break;
563                 case 2:
564                     return valuesToConcreteVectors<std::vector<std::vector<valueType>>>(vs);
565                     break;
566                 case 3:
567                     return valuesToConcreteVectors
568                         <std::vector<std::vector<std::vector<valueType>>>>(vs);
569                     break;
570             }
571         }
572
573         AttributeValue convertArrayToConcretes(const typeTag t,
574                 const unsigned int curLevel, const std::vector<OC::AttributeValue>& vs)
575         {
576             // This function converts a std::vector of AttributeValue to a std::vector
577             // of concrete types.  Since we don't use a recursive Variant, we need
578             // to get back to a 'base' primitive type
579             switch(t)
580             {
581                 default:
582                 case typeTag::NOTHING:
583                     throw OC::OCException(OC::Exception::INVALID_JSON_TYPE_TAG);
584                     break;
585                 case typeTag::_string:
586                     return remapArrayDepth<std::string>(curLevel, vs);
587                     break;
588                 case typeTag::_int:
589                     return remapArrayDepth<int>(curLevel, vs);
590                     break;
591                 case typeTag::_double:
592                     return remapArrayDepth<double>(curLevel, vs);
593                     break;
594                 case typeTag::_bool:
595                     return remapArrayDepth<bool>(curLevel, vs);
596                     break;
597                 case typeTag::_representation:
598                     return remapArrayDepth<OCRepresentation>(curLevel, vs);
599                     break;
600             }
601         }
602
603         AttributeValue parseAttributeValueArray(const GenericValue& v,
604                 const unsigned int curLevel, unsigned int& maxDepth, typeTag& t)
605         {
606             const unsigned int max_level = 3;
607
608             if(curLevel > max_level)
609             {
610                 throw OC::OCException(OC::Exception::INVALID_JSON_ARRAY_DEPTH);
611             }
612
613             if(curLevel > maxDepth)
614             {
615                 maxDepth = curLevel;
616             }
617
618             auto arrayItems = gatherArrayContents(v, curLevel, maxDepth, t);
619             const int remapLevel = maxDepth - (curLevel -1);
620             return convertArrayToConcretes(t, remapLevel, arrayItems);
621         }
622     }
623 }
624
625 namespace cereal
626 {
627    void JSONInputArchive::loadAttributeValues(std::map<std::string, OC::AttributeValue>& map)
628    {
629        for(auto&b = itsIteratorStack.back();
630            b.Member && b.itsMemberItEnd != b.itsMemberItBegin+b.itsIndex;
631            ++b)
632        {
633            std::string key = b.itsMemberItBegin[b.itsIndex].name.GetString();
634            const GenericValue& v = itsIteratorStack.back().value();
635            map[key] = OC::detail::parseAttributeValue(v);
636        }
637    }
638 }