1 /*
2  * Copyright (c) 2011-2021, The DART development contributors
3  * All rights reserved.
4  *
5  * The list of contributors can be found at:
6  *   https://github.com/dartsim/dart/blob/master/LICENSE
7  *
8  * This file is provided under the following "BSD-style" License:
9  *   Redistribution and use in source and binary forms, with or
10  *   without modification, are permitted provided that the following
11  *   conditions are met:
12  *   * Redistributions of source code must retain the above copyright
13  *     notice, this list of conditions and the following disclaimer.
14  *   * Redistributions in binary form must reproduce the above
15  *     copyright notice, this list of conditions and the following
16  *     disclaimer in the documentation and/or other materials provided
17  *     with the distribution.
18  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
19  *   CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
20  *   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
21  *   MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22  *   DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
23  *   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
26  *   USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27  *   AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  *   LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29  *   ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  *   POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 #include <cassert>
34 #include <iostream>
35 
36 #include "dart/common/Composite.hpp"
37 #include "dart/common/Console.hpp"
38 
39 namespace dart {
40 namespace common {
41 
42 //==============================================================================
43 /// Type maps are std::map containers which map an object's Type Info to some
44 /// instance or trait of that type. For example, an ObjectMap will map an
45 /// object's type to a std::unique_ptr of an instance of that object. a StateMap
46 /// will map an Object's type to a std::unique_ptr of a State instance for that
47 /// Object. Type maps are used for the dart::common::Aspect class.
48 ///
49 /// This function will move data from an Object instance into a container where
50 /// the data is sorted by the Object type that it belongs to. If the DataMap
51 /// that is being filled with data already has an instance of the data for a
52 /// particular Object type, it will perform a copy instead of a clone to improve
53 /// performance.
54 template <
55     typename ObjectType,
56     class DataType,
57     const DataType* (ObjectType::*getData)() const,
58     typename ObjectMap
59     = std::map<std::type_index, std::unique_ptr<ObjectType> >,
60     typename DataMap = std::map<std::type_index, std::unique_ptr<DataType> > >
extractDataFromObjectTypeMap(DataMap & dataMap,const ObjectMap & objectMap)61 static void extractDataFromObjectTypeMap(
62     DataMap& dataMap, const ObjectMap& objectMap)
63 {
64   // This method allows us to avoid dynamic allocation (cloning) whenever
65   // possible.
66   for (const auto& object : objectMap)
67   {
68     if (nullptr == object.second)
69       continue;
70 
71     const DataType* data = (object.second.get()->*getData)();
72     if (data)
73     {
74       // Attempt to insert a nullptr to see whether this data exists while also
75       // creating an iterator to it if it did not already exist. This allows us
76       // to search for a spot in the data map once, instead of searching the map
77       // to see if the data entry already exists and then searching the map
78       // again in order to insert the entry if it didn't already exist.
79       std::pair<typename DataMap::iterator, bool> insertion
80           = dataMap.insert(typename DataMap::value_type(object.first, nullptr));
81 
82       typename DataMap::iterator& it = insertion.first;
83       const bool existed = !insertion.second;
84 
85       if (existed)
86       {
87         // The entry already existed
88         if (it->second)
89         {
90           // The entry was not a nullptr, so we can do an efficient copy
91           it->second->copy(*data);
92         }
93         else
94         {
95           // The entry was a nullptr, so we need to clone
96           it->second = data->clone();
97         }
98       }
99       else
100       {
101         // The entry did not already exist, so we need to clone
102         it->second = data->clone();
103       }
104     }
105   }
106 }
107 
108 //==============================================================================
109 /// Type maps are std::map containers which map an object's Type Info to some
110 /// instance or trait of that type. For example, an ObjectMap will map an
111 /// object's type to a std::unique_ptr of an instance of that object. a StateMap
112 /// will map an Object's type to a std::unique_ptr of a State instance for that
113 /// Object. Type maps are used for the dart::common::Aspect class.
114 ///
115 /// This function will take a type map of Data and pass its contents into the
116 /// Objects contained in an ObjectMap for each corresponding Object type which
117 /// is available.
118 template <
119     typename ObjectType,
120     class DataType,
121     void (ObjectType::*setData)(const DataType&),
122     typename ObjectMap
123     = std::map<std::type_index, std::unique_ptr<ObjectType> >,
124     typename DataMap = std::map<std::type_index, std::unique_ptr<DataType> > >
setObjectsFromDataTypeMap(ObjectMap & objectMap,const DataMap & dataMap)125 static void setObjectsFromDataTypeMap(
126     ObjectMap& objectMap, const DataMap& dataMap)
127 {
128   typename ObjectMap::iterator objects = objectMap.begin();
129   typename DataMap::const_iterator data = dataMap.begin();
130 
131   while (objectMap.end() != objects && dataMap.end() != data)
132   {
133     if (objects->first == data->first)
134     {
135       ObjectType* object = objects->second.get();
136       if (object && data->second)
137         (object->*setData)(*data->second);
138 
139       ++objects;
140       ++data;
141     }
142     else if (objects->first < data->first)
143     {
144       ++objects;
145     }
146     else
147     {
148       ++data;
149     }
150   }
151 }
152 
153 //==============================================================================
setCompositeState(const State & newStates)154 void Composite::setCompositeState(const State& newStates)
155 {
156   setObjectsFromDataTypeMap<Aspect, Aspect::State, &Aspect::setAspectState>(
157       mAspectMap, newStates.getMap());
158 }
159 
160 //==============================================================================
getCompositeState() const161 Composite::State Composite::getCompositeState() const
162 {
163   State states;
164   copyCompositeStateTo(states);
165 
166   return states;
167 }
168 
169 //==============================================================================
copyCompositeStateTo(State & outgoingStates) const170 void Composite::copyCompositeStateTo(State& outgoingStates) const
171 {
172   auto& states = outgoingStates.getMap();
173   extractDataFromObjectTypeMap<Aspect, Aspect::State, &Aspect::getAspectState>(
174       states, mAspectMap);
175 }
176 
177 //==============================================================================
setCompositeProperties(const Properties & newProperties)178 void Composite::setCompositeProperties(const Properties& newProperties)
179 {
180   setObjectsFromDataTypeMap<
181       Aspect,
182       Aspect::Properties,
183       &Aspect::setAspectProperties>(mAspectMap, newProperties.getMap());
184 }
185 
186 //==============================================================================
getCompositeProperties() const187 Composite::Properties Composite::getCompositeProperties() const
188 {
189   Properties properties;
190   copyCompositePropertiesTo(properties);
191 
192   return properties;
193 }
194 
195 //==============================================================================
copyCompositePropertiesTo(Properties & outgoingProperties) const196 void Composite::copyCompositePropertiesTo(Properties& outgoingProperties) const
197 {
198   auto& properties = outgoingProperties.getMap();
199   extractDataFromObjectTypeMap<
200       Aspect,
201       Aspect::Properties,
202       &Aspect::getAspectProperties>(properties, mAspectMap);
203 }
204 
205 //==============================================================================
duplicateAspects(const Composite * fromComposite)206 void Composite::duplicateAspects(const Composite* fromComposite)
207 {
208   if (nullptr == fromComposite)
209   {
210     dterr << "[Composite::duplicateAspects] You have asked to duplicate the "
211           << "Aspects of a nullptr, which is not allowed!\n";
212     assert(false);
213     return;
214   }
215 
216   if (this == fromComposite)
217     return;
218 
219   const AspectMap& otherMap = fromComposite->mAspectMap;
220 
221   AspectMap::iterator receiving = mAspectMap.begin();
222   AspectMap::const_iterator incoming = otherMap.begin();
223 
224   while (otherMap.end() != incoming)
225   {
226     if (mAspectMap.end() == receiving)
227     {
228       // If we've reached the end of this Composite's AspectMap, then we should
229       // just add each entry
230       _set(incoming->first, incoming->second.get());
231       ++incoming;
232     }
233     else if (receiving->first == incoming->first)
234     {
235       if (incoming->second)
236         _set(incoming->first, incoming->second.get());
237 
238       ++receiving;
239       ++incoming;
240     }
241     else if (receiving->first < incoming->first)
242     {
243       ++receiving;
244     }
245     else
246     {
247       // If this Composite does not have an entry corresponding to the incoming
248       // Aspect, then we must create it
249       _set(incoming->first, incoming->second.get());
250       ++incoming;
251     }
252   }
253 }
254 
255 //==============================================================================
matchAspects(const Composite * otherComposite)256 void Composite::matchAspects(const Composite* otherComposite)
257 {
258   if (nullptr == otherComposite)
259   {
260     dterr << "[Composite::matchAspects] You have asked to match the Aspects "
261           << "of a nullptr, which is not allowed!\n";
262     assert(false);
263     return;
264   }
265 
266   for (auto& aspect : mAspectMap)
267     aspect.second = nullptr;
268 
269   duplicateAspects(otherComposite);
270 }
271 
272 //==============================================================================
addToComposite(Aspect * aspect)273 void Composite::addToComposite(Aspect* aspect)
274 {
275   if (aspect)
276     aspect->setComposite(this);
277 }
278 
279 //==============================================================================
removeFromComposite(Aspect * aspect)280 void Composite::removeFromComposite(Aspect* aspect)
281 {
282   if (aspect)
283     aspect->loseComposite(this);
284 }
285 
286 //==============================================================================
_set(std::type_index type_idx,const Aspect * aspect)287 void Composite::_set(std::type_index type_idx, const Aspect* aspect)
288 {
289   if (aspect)
290   {
291     mAspectMap[type_idx] = aspect->cloneAspect();
292     addToComposite(mAspectMap[type_idx].get());
293   }
294   else
295   {
296     mAspectMap[type_idx] = nullptr;
297   }
298 }
299 
300 //==============================================================================
_set(std::type_index type_idx,std::unique_ptr<Aspect> aspect)301 void Composite::_set(std::type_index type_idx, std::unique_ptr<Aspect> aspect)
302 {
303   mAspectMap[type_idx] = std::move(aspect);
304   addToComposite(mAspectMap[type_idx].get());
305 }
306 
307 } // namespace common
308 } // namespace dart
309