1 #ifndef OPENSIM_ABSTRACT_PROPERTY_H_
2 #define OPENSIM_ABSTRACT_PROPERTY_H_
3 /* -------------------------------------------------------------------------- *
4  *                        OpenSim:  AbstractProperty.h                        *
5  * -------------------------------------------------------------------------- *
6  * The OpenSim API is a toolkit for musculoskeletal modeling and simulation.  *
7  * See http://opensim.stanford.edu and the NOTICE file for more information.  *
8  * OpenSim is developed at Stanford University and supported by the US        *
9  * National Institutes of Health (U54 GM072970, R24 HD065690) and by DARPA    *
10  * through the Warrior Web program.                                           *
11  *                                                                            *
12  * Copyright (c) 2005-2017 Stanford University and the Authors                *
13  * Author(s): Frank C. Anderson, Cassidy Kelly, Ajay Seth, Michael A. Sherman *
14  *                                                                            *
15  * Licensed under the Apache License, Version 2.0 (the "License"); you may    *
16  * not use this file except in compliance with the License. You may obtain a  *
17  * copy of the License at http://www.apache.org/licenses/LICENSE-2.0.         *
18  *                                                                            *
19  * Unless required by applicable law or agreed to in writing, software        *
20  * distributed under the License is distributed on an "AS IS" BASIS,          *
21  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   *
22  * See the License for the specific language governing permissions and        *
23  * limitations under the License.                                             *
24  * -------------------------------------------------------------------------- */
25 
26 // INCLUDES
27 #include <assert.h>
28 #include <string>
29 #include <typeinfo>
30 #include "osimCommonDLL.h"
31 #include "Exception.h"
32 #include "SimTKcommon/internal/Xml.h"
33 
34 
35 namespace OpenSim {
36 
37 class Object;
38 template <class T> class Property;
39 
40 //==============================================================================
41 /// Property Exceptions
42 //==============================================================================
43 class InvalidPropertyValue : public Exception {
44 public:
InvalidPropertyValue(const std::string & file,size_t line,const std::string & func,const Object & obj,const std::string & propertyName,const std::string & errorMsg)45     InvalidPropertyValue(const std::string& file,
46         size_t line,
47         const std::string& func,
48         const Object& obj,
49         const std::string& propertyName,
50         const std::string& errorMsg) :
51         Exception(file, line, func, obj) {
52         std::string msg = "Property '" + propertyName;
53         msg += "' has an invalid value.\n(details: " + errorMsg + ").\n";
54         addMessage(msg);
55     }
56 };
57 
58 
59 //==============================================================================
60 //                            ABSTRACT PROPERTY
61 //==============================================================================
62 /** An abstract property is a serializable (name,value) pair, for which we
63 do not know the type of the value. Values may be simple types like int or
64 string, or may be serializable objects derived from the %OpenSim Object class.
65 
66 %AbstractProperty is an abstract base class that provides the functionality
67 common to all properties that does not involve knowledge of the value
68 type. Property\<T> derives from %AbstractProperty to represent properties
69 where the type is known.
70 
71 @see Property, Object
72 
73 @author Cassidy Kelly, Ajay Seth, Michael Sherman
74 **/
75 class OSIMCOMMON_API AbstractProperty {
76 public:
77     // Constructors are protected.
78 
79     /** Require that the number of values n in the value list of this property
80     be in the range aMin <= n <= aMax. */
setAllowableListSize(int aMin,int aMax)81     void setAllowableListSize(int aMin, int aMax)
82     {   assert(0 <= aMin && aMin <= aMax);
83        _minListSize = aMin; _maxListSize = aMax; }
84 
85     /** Require that the number of values n in the value list of this property
86     be exactly n=aNum values. **/
setAllowableListSize(int aNum)87     void setAllowableListSize(int aNum)
88     {   assert(aNum >= 1); _minListSize = _maxListSize = aNum; }
89 
90     // Default copy constructor and copy assignment operator.
91 
92     /** Return all heap space used by this property. **/
~AbstractProperty()93     virtual ~AbstractProperty() {}
94 
95     /** Return a new instance of this concrete property object, containing
96     new copies of this property's values. The new property object is
97     allocated on the heap and it is up to the caller to delete it when done. **/
98     virtual AbstractProperty* clone() const = 0;
99 
100     /** For relatively simple types, return the current value of this property
101     in a string suitable for displaying to a user in the GUI (i.e., this number
102     may be rounded and not an exact representation of the actual value being
103     used). Objects just return something like "(Object)".
104     For `Property`s, This function calls `toStringForDisplay()` with
105     `precision = 6`.**/
106     virtual std::string toString() const = 0;
107 
108     /** For relatively simple types, return the current value of this property
109     in a string suitable for displaying to a user in the GUI (i.e., this number
110     may be rounded and not an exact representation of the actual value being
111     used). Objects just return something like "(Object)". This differs from
112     `toString()` as it has an argument, `precision`, for controlling the number
113     of digits printed to string for floats. If this function is not overridden
114     in a derived class, this function uses `toString()` and the `precision`
115     argument is ignored.
116     For `Property`s, in general, this means that floats will
117     be represented with the number of significant digits denoted by the
118     `precision` argument, and the default formatting of `stringstream`
119     determines whether or not exponential notation is used. **/
toStringForDisplay(const int precision)120     virtual std::string toStringForDisplay(const int precision) const
121     {   return toString(); }
122 
123     /** This returns a string representation of this property's value type
124     which will be the same as T::getClassName() for Object-derived types T, and
125     some reasonably nice name for simple types, including at least "bool",
126     "int", "double", "string", "Vec3", "Vector", and "Transform". **/
127     virtual std::string getTypeName() const = 0;
128 
129     /** Return true if this is an "object property", meaning that its values
130     are all concrete objects of types that ultimately derive from the %OpenSim
131     serializable base class Object. If this returns true then it is safe to
132     call getValueAsObject(). Otherwise this property contains only simple types
133     like "int" or "std::string", and you'll need to know the actual type in
134     order to access the values. **/
135     virtual bool isObjectProperty() const = 0;
136 
137     /** An unnamed property is a one-object property whose name was given as
138     null or as the contained object's type tag. In that case getName() will
139     return the object type tag, and the XML representation will just be the
140     object, with name attribute ignored if there is one. **/
141     virtual bool isUnnamedProperty() const = 0;
142 
143     /** Compare this property with another one; this is primarily used
144     for testing. The properties must be of the identical concrete type, and
145     their names and other base class attributes must
146     be identical (including the comment). If they both have the "use default"
147     flag set then we consider the values identical without looking. Otherwise,
148     we delegate to the concrete property to determine if the values are equal;
149     the meaning is determined by the concrete property depending on its type.
150     Floating point values should be compared to a tolerance, and should be
151     considered equal if both are the same infinity or both are NaN (the latter
152     in contrast to normal IEEE floating point behavior, where NaN!=NaN). **/
equals(const AbstractProperty & other)153     bool equals(const AbstractProperty& other) const
154     {   if (!isSamePropertyClass(other)) return false;
155         if (getName() != other.getName()) return false;
156         if (getComment() != other.getComment()) return false;
157         if (getMinListSize() != other.getMinListSize()) return false;
158         if (getMaxListSize() != other.getMaxListSize()) return false;
159 
160         if (size() != other.size()) return false;
161         // Note: we're delegating comparison of the "use default" flags
162         // because only the new Property system copies them correctly, so
163         // only it should compare them.
164         return isEqualTo(other); // delegate to concrete property
165     }
166 
167     /** Return true if the \a other property is an object of exactly the same
168     concrete class as this one. **/
isSamePropertyClass(const AbstractProperty & other)169     bool isSamePropertyClass(const AbstractProperty& other) const
170     {   return typeid(*this) == typeid(other); }
171 
172 
173     #ifndef SWIG
174     /** See the equals() method for the meaning of this operator. **/
175     bool operator==(const AbstractProperty& other) const
176     {   return equals(other); }
177     #endif
178 
179     /**@name                 Container interface
180     A property can be viewed as a random-access container of values. These
181     methods provide a subset of the usual container methods modeled after
182     std::vector. Note that any methods involving the actual property value type
183     T must be templatized; they will be delegated to the concrete Property\<T>
184     for resolution. **/
185     /**@{**/
186     /** Return the number of values currently in this property's value list. **/
size()187     int size() const {return getNumValues();}
188     /** Return true if this property's value list is currently empty. **/
empty()189     bool empty() const {return size()==0;}
190     /** Empty the value list for this property; fails if zero is not an
191     allowable size for this property. **/
192     void clear();
193 
194     /** For an object property, the values can be obtained as references to
195     the abstract base class Object from which all the objects derive. If the
196     property can hold a list of values you must provide an index to select
197     the value, otherwise it is optional but if supplied must be 0. This will
198     throw an exception if this is not an object property, that is, if it is
199     a simple property, because its values can't be represented as an Object in
200     that case.
201 
202     @param[in] index    If supplied must be 0 <= index < getNumValues().
203     @returns const reference to the value as an Object
204     @see updValueAsObject(), getValue\<T>() **/
205     virtual const Object& getValueAsObject(int index=-1) const = 0;
206     /** Get writable access to an existing object value. Note that you can't
207     use this to install a different concrete object; see setValueAsObject()
208     if you want to do that.
209     @param[in] index    If supplied must be 0 <= index < getNumValues().
210     @returns writable reference to the value as an Object
211     @see getValueAsObject(), updValue\<T>() **/
212     virtual Object& updValueAsObject(int index=-1) = 0;
213     /** %Set the indicated value element to a new copy of the supplied object.
214     If you already have a heap-allocated object you're willing to give up and
215     want to avoid the extra copy, use adoptValueObject(). **/
216     virtual void setValueAsObject(const Object& obj, int index=-1) = 0;
217     // Implementation of these non-virtual templatized methods must be
218     // deferred until the concrete property declarations are known.
219     // See Object.h.
220 
221     /** Return one of the values in this property as type T; this works only
222     if the underlying concrete property stores type T and if the indexed
223     element is present, otherwise throws an exception. **/
224     template <class T> const T& getValue(int index=-1) const;
225     /** Return a writable reference to one of the values in this property as
226     type T; this works only if the underlying concrete property is actually of
227     type T and the indexed element is present. Otherwise it throws an exception. **/
228     template <class T> T& updValue(int index=-1);
229     /** Append a new value of type T to the end of the list of values currently
230     contained in this property. This works only if the underlying concrete
231     property is of type T, the property holds a variable-length list, and the
232     list isn't already of maximum size.
233     @returns The index assigned to this value in the list. **/
234     template <class T> int appendValue(const T& value);
235 
236     /** Assign (copy) property *that* to this object. */
237     virtual void assign(const AbstractProperty& that) = 0;
238     /**@}**/
239 
240     /** This method sets the "use default" flag for this property and the
241     properties of any objects it contains to the given value. **/
242     void setAllPropertiesUseDefault(bool shouldUseDefault);
243 
244     /** Given an XML parent element expected to contain a value for this
245     property as an immediate child element, find that property element and set
246     the property value from it. If no such property element can be found, the
247     "use default value" attribute of this property will be set on return; that
248     is not an error. However, if the property element is found but is
249     malformed or unsuitable in some way, an exception will be thrown with
250     a message explaining what is wrong. **/
251     void readFromXMLParentElement(SimTK::Xml::Element& parent,
252                                   int                  versionNumber);
253 
254     /** Given an XML parent element, append a single child element representing
255     the serialized form of this property. **/
256     void writeToXMLParentElement(SimTK::Xml::Element& parent) const;
257 
258 
259     /** %Set the property name. **/
setName(const std::string & name)260     void setName(const std::string& name){ _name = name; }
261 
262     /** %Set a user-friendly comment to be associated with property. This will
263     be displayed in XML and in "help" output for %OpenSim Objects. **/
setComment(const std::string & aComment)264     void setComment(const std::string& aComment){ _comment = aComment; }
265 
266     /** %Set flag indicating whether the value of this property was simply
267     taken from a default object and thus should not be written out when
268     serializing. **/
setValueIsDefault(bool isDefault)269     void setValueIsDefault(bool isDefault) { _valueIsDefault = isDefault; }
270 
271     /** Get the property name. **/
getName()272     const std::string& getName() const { return _name; }
273     /** Get the comment associated with this property. **/
getComment()274     const std::string& getComment() const { return _comment; }
275     /** Get the flag indicating whether the current value is just the default
276     value for this property (in which case it doesn't need to be written
277     out). **/
getValueIsDefault()278     bool getValueIsDefault() const { return _valueIsDefault; }
279 
280     /** Get the minimum number of values allowed in this property's value
281     list. Will be zero for optional properties, zero for list properties
282     (unless explicitly changed), and one for one-value properties. **/
getMinListSize()283     int getMinListSize() const { return _minListSize; }
284     /** Get the maximum number of values allowed in this property's value
285     list. Will be unlimited for list properties (unless explicitly changed),
286     and one for optional and one-value properties. **/
getMaxListSize()287     int getMaxListSize() const { return _maxListSize; }
288 
289     /** This is an "optional" property if its value list can contain at most
290     one value. This is the kind of property created by the
291     Object::addOptionalProperty\<T> method, for any type T. **/
isOptionalProperty()292     bool isOptionalProperty() const
293     {   return getMinListSize()==0 && getMaxListSize()==1; }
294 
295     /** This is a "list" property if its value list can contain more than
296     one value. This is the kind of property created by the
297     Object::addListProperty\<T> method, for any type T. **/
isListProperty()298     bool isListProperty() const
299     {   return getMaxListSize()>1; }
300 
301     /** This is a "one-value" property if its value list must always contain
302     exactly one value. This is the kind of property created by the
303     Object::addProperty\<T> method, for any type T. **/
isOneValueProperty()304     bool isOneValueProperty() const
305     {   return getMinListSize()==1 && getMaxListSize()==1; }
306 
307     /** This is a "one-object" property if it is a "one-value" property and
308     it contains an Object-derived value. This is the kind of property created
309     by the Object::addProperty\<T> method when T is a type derived from
310     %OpenSim's Object serializable base class. One-object properties have
311     a special, compact representation in XML. **/
isOneObjectProperty()312     bool isOneObjectProperty() const
313     {   return isOneValueProperty() && isObjectProperty(); }
314 
315 
316 protected:
317     AbstractProperty();
318     AbstractProperty(const std::string& name,
319                      const std::string& comment);
320     AbstractProperty(const AbstractProperty&)            = default;
321     AbstractProperty(AbstractProperty&&)                 = default;
322     AbstractProperty& operator=(const AbstractProperty&) = default;
323     AbstractProperty& operator=(AbstractProperty&&)      = default;
324 
325     // This is the remainder of the interface that a concrete Property class
326     // must implement, hidden from AbstractProperty users.
327     //--------------------------------------------------------------------------
328     /** The base class equals() method will have already done a lot of checking
329     prior to calling this method, including verifying that both values are
330     non-default and that the value lists are the same size; the concrete
331     property need only compare the values.**/
332     virtual bool isEqualTo(const AbstractProperty& other) const = 0;
333 
334     /** Read in a new value for this property from the XML element
335     \a propertyElement. The element is expected to have the form
336     @code
337         <propertyName> value(s) </propertyName>
338     @endcode
339     where the values may be simple (like int or double) or may be objects
340     contained in child elements for which the object type name serves as
341     the element tag. Note that although the XML file may contain an abbreviated
342     representation for one-object properties, it will have been canonicalized
343     into the above form for the purpose of reading, so concrete properties may
344     assume the above form always.
345 
346     The format for the property value (and any of its contained objects) is
347     assumed to be the one that was in use when the given ".osim" file version
348     number was current; if necessary the in-memory version will be updated to
349     the now-current format.
350 
351     If this is an object property, the contained objects will be asked
352     recursively to read themselves in from the same document. However, any
353     object that has the "file" attribute will read in its contents from that
354     file rather than from the supplied XML document, and the version number
355     will be taken from that file rather than the argument supplied here. **/
356     virtual void readFromXMLElement
357        (SimTK::Xml::Element& propertyElement,
358         int                  versionNumber) = 0;
359 
360     /** Output a serialized representation of this property by writing its
361     value to the given XML property element. If the "use default value"
362     attribute is set for this property (meaning we don't have a meaningful
363     value for it) then you should not call this method unless you are trying
364     to serialize the defaults. Note that this method unconditionally
365     serializes the property; it does not check to see whether it should.
366 
367     This method is not called for the special case of a one-object property,
368     in which case only the object is written to the XML file (without the
369     property element). In all other cases (simple property or property
370     containing an array of objects), the format is
371     @code
372         <propertyName> value(s) </propertyName>
373     @endcode
374     and that is the only format produced here since the empty-valued property
375     element is supplied (with the property name as its tag). **/
376     virtual void writeToXMLElement
377        (SimTK::Xml::Element& propertyElement) const = 0;
378 
379 
380     /** How may values are currently stored in this property? If this is an
381     object property you can use this with getValueAsObject() to iterate over
382     the contained objects. **/
383     virtual int getNumValues() const = 0;
384 
385     /** If the concrete property allows it, clear the value list. **/
386     virtual void clearValues() = 0;
387 
388 
389     /** Return true if the given string is the XML tag name for one of the
390     Object-derived types that is allowed by this property. If so, we expect that
391     an element with that tag could be deserialized into a value element of this
392     property. This always returns false for a simple property. **/
393     virtual bool isAcceptableObjectTag
394        (const std::string& objectTypeTag) const = 0;
395 
396     //--------------------------------------------------------------------------
397 
398 private:
399     void setNull();
400 
401     std::string _name;
402     std::string _comment;
403     bool        _valueIsDefault;    // current value is just the default
404 
405     int         _minListSize;       // minimum # values for property
406     int         _maxListSize;       // maximum # value for property
407 };
408 
409 
410 }; //namespace
411 //=============================================================================
412 //=============================================================================
413 
414 #endif // OPENSIM_ABSTRACT_PROPERTY_H_
415