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