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 <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 unsigned char* payload)
117 setJSONRepresentation(std::string(reinterpret_cast<const char*>(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].empty())
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 OCRepresentation::OCRepresentation()
167 :m_interfaceType(InterfaceType::None)
169 std::string OCRepresentation::getJSONRepresentation() const
176 std::stringstream os;
178 // note: the block is required because cereal closes the JSON string
179 // upon destruction, so the archive needs to be destroyed before accessing
182 OutputArchiveType archive (os);
189 void OCRepresentation::addChild(const OCRepresentation& rep)
191 m_children.push_back(rep);
194 void OCRepresentation::clearChildren()
199 const std::vector<OCRepresentation>& OCRepresentation::getChildren() const
204 void OCRepresentation::setUri(const std::string& uri)
209 std::string OCRepresentation::getUri() const
214 const std::vector<std::string>& OCRepresentation::getResourceTypes() const
216 return m_resourceTypes;
219 void OCRepresentation::setResourceTypes(const std::vector<std::string>& resourceTypes)
221 m_resourceTypes = resourceTypes;
224 const std::vector<std::string>& OCRepresentation::getResourceInterfaces() const
229 void OCRepresentation::setResourceInterfaces(const std::vector<std::string>& resourceInterfaces)
231 m_interfaces = resourceInterfaces;
234 bool OCRepresentation::hasAttribute(const std::string& str) const
236 return m_values.find(str) != m_values.end();
239 bool OCRepresentation::empty() const
241 // This logic is meant to determine whether based on the JSON serialization rules
242 // if this object will result in empty JSON. URI is only serialized if there is valid
243 // data, ResourceType and Interfaces are only serialized if we are a nothing, a
244 // child of a default or link item.
245 // Our values array is only printed in the if we are the child of a Batch resource,
246 // the parent in a 'default' situation, or not in a child/parent relationship.
251 else if ((m_interfaceType == InterfaceType::None
252 || m_interfaceType==InterfaceType::DefaultChild
253 || m_interfaceType==InterfaceType::LinkChild)
254 && (m_resourceTypes.size()>0 || m_interfaces.size()>0))
258 else if((m_interfaceType == InterfaceType::None
259 || m_interfaceType == InterfaceType::BatchChild
260 || m_interfaceType == InterfaceType::DefaultParent)
261 && m_values.size()>0)
266 if(m_children.size() > 0)
274 int OCRepresentation::numberOfAttributes() const
276 return m_values.size();
279 bool OCRepresentation::erase(const std::string& str)
281 return m_values.erase(str);
284 void OCRepresentation::setNULL(const std::string& str)
286 m_values[str] = OC::NullType();
289 bool OCRepresentation::isNULL(const std::string& str) const
291 auto x = m_values.find(str);
293 if(m_values.end() != x)
295 return x->second.which() == AttributeValueNullIndex;
299 throw OCException(OC::Exception::INVALID_ATTRIBUTE+ str);
306 template <class Archive, class Val>
307 void OCRepresentation::optional_load(Archive& ar, Val&& v)
313 catch(cereal::Exception& e)
315 ar.setNextName(nullptr);
316 // Loading a key that doesn't exist results in an exception
317 // Since "Not Found" is a valid condition for us, we swallow
318 // this exception and the archive will not load anything
322 template<class Archive>
323 void OCRepresentation::save(Archive& ar) const
325 // printed for all interface types
328 ar(cereal::make_nvp(Key::URIKEY, m_uri));
331 if((m_interfaceType == InterfaceType::None
332 || m_interfaceType==InterfaceType::DefaultChild
333 || m_interfaceType==InterfaceType::LinkChild)
334 && (m_resourceTypes.size()>0 || m_interfaces.size()>0))
336 // The Prop object requires that it refer to non-const vectors
337 // so that it can alter them in the 'load' case. In the save case
338 // (initiated here) it will not modify the object. So, to keep the
339 // compiler happy, removing the 'const' context here is necessary.
340 const std::vector<std::string>& rt(m_resourceTypes);
341 const std::vector<std::string>& intf(m_interfaces);
342 Prop temp(const_cast<std::vector<std::string>&>(rt),
343 const_cast<std::vector<std::string>&>(intf));
344 ar(cereal::make_nvp(Key::PROPERTYKEY, temp));
347 // printed only for BatchChildren and DefaultParent
348 if((m_interfaceType == InterfaceType::None
349 || m_interfaceType == InterfaceType::BatchChild
350 || m_interfaceType == InterfaceType::DefaultParent)
351 && m_values.size()>0)
353 ar(cereal::make_nvp(Key::REPKEY, m_values));
357 template<class Archive>
358 void OCRepresentation::load(Archive& ar)
360 optional_load(ar, cereal::make_nvp(Key::URIKEY, m_uri));
362 Prop temp(m_resourceTypes, m_interfaces);
363 optional_load(ar, cereal::make_nvp(Key::PROPERTYKEY, temp));
365 optional_load(ar, cereal::make_nvp(Key::REPKEY, m_values));
368 template<class Archive>
369 void OCRepresentation::Prop::save(Archive& ar) const
371 if(m_types.size() > 0)
373 ar(cereal::make_nvp(Key::RESOURCETYPESKEY, m_types));
376 if(m_interfaces.size()>0)
378 ar(cereal::make_nvp(Key::INTERFACESKEY, m_interfaces));
382 template<class Archive>
383 void OCRepresentation::Prop::load(Archive& ar)
385 optional_load(ar, cereal::make_nvp(Key::RESOURCETYPESKEY, m_types));
386 optional_load(ar, cereal::make_nvp(Key::INTERFACESKEY, m_interfaces));
390 // note: the below is used to load an AttributeValue map out of JSON
395 enum class typeTag:uint8_t
405 typedef rapidjson::Document::GenericValue GenericValue;
407 AttributeValue parseAttributeValue(const GenericValue& v);
408 AttributeValue parseAttributeValue(const GenericValue& v,
409 const unsigned int curLevel, unsigned int& maxDepth, typeTag& t);
410 AttributeValue parseAttributeValueObject(const GenericValue& v, typeTag& t);
411 AttributeValue parseAttributeValueArray(const GenericValue& v,
412 const unsigned int curLevel, unsigned int& maxDepth, typeTag& t);
413 AttributeValue parseAttributeValuePrimitive(const GenericValue& v, typeTag& t);
415 AttributeValue parseAttributeValue(const GenericValue& v)
417 // base entrance, start everything at '0'
418 unsigned int max_depth {0};
419 typeTag t {typeTag::NOTHING};
421 return parseAttributeValue(v, 0, max_depth, t);
424 AttributeValue parseAttributeValue(const GenericValue& v,
425 const unsigned int curLevel, unsigned int& maxDepth, typeTag& t)
429 return parseAttributeValueObject(v, t);
433 return parseAttributeValueArray(v, curLevel + 1, maxDepth, t);
437 return parseAttributeValuePrimitive(v,t);
441 AttributeValue parseAttributeValueObject(const GenericValue& v, typeTag& t)
443 typedef rapidjson::Value::ConstMemberIterator CMI;
444 t = typeTag::_representation;
445 OC::OCRepresentation rep;
447 for(CMI itr = v.MemberBegin(); itr!= v.MemberEnd(); ++itr)
449 std::string keyName = itr->name.GetString();
451 if(keyName == OC::Key::URIKEY)
453 rep.setUri(boost::get<std::string>(parseAttributeValue(itr->value)));
455 else if (keyName == OC::Key::PROPERTYKEY)
457 for(CMI itr2 = itr->value.MemberBegin();
458 itr->value.MemberEnd()!=itr2;
461 if(keyName == OC::Key::RESOURCETYPESKEY)
463 rep.setResourceTypes(
464 boost::get<std::vector<std::string>>(
465 parseAttributeValue(itr->value)));
467 else if(keyName == OC::Key::PROPERTYKEY)
469 rep.setResourceInterfaces(
470 boost::get<std::vector<std::string>>(
471 parseAttributeValue(itr->value)));
475 else if (keyName == OC::Key::REPKEY)
477 for(CMI itr2 = itr->value.MemberBegin();
478 itr->value.MemberEnd()!=itr2;
481 rep.setValue(itr2->name.GetString(),
482 parseAttributeValue(itr2->value));
490 AttributeValue parseAttributeValuePrimitive(const GenericValue& v, typeTag& t)
494 t = typeTag::_string;
495 return std::string(v.GetString());
497 else if (v.IsNumber())
501 t = typeTag::_double;
502 return double(v.GetDouble());
507 return int(v.GetInt());
511 throw OC::OCException(OC::Exception::INVALID_JSON_NUMERIC
512 + std::to_string(v.GetType()));
518 return bool(v.GetBool_());
522 return OC::NullType();
526 throw OC::OCException(OC::Exception::INVALID_JSON_TYPE
527 + std::to_string(v.GetType()));
531 std::vector<AttributeValue> gatherArrayContents(const GenericValue& v,
532 const unsigned int curLevel, unsigned int& maxDepth, typeTag& t)
534 std::vector<AttributeValue> out;
536 std::transform(v.Begin(), v.End(), back_inserter(out),
537 [curLevel, &maxDepth, &t](const GenericValue& x)
539 return parseAttributeValue(x, curLevel, maxDepth, t);
545 struct valueToConcrete
547 OutT operator()(const AttributeValue& v)
549 return boost::get<OutT>(v);
554 template <class OutSeqT>
555 OutSeqT valuesToConcreteVectors(const std::vector<AttributeValue>& vs)
559 std::transform(begin(vs),end(vs), back_inserter(ret),
560 valueToConcrete<typename OutSeqT::value_type>());
564 template<class valueType>
565 AttributeValue remapArrayDepth(const unsigned int curLevel,
566 const std::vector<OC::AttributeValue>& vs)
571 throw OC::OCException(OC::Exception::INVALID_JSON_ARRAY_DEPTH);
574 return valuesToConcreteVectors<std::vector<valueType>>(vs);
577 return valuesToConcreteVectors<std::vector<std::vector<valueType>>>(vs);
580 return valuesToConcreteVectors
581 <std::vector<std::vector<std::vector<valueType>>>>(vs);
586 AttributeValue convertArrayToConcretes(const typeTag t,
587 const unsigned int curLevel, const std::vector<OC::AttributeValue>& vs)
589 // This function converts a std::vector of AttributeValue to a std::vector
590 // of concrete types. Since we don't use a recursive Variant, we need
591 // to get back to a 'base' primitive type
595 case typeTag::NOTHING:
596 throw OC::OCException(OC::Exception::INVALID_JSON_TYPE_TAG);
598 case typeTag::_string:
599 return remapArrayDepth<std::string>(curLevel, vs);
602 return remapArrayDepth<int>(curLevel, vs);
604 case typeTag::_double:
605 return remapArrayDepth<double>(curLevel, vs);
608 return remapArrayDepth<bool>(curLevel, vs);
610 case typeTag::_representation:
611 return remapArrayDepth<OCRepresentation>(curLevel, vs);
616 AttributeValue parseAttributeValueArray(const GenericValue& v,
617 const unsigned int curLevel, unsigned int& maxDepth, typeTag& t)
619 const unsigned int max_level = 3;
621 if(curLevel > max_level)
623 throw OC::OCException(OC::Exception::INVALID_JSON_ARRAY_DEPTH);
626 if(curLevel > maxDepth)
631 auto arrayItems = gatherArrayContents(v, curLevel, maxDepth, t);
632 const int remapLevel = maxDepth - (curLevel -1);
633 return convertArrayToConcretes(t, remapLevel, arrayItems);
640 void JSONInputArchive::loadAttributeValues(std::map<std::string, OC::AttributeValue>& map)
642 for(auto&b = itsIteratorStack.back();
643 b.Member && b.itsMemberItEnd != b.itsMemberItBegin+b.itsIndex;
646 std::string key = b.itsMemberItBegin[b.itsIndex].name.GetString();
647 const GenericValue& v = itsIteratorStack.back().value();
648 map[key] = OC::detail::parseAttributeValue(v);