1 //******************************************************************
3 // Copyright 2014 Intel Mobile Communications GmbH All Rights Reserved.
5 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
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
11 // http://www.apache.org/licenses/LICENSE-2.0
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.
19 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
24 * This file contains the implementation of classes and its members related
25 * to OCRepresentation.
29 #include <OCRepresentation.h>
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>
39 // code needed to serialize a string=>Attribute value map
44 template<class Archive>
45 class WriteAttributeValue : public boost::static_visitor<>
48 WriteAttributeValue(const std::string& name, Archive& ar)
49 :m_name(name), m_archive(ar)
53 void operator()(const T& value) const
55 m_archive(cereal::make_nvp(m_name, value));
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)
72 template<class Archive>
73 void save(Archive& ar, const std::map<std::string, OC::AttributeValue>& vals)
75 for(const auto& kv : vals)
77 const auto& k = kv.first;
78 const auto& v = kv.second;
80 if(v.which() != OC::AttributeValueNullIndex)
82 OC::detail::WriteAttributeValue<Archive> writer(k,ar);
83 boost::apply_visitor(writer, v);
87 ar.setNextName(k.c_str());
94 template<class Archive>
95 void load(Archive& ar, std::map<std::string, OC::AttributeValue>& vals)
97 ar.loadAttributeValues(vals);
103 typedef cereal::JSONOutputArchive OutputArchiveType;
104 typedef cereal::JSONInputArchive InputArchiveType;
106 void MessageContainer::setJSONRepresentation(const std::string& payload)
108 std::stringstream os(payload);
110 InputArchiveType archive(os);
111 archive(cereal::make_nvp(OC::Key::OCKEY, m_reps));
115 void MessageContainer::setJSONRepresentation(const char* payload)
117 setJSONRepresentation(std::string(payload));
120 std::string MessageContainer::getJSONRepresentation(OCInfoFormat f) const
122 std::stringstream os;
124 // note: the block is required because cereal closes the JSON string
125 // upon destruction, so the archive needs to be destroyed before accessing
128 if(f == OCInfoFormat::IncludeOC)
130 OutputArchiveType archive(os);
131 archive(cereal::make_nvp(OC::Key::OCKEY, m_reps));
133 else if(f== OCInfoFormat::ExcludeOC)
135 bool firstPrinted = false;
136 for(std::vector<OCRepresentation>::size_type i = 0; i< m_reps.size();++i)
138 if(!m_reps[i].emptyData())
145 os << m_reps[i].getJSONRepresentation();
153 const std::vector<OCRepresentation>& MessageContainer::representations() const
158 void MessageContainer::addRepresentation(const OCRepresentation& rep)
160 m_reps.push_back(rep);
166 std::string OCRepresentation::getJSONRepresentation() const
173 std::stringstream os;
175 // note: the block is required because cereal closes the JSON string
176 // upon destruction, so the archive needs to be destroyed before accessing
179 OutputArchiveType archive (os);
186 void OCRepresentation::addChild(const OCRepresentation& rep)
188 m_children.push_back(rep);
191 void OCRepresentation::clearChildren()
196 const std::vector<OCRepresentation>& OCRepresentation::getChildren() const
201 void OCRepresentation::setChildren(const std::vector<OCRepresentation>& children)
203 m_children = children;
206 void OCRepresentation::setUri(const std::string& uri)
211 std::string OCRepresentation::getUri() const
216 const std::vector<std::string>& OCRepresentation::getResourceTypes() const
218 return m_resourceTypes;
221 void OCRepresentation::setResourceTypes(const std::vector<std::string>& resourceTypes)
223 m_resourceTypes = resourceTypes;
226 const std::vector<std::string>& OCRepresentation::getResourceInterfaces() const
231 void OCRepresentation::setResourceInterfaces(const std::vector<std::string>& resourceInterfaces)
233 m_interfaces = resourceInterfaces;
236 bool OCRepresentation::hasAttribute(const std::string& str) const
238 return m_values.find(str) != m_values.end();
241 bool OCRepresentation::emptyData() const
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.
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))
260 else if((m_interfaceType == InterfaceType::None
261 || m_interfaceType == InterfaceType::BatchChild
262 || m_interfaceType == InterfaceType::DefaultParent)
263 && m_values.size()>0)
268 if(m_children.size() > 0)
276 int OCRepresentation::numberOfAttributes() const
278 return m_values.size();
281 bool OCRepresentation::erase(const std::string& str)
283 return m_values.erase(str);
286 void OCRepresentation::setNULL(const std::string& str)
288 m_values[str] = OC::NullType();
291 bool OCRepresentation::isNULL(const std::string& str) const
293 auto x = m_values.find(str);
295 if(m_values.end() != x)
297 return x->second.which() == AttributeValueNullIndex;
301 throw OCException(OC::Exception::INVALID_ATTRIBUTE+ str);
308 template <class Archive, class Val>
309 void OCRepresentation::optional_load(Archive& ar, Val&& v)
315 catch(cereal::Exception&)
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
324 template<class Archive>
325 void OCRepresentation::save(Archive& ar) const
327 // printed for all interface types
330 ar(cereal::make_nvp(Key::URIKEY, m_uri));
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))
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));
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)
355 ar(cereal::make_nvp(Key::REPKEY, m_values));
359 template<class Archive>
360 void OCRepresentation::load(Archive& ar)
362 optional_load(ar, cereal::make_nvp(Key::URIKEY, m_uri));
364 Prop temp(m_resourceTypes, m_interfaces);
365 optional_load(ar, cereal::make_nvp(Key::PROPERTYKEY, temp));
367 optional_load(ar, cereal::make_nvp(Key::REPKEY, m_values));
370 template<class Archive>
371 void OCRepresentation::Prop::save(Archive& ar) const
373 if(m_types.size() > 0)
375 ar(cereal::make_nvp(Key::RESOURCETYPESKEY, m_types));
378 if(m_interfaces.size()>0)
380 ar(cereal::make_nvp(Key::INTERFACESKEY, m_interfaces));
384 template<class Archive>
385 void OCRepresentation::Prop::load(Archive& ar)
387 optional_load(ar, cereal::make_nvp(Key::RESOURCETYPESKEY, m_types));
388 optional_load(ar, cereal::make_nvp(Key::INTERFACESKEY, m_interfaces));
392 // note: the below is used to load an AttributeValue map out of JSON
397 enum class typeTag:uint8_t
407 typedef rapidjson::Document::GenericValue GenericValue;
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);
417 AttributeValue parseAttributeValue(const GenericValue& v)
419 // base entrance, start everything at '0'
420 unsigned int max_depth {0};
421 typeTag t {typeTag::NOTHING};
423 return parseAttributeValue(v, 0, max_depth, t);
426 AttributeValue parseAttributeValue(const GenericValue& v,
427 const unsigned int curLevel, unsigned int& maxDepth, typeTag& t)
431 return parseAttributeValueObject(v, t);
435 return parseAttributeValueArray(v, curLevel + 1, maxDepth, t);
439 return parseAttributeValuePrimitive(v,t);
443 AttributeValue parseAttributeValueObject(const GenericValue& v, typeTag& t)
445 typedef rapidjson::Value::ConstMemberIterator CMI;
446 t = typeTag::_representation;
447 OC::OCRepresentation rep;
449 for(CMI itr = v.MemberBegin(); itr!= v.MemberEnd(); ++itr)
451 std::string keyName = itr->name.GetString();
453 if(keyName == OC::Key::URIKEY)
455 rep.setUri(boost::get<std::string>(parseAttributeValue(itr->value)));
457 else if (keyName == OC::Key::PROPERTYKEY)
459 for(CMI itr2 = itr->value.MemberBegin();
460 itr->value.MemberEnd()!=itr2;
463 if(keyName == OC::Key::RESOURCETYPESKEY)
465 rep.setResourceTypes(
466 boost::get<std::vector<std::string>>(
467 parseAttributeValue(itr->value)));
469 else if(keyName == OC::Key::PROPERTYKEY)
471 rep.setResourceInterfaces(
472 boost::get<std::vector<std::string>>(
473 parseAttributeValue(itr->value)));
477 else if (keyName == OC::Key::REPKEY)
479 for(CMI itr2 = itr->value.MemberBegin();
480 itr->value.MemberEnd()!=itr2;
483 rep.setValue(itr2->name.GetString(),
484 parseAttributeValue(itr2->value));
492 AttributeValue parseAttributeValuePrimitive(const GenericValue& v, typeTag& t)
496 t = typeTag::_string;
497 return std::string(v.GetString());
499 else if (v.IsNumber())
503 t = typeTag::_double;
504 return double(v.GetDouble());
509 return int(v.GetInt());
513 throw OC::OCException(OC::Exception::INVALID_JSON_NUMERIC
514 + std::to_string(v.GetType()));
520 return bool(v.GetBool_());
524 return OC::NullType();
528 throw OC::OCException(OC::Exception::INVALID_JSON_TYPE
529 + std::to_string(v.GetType()));
533 std::vector<AttributeValue> gatherArrayContents(const GenericValue& v,
534 const unsigned int curLevel, unsigned int& maxDepth, typeTag& t)
536 std::vector<AttributeValue> out;
538 std::transform(v.Begin(), v.End(), back_inserter(out),
539 [curLevel, &maxDepth, &t](const GenericValue& x)
541 return parseAttributeValue(x, curLevel, maxDepth, t);
547 struct valueToConcrete
549 OutT operator()(const AttributeValue& v)
551 return boost::get<OutT>(v);
556 template <class OutSeqT>
557 OutSeqT valuesToConcreteVectors(const std::vector<AttributeValue>& vs)
561 std::transform(begin(vs),end(vs), back_inserter(ret),
562 valueToConcrete<typename OutSeqT::value_type>());
566 template<class valueType>
567 AttributeValue remapArrayDepth(const unsigned int curLevel,
568 const std::vector<OC::AttributeValue>& vs)
573 throw OC::OCException(OC::Exception::INVALID_JSON_ARRAY_DEPTH);
576 return valuesToConcreteVectors<std::vector<valueType>>(vs);
579 return valuesToConcreteVectors<std::vector<std::vector<valueType>>>(vs);
582 return valuesToConcreteVectors
583 <std::vector<std::vector<std::vector<valueType>>>>(vs);
588 AttributeValue convertArrayToConcretes(const typeTag t,
589 const unsigned int curLevel, const std::vector<OC::AttributeValue>& vs)
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
597 case typeTag::NOTHING:
598 throw OC::OCException(OC::Exception::INVALID_JSON_TYPE_TAG);
600 case typeTag::_string:
601 return remapArrayDepth<std::string>(curLevel, vs);
604 return remapArrayDepth<int>(curLevel, vs);
606 case typeTag::_double:
607 return remapArrayDepth<double>(curLevel, vs);
610 return remapArrayDepth<bool>(curLevel, vs);
612 case typeTag::_representation:
613 return remapArrayDepth<OCRepresentation>(curLevel, vs);
618 AttributeValue parseAttributeValueArray(const GenericValue& v,
619 const unsigned int curLevel, unsigned int& maxDepth, typeTag& t)
621 const unsigned int max_level = 3;
623 if(curLevel > max_level)
625 throw OC::OCException(OC::Exception::INVALID_JSON_ARRAY_DEPTH);
628 if(curLevel > maxDepth)
633 auto arrayItems = gatherArrayContents(v, curLevel, maxDepth, t);
634 const int remapLevel = maxDepth - (curLevel -1);
635 return convertArrayToConcretes(t, remapLevel, arrayItems);
642 void JSONInputArchive::loadAttributeValues(std::map<std::string, OC::AttributeValue>& map)
644 for(auto&b = itsIteratorStack.back();
645 b.Member && b.itsMemberItEnd != b.itsMemberItBegin+b.itsIndex;
648 std::string key = b.itsMemberItBegin[b.itsIndex].name.GetString();
649 const GenericValue& v = itsIteratorStack.back().value();
650 map[key] = OC::detail::parseAttributeValue(v);
657 std::ostream& operator <<(std::ostream& os, const AttributeType at)
661 case AttributeType::Null:
664 case AttributeType::Integer:
667 case AttributeType::Double:
670 case AttributeType::Boolean:
673 case AttributeType::String:
676 case AttributeType::OCRepresentation:
677 os << "OCRepresentation";
679 case AttributeType::Vector:
687 // STL Container For OCRepresentation
690 OCRepresentation::AttributeItem::AttributeItem(const std::string& name,
691 std::map<std::string, AttributeValue>& vals):
692 m_attrName(name), m_values(vals){}
694 OCRepresentation::AttributeItem OCRepresentation::operator[](const std::string& key)
696 OCRepresentation::AttributeItem attr{key, m_values};
697 return std::move(attr);
700 const OCRepresentation::AttributeItem OCRepresentation::operator[](const std::string& key) const
702 OCRepresentation::AttributeItem attr{key, m_values};
703 return std::move(attr);
706 const std::string& OCRepresentation::AttributeItem::attrname() const
711 template<typename T, typename = void>
714 // contains the actual type
716 // contains the inner most vector-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;
729 struct type_info<T, typename std::enable_if<is_vector<T>::value>::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;
740 struct type_introspection_visitor : boost::static_visitor<>
743 AttributeType base_type;
746 type_introspection_visitor() : boost::static_visitor<>(),
747 type(AttributeType::Null), base_type(AttributeType::Null), depth(0){}
749 template <typename T>
750 void operator()(T const& item)
752 type = type_info<T>::enum_type;
753 base_type = type_info<T>::enum_base_type;
754 depth = type_info<T>::depth;
758 AttributeType OCRepresentation::AttributeItem::type() const
760 type_introspection_visitor vis;
761 boost::apply_visitor(vis, m_values[m_attrName]);
765 AttributeType OCRepresentation::AttributeItem::base_type() const
767 type_introspection_visitor vis;
768 boost::apply_visitor(vis, m_values[m_attrName]);
769 return vis.base_type;
772 size_t OCRepresentation::AttributeItem::depth() const
774 type_introspection_visitor vis;
775 boost::apply_visitor(vis, m_values[m_attrName]);
779 OCRepresentation::iterator OCRepresentation::begin()
781 return OCRepresentation::iterator(m_values.begin(), m_values);
784 OCRepresentation::const_iterator OCRepresentation::begin() const
786 return OCRepresentation::const_iterator(m_values.begin(), m_values);
789 OCRepresentation::const_iterator OCRepresentation::cbegin() const
791 return OCRepresentation::const_iterator(m_values.cbegin(), m_values);
794 OCRepresentation::iterator OCRepresentation::end()
796 return OCRepresentation::iterator(m_values.end(), m_values);
799 OCRepresentation::const_iterator OCRepresentation::end() const
801 return OCRepresentation::const_iterator(m_values.end(), m_values);
804 OCRepresentation::const_iterator OCRepresentation::cend() const
806 return OCRepresentation::const_iterator(m_values.cend(), m_values);
809 size_t OCRepresentation::size() const
811 return m_values.size();
814 bool OCRepresentation::empty() const
816 return m_values.empty();
819 bool OCRepresentation::iterator::operator==(const OCRepresentation::iterator& rhs) const
821 return m_iterator == rhs.m_iterator;
824 bool OCRepresentation::iterator::operator!=(const OCRepresentation::iterator& rhs) const
826 return m_iterator != rhs.m_iterator;
829 bool OCRepresentation::const_iterator::operator==(
830 const OCRepresentation::const_iterator& rhs) const
832 return m_iterator == rhs.m_iterator;
835 bool OCRepresentation::const_iterator::operator!=(
836 const OCRepresentation::const_iterator& rhs) const
838 return m_iterator != rhs.m_iterator;
841 OCRepresentation::iterator::reference OCRepresentation::iterator::operator*()
846 OCRepresentation::const_iterator::const_reference
847 OCRepresentation::const_iterator::operator*() const
852 OCRepresentation::iterator::pointer OCRepresentation::iterator::operator->()
857 OCRepresentation::const_iterator::const_pointer
858 OCRepresentation::const_iterator::operator->() const
863 OCRepresentation::iterator& OCRepresentation::iterator::operator++()
866 if(m_iterator != m_item.m_values.end())
868 m_item.m_attrName = m_iterator->first;
872 m_item.m_attrName = "";
877 OCRepresentation::const_iterator& OCRepresentation::const_iterator::operator++()
880 if(m_iterator != m_item.m_values.end())
882 m_item.m_attrName = m_iterator->first;
886 m_item.m_attrName = "";
891 OCRepresentation::iterator OCRepresentation::iterator::operator++(int)
893 OCRepresentation::iterator itr(*this);
898 OCRepresentation::const_iterator OCRepresentation::const_iterator::operator++(int)
900 OCRepresentation::const_iterator itr(*this);
905 struct to_string_visitor : boost::static_visitor<>
908 template <typename T>
909 void operator()(T const& item)
911 str = boost::lexical_cast<std::string>(item);
914 template <typename T>
915 void operator()(std::vector<T> const& item)
917 to_string_visitor vis;
918 std::ostringstream stream;
921 for(const auto& i : item)
924 stream << vis.str << " ";
932 void to_string_visitor::operator()(bool const& item)
934 str = item ? "true" : "false";
938 void to_string_visitor::operator()(std::string const& item)
944 void to_string_visitor::operator()(NullType const& item)
950 void to_string_visitor::operator()(OCRepresentation const& item)
952 str = "OC::OCRepresentation";
955 std::string OCRepresentation::getValueToString(const std::string& key) const
957 auto x = m_values.find(key);
958 if(x != m_values.end())
960 to_string_visitor vis;
961 boost::apply_visitor(vis, x->second);
962 return std::move(vis.str);
968 std::string OCRepresentation::AttributeItem::getValueToString() const
970 to_string_visitor vis;
971 boost::apply_visitor(vis, m_values[m_attrName]);
972 return std::move(vis.str);
975 std::ostream& operator<<(std::ostream& os, const OCRepresentation::AttributeItem& ai)
977 os << ai.getValueToString();