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 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
21 /// @file OCRepresentation.cpp
23 /// @brief This file contains the implementation of classes and its members
24 /// related to OCRepresentation
27 #include "OCAndroid.h"
30 #include <OCRepresentation.h>
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>
40 // code needed to serialize a string=>Attribute value map
45 template<class Archive>
46 class WriteAttributeValue : public boost::static_visitor<>
49 WriteAttributeValue(const std::string& name, Archive& ar)
50 :m_name(name), m_archive(ar)
54 void operator()(const T& value) const
56 m_archive(cereal::make_nvp(m_name, value));
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)
73 template<class Archive>
74 void save(Archive& ar, const std::map<std::string, OC::AttributeValue>& vals)
76 for(const auto& kv : vals)
78 const auto& k = kv.first;
79 const auto& v = kv.second;
81 if(v.which() != OC::AttributeValueNullIndex)
83 OC::detail::WriteAttributeValue<Archive> writer(k,ar);
84 boost::apply_visitor(writer, v);
88 ar.setNextName(k.c_str());
95 template<class Archive>
96 void load(Archive& ar, std::map<std::string, OC::AttributeValue>& vals)
98 ar.loadAttributeValues(vals);
104 typedef cereal::JSONOutputArchive OutputArchiveType;
105 typedef cereal::JSONInputArchive InputArchiveType;
107 void MessageContainer::setJSONRepresentation(const std::string& payload)
109 std::stringstream os(payload);
111 InputArchiveType archive(os);
112 archive(cereal::make_nvp(OC::Key::OCKEY, m_reps));
116 void MessageContainer::setJSONRepresentation(const unsigned char* payload)
118 setJSONRepresentation(std::string(reinterpret_cast<const char*>(payload)));
121 std::string MessageContainer::getJSONRepresentation(OCInfoFormat f) const
123 std::stringstream os;
125 // note: the block is required because cereal closes the JSON string
126 // upon destruction, so the archive needs to be destroyed before accessing
129 if(f == OCInfoFormat::IncludeOC)
131 OutputArchiveType archive(os);
132 archive(cereal::make_nvp(OC::Key::OCKEY, m_reps));
134 else if(f== OCInfoFormat::ExcludeOC)
136 bool firstPrinted = false;
137 for(std::vector<OCRepresentation>::size_type i = 0; i< m_reps.size();++i)
139 if(!m_reps[i].emptyData())
146 os << m_reps[i].getJSONRepresentation();
154 const std::vector<OCRepresentation>& MessageContainer::representations() const
159 void MessageContainer::addRepresentation(const OCRepresentation& rep)
161 m_reps.push_back(rep);
167 std::string OCRepresentation::getJSONRepresentation() const
174 std::stringstream os;
176 // note: the block is required because cereal closes the JSON string
177 // upon destruction, so the archive needs to be destroyed before accessing
180 OutputArchiveType archive (os);
187 void OCRepresentation::addChild(const OCRepresentation& rep)
189 m_children.push_back(rep);
192 void OCRepresentation::clearChildren()
197 const std::vector<OCRepresentation>& OCRepresentation::getChildren() const
202 void OCRepresentation::setUri(const std::string& uri)
207 std::string OCRepresentation::getUri() const
212 const std::vector<std::string>& OCRepresentation::getResourceTypes() const
214 return m_resourceTypes;
217 void OCRepresentation::setResourceTypes(const std::vector<std::string>& resourceTypes)
219 m_resourceTypes = resourceTypes;
222 const std::vector<std::string>& OCRepresentation::getResourceInterfaces() const
227 void OCRepresentation::setResourceInterfaces(const std::vector<std::string>& resourceInterfaces)
229 m_interfaces = resourceInterfaces;
232 bool OCRepresentation::hasAttribute(const std::string& str) const
234 return m_values.find(str) != m_values.end();
237 bool OCRepresentation::emptyData() const
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.
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))
256 else if((m_interfaceType == InterfaceType::None
257 || m_interfaceType == InterfaceType::BatchChild
258 || m_interfaceType == InterfaceType::DefaultParent)
259 && m_values.size()>0)
264 if(m_children.size() > 0)
272 int OCRepresentation::numberOfAttributes() const
274 return m_values.size();
277 bool OCRepresentation::erase(const std::string& str)
279 return m_values.erase(str);
282 void OCRepresentation::setNULL(const std::string& str)
284 m_values[str] = OC::NullType();
287 bool OCRepresentation::isNULL(const std::string& str) const
289 auto x = m_values.find(str);
291 if(m_values.end() != x)
293 return x->second.which() == AttributeValueNullIndex;
297 throw OCException(OC::Exception::INVALID_ATTRIBUTE+ str);
304 template <class Archive, class Val>
305 void OCRepresentation::optional_load(Archive& ar, Val&& v)
311 catch(cereal::Exception& e)
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
320 template<class Archive>
321 void OCRepresentation::save(Archive& ar) const
323 // printed for all interface types
326 ar(cereal::make_nvp(Key::URIKEY, m_uri));
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))
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));
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)
351 ar(cereal::make_nvp(Key::REPKEY, m_values));
355 template<class Archive>
356 void OCRepresentation::load(Archive& ar)
358 optional_load(ar, cereal::make_nvp(Key::URIKEY, m_uri));
360 Prop temp(m_resourceTypes, m_interfaces);
361 optional_load(ar, cereal::make_nvp(Key::PROPERTYKEY, temp));
363 optional_load(ar, cereal::make_nvp(Key::REPKEY, m_values));
366 template<class Archive>
367 void OCRepresentation::Prop::save(Archive& ar) const
369 if(m_types.size() > 0)
371 ar(cereal::make_nvp(Key::RESOURCETYPESKEY, m_types));
374 if(m_interfaces.size()>0)
376 ar(cereal::make_nvp(Key::INTERFACESKEY, m_interfaces));
380 template<class Archive>
381 void OCRepresentation::Prop::load(Archive& ar)
383 optional_load(ar, cereal::make_nvp(Key::RESOURCETYPESKEY, m_types));
384 optional_load(ar, cereal::make_nvp(Key::INTERFACESKEY, m_interfaces));
388 // note: the below is used to load an AttributeValue map out of JSON
393 enum class typeTag:uint8_t
403 typedef rapidjson::Document::GenericValue GenericValue;
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);
413 AttributeValue parseAttributeValue(const GenericValue& v)
415 // base entrance, start everything at '0'
416 unsigned int max_depth {0};
417 typeTag t {typeTag::NOTHING};
419 return parseAttributeValue(v, 0, max_depth, t);
422 AttributeValue parseAttributeValue(const GenericValue& v,
423 const unsigned int curLevel, unsigned int& maxDepth, typeTag& t)
427 return parseAttributeValueObject(v, t);
431 return parseAttributeValueArray(v, curLevel + 1, maxDepth, t);
435 return parseAttributeValuePrimitive(v,t);
439 AttributeValue parseAttributeValueObject(const GenericValue& v, typeTag& t)
441 typedef rapidjson::Value::ConstMemberIterator CMI;
442 t = typeTag::_representation;
443 OC::OCRepresentation rep;
445 for(CMI itr = v.MemberBegin(); itr!= v.MemberEnd(); ++itr)
447 std::string keyName = itr->name.GetString();
449 if(keyName == OC::Key::URIKEY)
451 rep.setUri(boost::get<std::string>(parseAttributeValue(itr->value)));
453 else if (keyName == OC::Key::PROPERTYKEY)
455 for(CMI itr2 = itr->value.MemberBegin();
456 itr->value.MemberEnd()!=itr2;
459 if(keyName == OC::Key::RESOURCETYPESKEY)
461 rep.setResourceTypes(
462 boost::get<std::vector<std::string>>(
463 parseAttributeValue(itr->value)));
465 else if(keyName == OC::Key::PROPERTYKEY)
467 rep.setResourceInterfaces(
468 boost::get<std::vector<std::string>>(
469 parseAttributeValue(itr->value)));
473 else if (keyName == OC::Key::REPKEY)
475 for(CMI itr2 = itr->value.MemberBegin();
476 itr->value.MemberEnd()!=itr2;
479 rep.setValue(itr2->name.GetString(),
480 parseAttributeValue(itr2->value));
488 AttributeValue parseAttributeValuePrimitive(const GenericValue& v, typeTag& t)
492 t = typeTag::_string;
493 return std::string(v.GetString());
495 else if (v.IsNumber())
499 t = typeTag::_double;
500 return double(v.GetDouble());
505 return int(v.GetInt());
509 throw OC::OCException(OC::Exception::INVALID_JSON_NUMERIC
510 + std::to_string(v.GetType()));
516 return bool(v.GetBool_());
520 return OC::NullType();
524 throw OC::OCException(OC::Exception::INVALID_JSON_TYPE
525 + std::to_string(v.GetType()));
529 std::vector<AttributeValue> gatherArrayContents(const GenericValue& v,
530 const unsigned int curLevel, unsigned int& maxDepth, typeTag& t)
532 std::vector<AttributeValue> out;
534 std::transform(v.Begin(), v.End(), back_inserter(out),
535 [curLevel, &maxDepth, &t](const GenericValue& x)
537 return parseAttributeValue(x, curLevel, maxDepth, t);
543 struct valueToConcrete
545 OutT operator()(const AttributeValue& v)
547 return boost::get<OutT>(v);
552 template <class OutSeqT>
553 OutSeqT valuesToConcreteVectors(const std::vector<AttributeValue>& vs)
557 std::transform(begin(vs),end(vs), back_inserter(ret),
558 valueToConcrete<typename OutSeqT::value_type>());
562 template<class valueType>
563 AttributeValue remapArrayDepth(const unsigned int curLevel,
564 const std::vector<OC::AttributeValue>& vs)
569 throw OC::OCException(OC::Exception::INVALID_JSON_ARRAY_DEPTH);
572 return valuesToConcreteVectors<std::vector<valueType>>(vs);
575 return valuesToConcreteVectors<std::vector<std::vector<valueType>>>(vs);
578 return valuesToConcreteVectors
579 <std::vector<std::vector<std::vector<valueType>>>>(vs);
584 AttributeValue convertArrayToConcretes(const typeTag t,
585 const unsigned int curLevel, const std::vector<OC::AttributeValue>& vs)
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
593 case typeTag::NOTHING:
594 throw OC::OCException(OC::Exception::INVALID_JSON_TYPE_TAG);
596 case typeTag::_string:
597 return remapArrayDepth<std::string>(curLevel, vs);
600 return remapArrayDepth<int>(curLevel, vs);
602 case typeTag::_double:
603 return remapArrayDepth<double>(curLevel, vs);
606 return remapArrayDepth<bool>(curLevel, vs);
608 case typeTag::_representation:
609 return remapArrayDepth<OCRepresentation>(curLevel, vs);
614 AttributeValue parseAttributeValueArray(const GenericValue& v,
615 const unsigned int curLevel, unsigned int& maxDepth, typeTag& t)
617 const unsigned int max_level = 3;
619 if(curLevel > max_level)
621 throw OC::OCException(OC::Exception::INVALID_JSON_ARRAY_DEPTH);
624 if(curLevel > maxDepth)
629 auto arrayItems = gatherArrayContents(v, curLevel, maxDepth, t);
630 const int remapLevel = maxDepth - (curLevel -1);
631 return convertArrayToConcretes(t, remapLevel, arrayItems);
638 void JSONInputArchive::loadAttributeValues(std::map<std::string, OC::AttributeValue>& map)
640 for(auto&b = itsIteratorStack.back();
641 b.Member && b.itsMemberItEnd != b.itsMemberItBegin+b.itsIndex;
644 std::string key = b.itsMemberItBegin[b.itsIndex].name.GetString();
645 const GenericValue& v = itsIteratorStack.back().value();
646 map[key] = OC::detail::parseAttributeValue(v);
653 std::ostream& operator <<(std::ostream& os, const AttributeType at)
657 case AttributeType::Null:
660 case AttributeType::Integer:
663 case AttributeType::Double:
666 case AttributeType::Boolean:
669 case AttributeType::String:
672 case AttributeType::OCRepresentation:
673 os << "OCRepresentation";
675 case AttributeType::Vector:
683 // STL Container For OCRepresentation
686 OCRepresentation::AttributeItem::AttributeItem(const std::string& name,
687 std::map<std::string, AttributeValue>& vals):
688 m_attrName(name), m_values(vals){}
690 OCRepresentation::AttributeItem OCRepresentation::operator[](const std::string& key)
692 OCRepresentation::AttributeItem attr{key, m_values};
693 return std::move(attr);
696 const OCRepresentation::AttributeItem OCRepresentation::operator[](const std::string& key) const
698 OCRepresentation::AttributeItem attr{key, m_values};
699 return std::move(attr);
702 const std::string& OCRepresentation::AttributeItem::attrname() const
707 template<typename T, typename = void>
710 // contains the actual type
712 // contains the inner most vector-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;
725 struct type_info<T, typename std::enable_if<is_vector<T>::value>::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;
736 struct type_introspection_visitor : boost::static_visitor<>
739 AttributeType base_type;
742 type_introspection_visitor() : boost::static_visitor<>(),
743 type(AttributeType::Null), base_type(AttributeType::Null), depth(0){}
745 template <typename T>
746 void operator()(T const& item)
748 type = type_info<T>::enum_type;
749 base_type = type_info<T>::enum_base_type;
750 depth = type_info<T>::depth;
754 AttributeType OCRepresentation::AttributeItem::type() const
756 type_introspection_visitor vis;
757 boost::apply_visitor(vis, m_values[m_attrName]);
761 AttributeType OCRepresentation::AttributeItem::base_type() const
763 type_introspection_visitor vis;
764 boost::apply_visitor(vis, m_values[m_attrName]);
765 return vis.base_type;
768 size_t OCRepresentation::AttributeItem::depth() const
770 type_introspection_visitor vis;
771 boost::apply_visitor(vis, m_values[m_attrName]);
775 OCRepresentation::iterator OCRepresentation::begin()
777 return OCRepresentation::iterator(m_values.begin(), m_values);
780 OCRepresentation::const_iterator OCRepresentation::begin() const
782 return OCRepresentation::const_iterator(m_values.begin(), m_values);
785 OCRepresentation::const_iterator OCRepresentation::cbegin() const
787 return OCRepresentation::const_iterator(m_values.cbegin(), m_values);
790 OCRepresentation::iterator OCRepresentation::end()
792 return OCRepresentation::iterator(m_values.end(), m_values);
795 OCRepresentation::const_iterator OCRepresentation::end() const
797 return OCRepresentation::const_iterator(m_values.end(), m_values);
800 OCRepresentation::const_iterator OCRepresentation::cend() const
802 return OCRepresentation::const_iterator(m_values.cend(), m_values);
805 size_t OCRepresentation::size() const
807 return m_values.size();
810 bool OCRepresentation::empty() const
812 return m_values.empty();
815 bool OCRepresentation::iterator::operator==(const OCRepresentation::iterator& rhs) const
817 return m_iterator == rhs.m_iterator;
820 bool OCRepresentation::iterator::operator!=(const OCRepresentation::iterator& rhs) const
822 return m_iterator != rhs.m_iterator;
825 bool OCRepresentation::const_iterator::operator==(
826 const OCRepresentation::const_iterator& rhs) const
828 return m_iterator == rhs.m_iterator;
831 bool OCRepresentation::const_iterator::operator!=(
832 const OCRepresentation::const_iterator& rhs) const
834 return m_iterator != rhs.m_iterator;
837 OCRepresentation::iterator::reference OCRepresentation::iterator::operator*()
842 OCRepresentation::const_iterator::const_reference
843 OCRepresentation::const_iterator::operator*() const
848 OCRepresentation::iterator::pointer OCRepresentation::iterator::operator->()
853 OCRepresentation::const_iterator::const_pointer
854 OCRepresentation::const_iterator::operator->() const
859 OCRepresentation::iterator& OCRepresentation::iterator::operator++()
862 if(m_iterator != m_item.m_values.end())
864 m_item.m_attrName = m_iterator->first;
868 m_item.m_attrName = "";
873 OCRepresentation::const_iterator& OCRepresentation::const_iterator::operator++()
876 if(m_iterator != m_item.m_values.end())
878 m_item.m_attrName = m_iterator->first;
882 m_item.m_attrName = "";
887 OCRepresentation::iterator OCRepresentation::iterator::operator++(int)
889 OCRepresentation::iterator itr(*this);
894 OCRepresentation::const_iterator OCRepresentation::const_iterator::operator++(int)
896 OCRepresentation::const_iterator itr(*this);
901 struct to_string_visitor : boost::static_visitor<>
904 template <typename T>
905 void operator()(T const& item)
907 str = boost::lexical_cast<std::string>(item);
910 template <typename T>
911 void operator()(std::vector<T> const& item)
913 to_string_visitor vis;
914 std::ostringstream stream;
917 for(const auto& i : item)
920 stream << vis.str << " ";
928 void to_string_visitor::operator()(bool const& item)
930 str = item ? "true" : "false";
934 void to_string_visitor::operator()(std::string const& item)
940 void to_string_visitor::operator()(NullType const& item)
946 void to_string_visitor::operator()(OCRepresentation const& item)
948 str = "OC::OCRepresentation";
951 std::string OCRepresentation::getValueToString(const std::string& key) const
953 auto x = m_values.find(key);
954 if(x != m_values.end())
956 to_string_visitor vis;
957 boost::apply_visitor(vis, x->second);
958 return std::move(vis.str);
964 std::string OCRepresentation::AttributeItem::getValueToString() const
966 to_string_visitor vis;
967 boost::apply_visitor(vis, m_values[m_attrName]);
968 return std::move(vis.str);
971 std::ostream& operator<<(std::ostream& os, const OCRepresentation::AttributeItem& ai)
973 os << ai.getValueToString();