Implement [] and iteration for OCRepresentation
[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 #ifdef __ANDROID__
27 #include "OCAndroid.h"
28 #endif
29
30 #include <OCRepresentation.h>
31
32 #include <boost/lexical_cast.hpp>
33 #include <cereal/cereal.hpp>
34 #include <cereal/types/map.hpp>
35 #include <cereal/types/vector.hpp>
36 #include <cereal/types/utility.hpp>
37 #include <OicJsonSerializer.hpp>
38 #include <algorithm>
39
40 // code needed to serialize a string=>Attribute value map
41 namespace OC
42 {
43     namespace detail
44     {
45         template<class Archive>
46         class WriteAttributeValue : public boost::static_visitor<>
47         {
48             public:
49                 WriteAttributeValue(const std::string& name, Archive& ar)
50                     :m_name(name), m_archive(ar)
51                 {}
52
53                 template<class T>
54                 void operator()(const T& value) const
55                 {
56                     m_archive(cereal::make_nvp(m_name, value));
57                 }
58             private:
59                 std::string m_name;
60                 Archive& m_archive;
61         };
62     }
63 }
64
65 namespace cereal
66 {
67     // take no action when serializing the null type, because the 'save' below
68     // doesn't use the visitor for this type.
69     template <class Archive>
70     void serialize(Archive&, OC::NullType t)
71     {}
72
73     template<class Archive>
74     void save(Archive& ar, const std::map<std::string, OC::AttributeValue>& vals)
75     {
76         for(const auto& kv : vals)
77         {
78             const auto& k = kv.first;
79             const auto& v = kv.second;
80
81             if(v.which() != OC::AttributeValueNullIndex)
82             {
83                 OC::detail::WriteAttributeValue<Archive> writer(k,ar);
84                 boost::apply_visitor(writer, v);
85             }
86             else
87             {
88                 ar.setNextName(k.c_str());
89                 ar.writeName();
90                 ar.saveValue();
91             }
92         }
93     }
94
95     template<class Archive>
96     void load(Archive& ar, std::map<std::string, OC::AttributeValue>& vals)
97     {
98         ar.loadAttributeValues(vals);
99     }
100 }
101
102 namespace OC
103 {
104     typedef cereal::JSONOutputArchive OutputArchiveType;
105     typedef cereal::JSONInputArchive InputArchiveType;
106
107     void MessageContainer::setJSONRepresentation(const std::string& payload)
108     {
109         std::stringstream os(payload);
110         {
111             InputArchiveType archive(os);
112             archive(cereal::make_nvp(OC::Key::OCKEY, m_reps));
113         }
114     }
115
116     void MessageContainer::setJSONRepresentation(const unsigned char* payload)
117     {
118         setJSONRepresentation(std::string(reinterpret_cast<const char*>(payload)));
119     }
120
121     std::string MessageContainer::getJSONRepresentation(OCInfoFormat f) const
122     {
123         std::stringstream os;
124
125         // note: the block is required because cereal closes the JSON string
126         // upon destruction, so the archive needs to be destroyed before accessing
127         // the data
128         {
129             if(f == OCInfoFormat::IncludeOC)
130             {
131                 OutputArchiveType archive(os);
132                 archive(cereal::make_nvp(OC::Key::OCKEY, m_reps));
133             }
134             else if(f== OCInfoFormat::ExcludeOC)
135             {
136                 bool firstPrinted = false;
137                 for(std::vector<OCRepresentation>::size_type i = 0; i< m_reps.size();++i)
138                 {
139                     if(!m_reps[i].emptyData())
140                     {
141                         if(firstPrinted)
142                         {
143                             os<<',';
144                         }
145                         firstPrinted=true;
146                         os << m_reps[i].getJSONRepresentation();
147                     }
148                 }
149             }
150         }
151         return os.str();
152     }
153
154     const std::vector<OCRepresentation>& MessageContainer::representations() const
155     {
156         return m_reps;
157     }
158
159     void MessageContainer::addRepresentation(const OCRepresentation& rep)
160     {
161         m_reps.push_back(rep);
162     }
163 }
164
165 namespace OC
166 {
167     std::string OCRepresentation::getJSONRepresentation() const
168     {
169         if(emptyData())
170         {
171             return "{}";
172         }
173
174         std::stringstream os;
175
176         // note: the block is required because cereal closes the JSON string
177         // upon destruction, so the archive needs to be destroyed before accessing
178         // the data
179         {
180             OutputArchiveType archive (os);
181             save(archive);
182         }
183
184         return os.str();
185     }
186
187     void OCRepresentation::addChild(const OCRepresentation& rep)
188     {
189         m_children.push_back(rep);
190     }
191
192     void OCRepresentation::clearChildren()
193     {
194         m_children.clear();
195     }
196
197     const std::vector<OCRepresentation>& OCRepresentation::getChildren() const
198     {
199         return m_children;
200     }
201
202     void OCRepresentation::setUri(const std::string& uri)
203     {
204         m_uri = uri;
205     }
206
207     std::string OCRepresentation::getUri() const
208     {
209         return m_uri;
210     }
211
212     const std::vector<std::string>& OCRepresentation::getResourceTypes() const
213     {
214         return m_resourceTypes;
215     }
216
217     void OCRepresentation::setResourceTypes(const std::vector<std::string>& resourceTypes)
218     {
219         m_resourceTypes = resourceTypes;
220     }
221
222     const std::vector<std::string>& OCRepresentation::getResourceInterfaces() const
223     {
224         return m_interfaces;
225     }
226
227     void OCRepresentation::setResourceInterfaces(const std::vector<std::string>& resourceInterfaces)
228     {
229         m_interfaces = resourceInterfaces;
230     }
231
232     bool OCRepresentation::hasAttribute(const std::string& str) const
233     {
234         return m_values.find(str) != m_values.end();
235     }
236
237     bool OCRepresentation::emptyData() const
238     {
239         // This logic is meant to determine whether based on the JSON serialization rules
240         // if this object will result in empty JSON.  URI is only serialized if there is valid
241         // data, ResourceType and Interfaces are only serialized if we are a nothing, a
242         // child of a default or link item.
243         // Our values array is only printed in the if we are the child of a Batch resource,
244         // the parent in a 'default' situation, or not in a child/parent relationship.
245         if(!m_uri.empty())
246         {
247             return false;
248         }
249         else if ((m_interfaceType == InterfaceType::None
250                         || m_interfaceType==InterfaceType::DefaultChild
251                         || m_interfaceType==InterfaceType::LinkChild)
252                     && (m_resourceTypes.size()>0 || m_interfaces.size()>0))
253         {
254             return false;
255         }
256         else if((m_interfaceType == InterfaceType::None
257                         || m_interfaceType == InterfaceType::BatchChild
258                         || m_interfaceType == InterfaceType::DefaultParent)
259                     && m_values.size()>0)
260         {
261             return false;
262         }
263
264         if(m_children.size() > 0)
265         {
266             return false;
267         }
268
269         return true;
270     }
271
272     int OCRepresentation::numberOfAttributes() const
273     {
274         return m_values.size();
275     }
276
277     bool OCRepresentation::erase(const std::string& str)
278     {
279         return m_values.erase(str);
280     }
281
282     void OCRepresentation::setNULL(const std::string& str)
283     {
284         m_values[str] = OC::NullType();
285     }
286
287     bool OCRepresentation::isNULL(const std::string& str) const
288     {
289         auto x = m_values.find(str);
290
291         if(m_values.end() != x)
292         {
293             return x->second.which() == AttributeValueNullIndex;
294         }
295         else
296         {
297             throw OCException(OC::Exception::INVALID_ATTRIBUTE+ str);
298         }
299     }
300 }
301
302 namespace OC
303 {
304     template <class Archive, class Val>
305     void OCRepresentation::optional_load(Archive& ar, Val&& v)
306     {
307         try
308         {
309             ar(v);
310         }
311         catch(cereal::Exception& e)
312         {
313             ar.setNextName(nullptr);
314             // Loading a key that doesn't exist results in an exception
315             // Since "Not Found" is a valid condition for us, we swallow
316             // this exception and the archive will not load anything
317         }
318     }
319
320     template<class Archive>
321     void OCRepresentation::save(Archive& ar) const
322     {
323         // printed for all interface types
324         if(!m_uri.empty())
325         {
326             ar(cereal::make_nvp(Key::URIKEY, m_uri));
327         }
328
329         if((m_interfaceType == InterfaceType::None
330                     || m_interfaceType==InterfaceType::DefaultChild
331                     || m_interfaceType==InterfaceType::LinkChild)
332                 && (m_resourceTypes.size()>0 || m_interfaces.size()>0))
333         {
334             // The Prop object requires that it refer to non-const vectors
335             // so that it can alter them in the 'load' case.  In the save case
336             // (initiated here) it will not modify the object.  So, to keep the
337             // compiler happy, removing the 'const' context here is necessary.
338             const std::vector<std::string>& rt(m_resourceTypes);
339             const std::vector<std::string>& intf(m_interfaces);
340             Prop temp(const_cast<std::vector<std::string>&>(rt),
341                     const_cast<std::vector<std::string>&>(intf));
342             ar(cereal::make_nvp(Key::PROPERTYKEY, temp));
343         }
344
345         // printed only for BatchChildren and DefaultParent
346         if((m_interfaceType == InterfaceType::None
347                     || m_interfaceType == InterfaceType::BatchChild
348                     || m_interfaceType == InterfaceType::DefaultParent)
349                 && m_values.size()>0)
350         {
351             ar(cereal::make_nvp(Key::REPKEY, m_values));
352         }
353     }
354
355     template<class Archive>
356     void OCRepresentation::load(Archive& ar)
357     {
358         optional_load(ar, cereal::make_nvp(Key::URIKEY, m_uri));
359         {
360             Prop temp(m_resourceTypes, m_interfaces);
361             optional_load(ar, cereal::make_nvp(Key::PROPERTYKEY, temp));
362         }
363         optional_load(ar, cereal::make_nvp(Key::REPKEY, m_values));
364     }
365
366     template<class Archive>
367     void OCRepresentation::Prop::save(Archive& ar) const
368     {
369         if(m_types.size() > 0)
370         {
371             ar(cereal::make_nvp(Key::RESOURCETYPESKEY, m_types));
372         }
373
374         if(m_interfaces.size()>0)
375         {
376             ar(cereal::make_nvp(Key::INTERFACESKEY, m_interfaces));
377         }
378     }
379
380     template<class Archive>
381     void OCRepresentation::Prop::load(Archive& ar)
382     {
383         optional_load(ar, cereal::make_nvp(Key::RESOURCETYPESKEY, m_types));
384         optional_load(ar, cereal::make_nvp(Key::INTERFACESKEY, m_interfaces));
385     }
386 }
387
388 // note: the below is used to load an AttributeValue map out of JSON
389 namespace OC
390 {
391     namespace detail
392     {
393         enum class typeTag:uint8_t
394         {
395             NOTHING = 0,
396             _string,
397             _int,
398             _double,
399             _bool,
400             _representation
401         };
402
403         typedef rapidjson::Document::GenericValue GenericValue;
404
405         AttributeValue parseAttributeValue(const GenericValue& v);
406         AttributeValue parseAttributeValue(const GenericValue& v,
407                 const unsigned int curLevel, unsigned int& maxDepth, typeTag& t);
408         AttributeValue parseAttributeValueObject(const GenericValue& v, typeTag& t);
409         AttributeValue parseAttributeValueArray(const GenericValue& v,
410                 const unsigned int curLevel, unsigned int& maxDepth, typeTag& t);
411         AttributeValue parseAttributeValuePrimitive(const GenericValue& v, typeTag& t);
412
413         AttributeValue parseAttributeValue(const GenericValue& v)
414         {
415             // base entrance, start everything at '0'
416             unsigned int max_depth {0};
417             typeTag t {typeTag::NOTHING};
418
419             return parseAttributeValue(v, 0, max_depth, t);
420         }
421
422         AttributeValue parseAttributeValue(const GenericValue& v,
423                 const unsigned int curLevel, unsigned int& maxDepth, typeTag& t)
424         {
425             if(v.IsObject())
426             {
427                 return parseAttributeValueObject(v, t);
428             }
429             else if(v.IsArray())
430             {
431                 return parseAttributeValueArray(v, curLevel + 1, maxDepth, t);
432             }
433             else
434             {
435                 return parseAttributeValuePrimitive(v,t);
436             }
437         }
438
439         AttributeValue parseAttributeValueObject(const GenericValue& v, typeTag& t)
440         {
441             typedef rapidjson::Value::ConstMemberIterator CMI;
442             t = typeTag::_representation;
443             OC::OCRepresentation rep;
444
445             for(CMI itr = v.MemberBegin(); itr!= v.MemberEnd(); ++itr)
446             {
447                 std::string keyName = itr->name.GetString();
448
449                 if(keyName == OC::Key::URIKEY)
450                 {
451                     rep.setUri(boost::get<std::string>(parseAttributeValue(itr->value)));
452                 }
453                 else if (keyName == OC::Key::PROPERTYKEY)
454                 {
455                     for(CMI itr2 = itr->value.MemberBegin();
456                             itr->value.MemberEnd()!=itr2;
457                             ++itr2)
458                     {
459                         if(keyName == OC::Key::RESOURCETYPESKEY)
460                         {
461                             rep.setResourceTypes(
462                                     boost::get<std::vector<std::string>>(
463                                         parseAttributeValue(itr->value)));
464                         }
465                         else if(keyName == OC::Key::PROPERTYKEY)
466                         {
467                             rep.setResourceInterfaces(
468                                     boost::get<std::vector<std::string>>(
469                                         parseAttributeValue(itr->value)));
470                         }
471                     }
472                 }
473                 else if (keyName == OC::Key::REPKEY)
474                 {
475                     for(CMI itr2 = itr->value.MemberBegin();
476                             itr->value.MemberEnd()!=itr2;
477                             ++itr2)
478                     {
479                         rep.setValue(itr2->name.GetString(),
480                                 parseAttributeValue(itr2->value));
481                     }
482                 }
483             }
484
485             return rep;
486         }
487
488         AttributeValue parseAttributeValuePrimitive(const GenericValue& v, typeTag& t)
489         {
490             if(v.IsString())
491             {
492                 t = typeTag::_string;
493                 return std::string(v.GetString());
494             }
495             else if (v.IsNumber())
496             {
497                 if(v.IsDouble())
498                 {
499                     t = typeTag::_double;
500                     return double(v.GetDouble());
501                 }
502                 else if (v.IsInt())
503                 {
504                     t = typeTag::_int;
505                     return int(v.GetInt());
506                 }
507                 else
508                 {
509                     throw OC::OCException(OC::Exception::INVALID_JSON_NUMERIC
510                             + std::to_string(v.GetType()));
511                 }
512             }
513             else if(v.IsBool_())
514             {
515                 t=typeTag::_bool;
516                 return bool(v.GetBool_());
517             }
518             else if(v.IsNull_())
519             {
520                 return OC::NullType();
521             }
522             else
523             {
524                 throw OC::OCException(OC::Exception::INVALID_JSON_TYPE
525                         + std::to_string(v.GetType()));
526             }
527         }
528
529         std::vector<AttributeValue> gatherArrayContents(const GenericValue& v,
530                 const unsigned int curLevel, unsigned int& maxDepth, typeTag& t)
531         {
532             std::vector<AttributeValue> out;
533
534             std::transform(v.Begin(), v.End(), back_inserter(out),
535                     [curLevel, &maxDepth, &t](const GenericValue& x)
536                     {
537                         return parseAttributeValue(x, curLevel, maxDepth, t);
538                     });
539             return out;
540         }
541
542         template<class OutT>
543         struct valueToConcrete
544         {
545             OutT operator()(const AttributeValue& v)
546             {
547                 return boost::get<OutT>(v);
548             }
549
550         };
551
552         template <class OutSeqT>
553         OutSeqT valuesToConcreteVectors(const std::vector<AttributeValue>& vs)
554         {
555             OutSeqT ret;
556
557             std::transform(begin(vs),end(vs), back_inserter(ret),
558                 valueToConcrete<typename OutSeqT::value_type>());
559             return ret;
560         }
561
562         template<class valueType>
563         AttributeValue remapArrayDepth(const unsigned int curLevel,
564                 const std::vector<OC::AttributeValue>& vs)
565         {
566             switch(curLevel)
567             {
568                 default:
569                     throw OC::OCException(OC::Exception::INVALID_JSON_ARRAY_DEPTH);
570                     break;
571                 case 1:
572                     return valuesToConcreteVectors<std::vector<valueType>>(vs);
573                     break;
574                 case 2:
575                     return valuesToConcreteVectors<std::vector<std::vector<valueType>>>(vs);
576                     break;
577                 case 3:
578                     return valuesToConcreteVectors
579                         <std::vector<std::vector<std::vector<valueType>>>>(vs);
580                     break;
581             }
582         }
583
584         AttributeValue convertArrayToConcretes(const typeTag t,
585                 const unsigned int curLevel, const std::vector<OC::AttributeValue>& vs)
586         {
587             // This function converts a std::vector of AttributeValue to a std::vector
588             // of concrete types.  Since we don't use a recursive Variant, we need
589             // to get back to a 'base' primitive type
590             switch(t)
591             {
592                 default:
593                 case typeTag::NOTHING:
594                     throw OC::OCException(OC::Exception::INVALID_JSON_TYPE_TAG);
595                     break;
596                 case typeTag::_string:
597                     return remapArrayDepth<std::string>(curLevel, vs);
598                     break;
599                 case typeTag::_int:
600                     return remapArrayDepth<int>(curLevel, vs);
601                     break;
602                 case typeTag::_double:
603                     return remapArrayDepth<double>(curLevel, vs);
604                     break;
605                 case typeTag::_bool:
606                     return remapArrayDepth<bool>(curLevel, vs);
607                     break;
608                 case typeTag::_representation:
609                     return remapArrayDepth<OCRepresentation>(curLevel, vs);
610                     break;
611             }
612         }
613
614         AttributeValue parseAttributeValueArray(const GenericValue& v,
615                 const unsigned int curLevel, unsigned int& maxDepth, typeTag& t)
616         {
617             const unsigned int max_level = 3;
618
619             if(curLevel > max_level)
620             {
621                 throw OC::OCException(OC::Exception::INVALID_JSON_ARRAY_DEPTH);
622             }
623
624             if(curLevel > maxDepth)
625             {
626                 maxDepth = curLevel;
627             }
628
629             auto arrayItems = gatherArrayContents(v, curLevel, maxDepth, t);
630             const int remapLevel = maxDepth - (curLevel -1);
631             return convertArrayToConcretes(t, remapLevel, arrayItems);
632         }
633     }
634 }
635
636 namespace cereal
637 {
638    void JSONInputArchive::loadAttributeValues(std::map<std::string, OC::AttributeValue>& map)
639    {
640        for(auto&b = itsIteratorStack.back();
641            b.Member && b.itsMemberItEnd != b.itsMemberItBegin+b.itsIndex;
642            ++b)
643        {
644            std::string key = b.itsMemberItBegin[b.itsIndex].name.GetString();
645            const GenericValue& v = itsIteratorStack.back().value();
646            map[key] = OC::detail::parseAttributeValue(v);
647        }
648    }
649 }
650
651 namespace OC
652 {
653     std::ostream& operator <<(std::ostream& os, const AttributeType at)
654     {
655         switch(at)
656         {
657             case AttributeType::Null:
658                 os << "Null";
659                 break;
660             case AttributeType::Integer:
661                 os << "Integer";
662                 break;
663             case AttributeType::Double:
664                 os << "Double";
665                 break;
666             case AttributeType::Boolean:
667                 os << "Boolean";
668                 break;
669             case AttributeType::String:
670                 os << "String";
671                 break;
672             case AttributeType::OCRepresentation:
673                 os << "OCRepresentation";
674                 break;
675             case AttributeType::Vector:
676                 os << "Vector";
677                 break;
678         }
679         return os;
680     }
681 }
682
683 // STL Container For OCRepresentation
684 namespace OC
685 {
686     OCRepresentation::AttributeItem::AttributeItem(const std::string& name,
687             std::map<std::string, AttributeValue>& vals):
688             m_attrName(name), m_values(vals){}
689
690     OCRepresentation::AttributeItem OCRepresentation::operator[](const std::string& key)
691     {
692         OCRepresentation::AttributeItem attr{key, m_values};
693         return std::move(attr);
694     }
695
696     const OCRepresentation::AttributeItem OCRepresentation::operator[](const std::string& key) const
697     {
698         OCRepresentation::AttributeItem attr{key, m_values};
699         return std::move(attr);
700     }
701
702     const std::string& OCRepresentation::AttributeItem::attrname() const
703     {
704         return m_attrName;
705     }
706
707     template<typename T, typename = void>
708     struct type_info
709     {
710         // contains the actual type
711         typedef T type;
712         // contains the inner most vector-type
713         typedef T base_type;
714         // contains the AttributeType for this item
715         constexpr static AttributeType enum_type =
716             AttributeTypeConvert<T>::type;
717         // contains the AttributeType for this base-type
718         constexpr static AttributeType enum_base_type =
719             AttributeTypeConvert<T>::type;
720         // depth of the vector
721         constexpr static size_t depth = 0;
722     };
723
724     template<typename T>
725     struct type_info<T, typename std::enable_if<is_vector<T>::value>::type>
726     {
727         typedef T type;
728         typedef typename type_info<typename T::value_type>::base_type base_type;
729         constexpr static AttributeType enum_type = AttributeType::Vector;
730         constexpr static AttributeType enum_base_type =
731             type_info<typename T::value_type>::enum_base_type;
732         constexpr static size_t depth = 1 +
733             type_info<typename T::value_type>::depth;
734     };
735
736     struct type_introspection_visitor : boost::static_visitor<>
737     {
738         AttributeType type;
739         AttributeType base_type;
740         size_t depth;
741
742         type_introspection_visitor() : boost::static_visitor<>(),
743             type(AttributeType::Null), base_type(AttributeType::Null), depth(0){}
744
745         template <typename T>
746         void operator()(T const& item)
747         {
748             type = type_info<T>::enum_type;
749             base_type = type_info<T>::enum_base_type;
750             depth = type_info<T>::depth;
751         }
752     };
753
754     AttributeType OCRepresentation::AttributeItem::type() const
755     {
756         type_introspection_visitor vis;
757         boost::apply_visitor(vis, m_values[m_attrName]);
758         return vis.type;
759     }
760
761     AttributeType OCRepresentation::AttributeItem::base_type() const
762     {
763         type_introspection_visitor vis;
764         boost::apply_visitor(vis, m_values[m_attrName]);
765         return vis.base_type;
766     }
767
768     size_t OCRepresentation::AttributeItem::depth() const
769     {
770         type_introspection_visitor vis;
771         boost::apply_visitor(vis, m_values[m_attrName]);
772         return vis.depth;
773     }
774
775     OCRepresentation::iterator OCRepresentation::begin()
776     {
777         return OCRepresentation::iterator(m_values.begin(), m_values);
778     }
779
780     OCRepresentation::const_iterator OCRepresentation::begin() const
781     {
782          return OCRepresentation::const_iterator(m_values.begin(), m_values);
783     }
784
785     OCRepresentation::const_iterator OCRepresentation::cbegin() const
786     {
787         return OCRepresentation::const_iterator(m_values.cbegin(), m_values);
788     }
789
790     OCRepresentation::iterator OCRepresentation::end()
791     {
792         return OCRepresentation::iterator(m_values.end(), m_values);
793     }
794
795     OCRepresentation::const_iterator OCRepresentation::end() const
796     {
797         return OCRepresentation::const_iterator(m_values.end(), m_values);
798     }
799
800     OCRepresentation::const_iterator OCRepresentation::cend() const
801     {
802         return OCRepresentation::const_iterator(m_values.cend(), m_values);
803     }
804
805     size_t OCRepresentation::size() const
806     {
807         return m_values.size();
808     }
809
810     bool OCRepresentation::empty() const
811     {
812         return m_values.empty();
813     }
814
815     bool OCRepresentation::iterator::operator==(const OCRepresentation::iterator& rhs) const
816     {
817         return m_iterator == rhs.m_iterator;
818     }
819
820     bool OCRepresentation::iterator::operator!=(const OCRepresentation::iterator& rhs) const
821     {
822         return m_iterator != rhs.m_iterator;
823     }
824
825     bool OCRepresentation::const_iterator::operator==(
826             const OCRepresentation::const_iterator& rhs) const
827     {
828         return m_iterator == rhs.m_iterator;
829     }
830
831     bool OCRepresentation::const_iterator::operator!=(
832             const OCRepresentation::const_iterator& rhs) const
833     {
834         return m_iterator != rhs.m_iterator;
835     }
836
837     OCRepresentation::iterator::reference OCRepresentation::iterator::operator*()
838     {
839         return m_item;
840     }
841
842     OCRepresentation::const_iterator::const_reference
843         OCRepresentation::const_iterator::operator*() const
844     {
845         return m_item;
846     }
847
848     OCRepresentation::iterator::pointer OCRepresentation::iterator::operator->()
849     {
850         return &m_item;
851     }
852
853     OCRepresentation::const_iterator::const_pointer
854         OCRepresentation::const_iterator::operator->() const
855     {
856         return &m_item;
857     }
858
859     OCRepresentation::iterator& OCRepresentation::iterator::operator++()
860     {
861         m_iterator++;
862         if(m_iterator != m_item.m_values.end())
863         {
864             m_item.m_attrName = m_iterator->first;
865         }
866         else
867         {
868             m_item.m_attrName = "";
869         }
870         return *this;
871     }
872
873     OCRepresentation::const_iterator& OCRepresentation::const_iterator::operator++()
874     {
875         m_iterator++;
876         if(m_iterator != m_item.m_values.end())
877         {
878             m_item.m_attrName = m_iterator->first;
879         }
880         else
881         {
882             m_item.m_attrName = "";
883         }
884         return *this;
885     }
886
887     OCRepresentation::iterator OCRepresentation::iterator::operator++(int)
888     {
889         OCRepresentation::iterator itr(*this);
890         ++(*this);
891         return itr;
892     }
893
894     OCRepresentation::const_iterator OCRepresentation::const_iterator::operator++(int)
895     {
896         OCRepresentation::const_iterator itr(*this);
897         ++(*this);
898         return itr;
899     }
900
901     struct to_string_visitor : boost::static_visitor<>
902     {
903         std::string str;
904         template <typename T>
905         void operator()(T const& item)
906         {
907             str = boost::lexical_cast<std::string>(item);
908         }
909
910         template <typename T>
911         void operator()(std::vector<T> const& item)
912         {
913             to_string_visitor vis;
914             std::ostringstream stream;
915             stream << "[";
916
917             for(const auto& i : item)
918             {
919                 vis(i);
920                 stream << vis.str  << " ";
921             }
922             stream << "]";
923             str = stream.str();
924         }
925     };
926
927     template<>
928     void to_string_visitor::operator()(bool const& item)
929     {
930         str = item ? "true" : "false";
931     }
932
933     template<>
934     void to_string_visitor::operator()(std::string const& item)
935     {
936         str = item;
937     }
938
939     template<>
940     void to_string_visitor::operator()(NullType const& item)
941     {
942         str = "(null)";
943     }
944
945     template<>
946     void to_string_visitor::operator()(OCRepresentation const& item)
947     {
948         str = "OC::OCRepresentation";
949     }
950
951     std::string OCRepresentation::getValueToString(const std::string& key) const
952     {
953         auto x = m_values.find(key);
954         if(x != m_values.end())
955         {
956             to_string_visitor vis;
957             boost::apply_visitor(vis, x->second);
958             return std::move(vis.str);
959         }
960
961         return "";
962     }
963
964     std::string OCRepresentation::AttributeItem::getValueToString() const
965     {
966         to_string_visitor vis;
967         boost::apply_visitor(vis, m_values[m_attrName]);
968         return std::move(vis.str);
969     }
970
971     std::ostream& operator<<(std::ostream& os, const OCRepresentation::AttributeItem& ai)
972     {
973         os << ai.getValueToString();
974         return os;
975     }
976 }