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