1 /* -------------------------------------------------------------------------- *
2  *                            OpenSim:  Object.cpp                            *
3  * -------------------------------------------------------------------------- *
4  * The OpenSim API is a toolkit for musculoskeletal modeling and simulation.  *
5  * See http://opensim.stanford.edu and the NOTICE file for more information.  *
6  * OpenSim is developed at Stanford University and supported by the US        *
7  * National Institutes of Health (U54 GM072970, R24 HD065690) and by DARPA    *
8  * through the Warrior Web program.                                           *
9  *                                                                            *
10  * Copyright (c) 2005-2017 Stanford University and the Authors                *
11  * Author(s): Frank C. Anderson                                               *
12  *                                                                            *
13  * Licensed under the Apache License, Version 2.0 (the "License"); you may    *
14  * not use this file except in compliance with the License. You may obtain a  *
15  * copy of the License at http://www.apache.org/licenses/LICENSE-2.0.         *
16  *                                                                            *
17  * Unless required by applicable law or agreed to in writing, software        *
18  * distributed under the License is distributed on an "AS IS" BASIS,          *
19  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   *
20  * See the License for the specific language governing permissions and        *
21  * limitations under the License.                                             *
22  * -------------------------------------------------------------------------- */
23 
24 /* Note: This code was originally developed by Realistic Dynamics Inc.
25  * Author: Frank C. Anderson
26  */
27 
28 
29 //============================================================================
30 // INCLUDES
31 //============================================================================
32 
33 #include "Object.h"
34 #include "XMLDocument.h"
35 #include "Exception.h"
36 #include "Property_Deprecated.h"
37 #include "PropertyTransform.h"
38 #include "IO.h"
39 
40 #include <fstream>
41 
42 using namespace OpenSim;
43 using namespace std;
44 using SimTK::Vec3;
45 using SimTK::Transform;
46 
47 //=============================================================================
48 // STATICS
49 //=============================================================================
50 ArrayPtrs<Object>           Object::_registeredTypes;
51 std::map<string,Object*>    Object::_mapTypesToDefaultObjects;
52 std::map<string,string>     Object::_renamedTypesMap;
53 
54 bool                        Object::_serializeAllDefaults=false;
55 const string                Object::DEFAULT_NAME(ObjectDEFAULT_NAME);
56 int                         Object::_debugLevel = 0;
57 
58 //=============================================================================
59 // CONSTRUCTOR(S)
60 //=============================================================================
61 //_____________________________________________________________________________
62 /**
63  * Destructor.
64  */
~Object()65 Object::~Object()
66 {
67     delete _document;
68 }
69 
70 //_____________________________________________________________________________
71 /**
72  * Default constructor.
73  */
Object()74 Object::Object()
75 {
76     setNull();
77 }
78 
79 //_____________________________________________________________________________
80 /**
81  * Construct an object from file.
82  *
83  * The object is constructed from the root element of the XML document.
84  * The type of object is the tag name of the XML root element.
85  *
86  * @param aFileName File name of the document.
87  */
Object(const string & aFileName,bool aUpdateFromXMLNode)88 Object::Object(const string &aFileName, bool aUpdateFromXMLNode)
89 {
90     // INITIALIZATION
91     setNull();
92 
93     // CREATE DOCUMENT
94     // Check file exists before trying to parse it. Is there a faster way to do this?
95     // This maybe slower than we like but definitely faster than
96     // going all the way down to the parser to throw an exception for null document!
97     // -Ayman 8/06
98     OPENSIM_THROW_IF(aFileName.empty(), Exception,
99         getClassName() +
100         ": Cannot construct from empty filename. No filename specified.");
101 
102     OPENSIM_THROW_IF(!ifstream(aFileName.c_str(), ios_base::in).good(),
103         Exception,
104         getClassName() + ": Cannot open file " + aFileName +
105         ". It may not exist or you do not have permission to read it.");
106 
107     _document = new XMLDocument(aFileName);
108 
109     // GET DOCUMENT ELEMENT
110     SimTK::Xml::Element myNode =  _document->getRootDataElement(); //either actual root or node after OpenSimDocument
111 
112     // UPDATE OBJECT
113     // Set current working directory to directory in which we found
114     // the XML document so that contained file names will be interpreted
115     // relative to that directory. Make sure we switch back properly in case
116     // of an exception.
117     if (aUpdateFromXMLNode) {
118         const string saveWorkingDirectory = IO::getCwd();
119         const string directoryOfXMLFile = IO::getParentDirectory(aFileName);
120         IO::chDir(directoryOfXMLFile);
121         try {
122             updateFromXMLNode(myNode, _document->getDocumentVersion());
123         } catch (...) {
124             IO::chDir(saveWorkingDirectory);
125             throw; // re-issue the exception
126         }
127         IO::chDir(saveWorkingDirectory);
128     }
129 }
130 //_____________________________________________________________________________
131 /**
132  * Copy constructor.
133  *
134  * Copy constructors for all Object's only copy the non-XML variable
135  * members of the object; that is, the object's DOMnode and XMLDocument
136  * are not copied but set to NULL.  The reason for this is that for the
137  * object and all its derived classes to establish the correct connection
138  * to the XML document nodes, the object would need to reconstruct based
139  * on the XML document not the values of the object's member variables.
140  *
141  * There are three proper ways to generate an XML document for an Object:
142  *
143  * 1) Construction based on XML file (@see Object(const char *aFileName)).
144  * In this case, the XML document is created by parsing the XML file.
145  *
146  * 2) Construction by Object(const XMLDocument *aDocument).
147  * This constructor explicitly requests construction based on an
148  * XML document.  In this way the proper connection between an object's node
149  * and the corresponding node within the XML document is established.
150  * This constructor is a copy constructor of sorts because all essential
151  * Object member variables should be held within the XML document.
152  * The advantage of this style of construction is that nodes
153  * within the XML document, such as comments that may not have any
154  * associated Object member variable, are preserved.
155  *
156  * 3) A call to generateXMLDocument().
157  * This method generates an XML document for the Object from scratch.
158  * Only the essential document nodes are created (that is, nodes that
159  * correspond directly to member variables.).
160  *
161  * @param aObject Object to be copied.
162  * @see Object(const XMLDocument *aDocument)
163  * @see Object(const char *aFileName)
164  * @see generateXMLDocument()
165  */
Object(const Object & aObject)166 Object::Object(const Object &aObject)
167 {
168     setNull();
169 
170     // Use copy assignment operator to copy simple data members and the
171     // property table; XML document is not copied and the new object is
172     // marked "inlined", meaning it is not associated with an XML document.
173     *this = aObject;
174 }
175 
Object(SimTK::Xml::Element & aNode)176 Object::Object(SimTK::Xml::Element& aNode)
177 {
178     setNull();
179     updateFromXMLNode(aNode, -1);
180 }
181 
182 //-----------------------------------------------------------------------------
183 // COPY ASSIGNMENT
184 //-----------------------------------------------------------------------------
185 /**
186  * Assign this object to the values of another.  The XML-associated variable
187  * members are not copied-- the XML nodes and/or document must be generated
188  * anew for a copied object.
189  *
190  * @return Reference to this object.
191  * @see updateXMLNode()
192  */
operator =(const Object & source)193 Object& Object::operator=(const Object& source)
194 {
195     if (&source != this) {
196         _name           = source._name;
197         _description    = source._description;
198         _authors        = source._authors;
199         _references     = source._references;
200         _propertyTable  = source._propertyTable;
201 
202         delete _document; _document = NULL;
203         _inlined = true; // meaning: not associated to an XML document
204     }
205     return *this;
206 }
207 
208 
209  //=============================================================================
210 // CONSTRUCTION METHODS
211 //==============================================================================
212 //_____________________________________________________________________________
213 /**
214  * Set all non-static member variables to their null or default values.
215  */
setNull()216 void Object::setNull()
217 {
218     _propertySet.clear();
219     _propertyTable.clear();
220     _objectIsUpToDate = false;
221 
222     _name = "";
223     _description = "";
224     _authors = "";
225     _references = "";
226 
227     _document = NULL;
228     _inlined = true;
229 }
230 
231 //-----------------------------------------------------------------------------
232 // EQUALITY
233 //-----------------------------------------------------------------------------
234 // Compare the base class mundane data members, and the properties. Concrete
235 // Objects should override this but they must make sure to invoke the base
236 // operator.
operator ==(const Object & other) const237 bool Object::operator==(const Object& other) const
238 {
239     auto printDiff = [](const std::string& name,
240                             const std::string& thisValue,
241                             const std::string& otherValue) {
242         if (Object::getDebugLevel() > 0) {
243             std::cout << "In Object::operator==(), differing " << name << ":\n"
244                 << "left: " << thisValue
245                 << "\nright: " << otherValue << std::endl;
246         }
247 
248     };
249     if (getConcreteClassName()  != other.getConcreteClassName()) {
250         printDiff("ConcreteClassName", getConcreteClassName(),
251                   other.getConcreteClassName());
252         return false;
253     }
254     if (getName()               != other.getName()) {
255         printDiff("name", getName(), other.getName());
256         return false;
257     }
258     if (getDescription()        != other.getDescription()) {
259         printDiff("description", getDescription(), other.getDescription());
260         return false;
261     }
262     if (getAuthors()            != other.getAuthors()) {
263         printDiff("authors", getAuthors(), other.getAuthors());
264         return false;
265     }
266     if (getReferences()         != other.getReferences()) {
267         printDiff("references", getReferences(), other.getReferences());
268         return false;
269     }
270 
271     // Must have the same number of properties, in the same order.
272     const int numProps = getNumProperties();
273     if (other.getNumProperties() != numProps) {
274         printDiff("number of properties", std::to_string(numProps),
275                   std::to_string(other.getNumProperties()));
276         return false;
277     }
278 
279     for (int px = 0; px < numProps; ++px) {
280         const AbstractProperty& myProp    = getPropertyByIndex(px);
281         const AbstractProperty& otherProp = other.getPropertyByIndex(px);
282 
283         if (!myProp.equals(otherProp)) {
284             printDiff("property '" + myProp.getName() + "'",
285                       myProp.toString(), otherProp.toString());
286             return false;
287         }
288     }
289 
290     return true;
291 }
292 
293 //-----------------------------------------------------------------------------
294 // LESS THAN
295 //-----------------------------------------------------------------------------
296 // This Object is less than another if the name of this string is less
297 // than the name of the other Object. TODO: is that a unique ordering?
298 bool Object::
operator <(const Object & other) const299 operator<(const Object& other) const
300 {
301     return getName() < other.getName();
302 }
303 
304 
305 //=============================================================================
306 // GET AND SET
307 //=============================================================================
308 //-----------------------------------------------------------------------------
309 // NAME
310 //-----------------------------------------------------------------------------
311 //_____________________________________________________________________________
312 /**
313  * Set the name of this object.
314  */
315 void Object::
setName(const string & aName)316 setName(const string &aName)
317 {
318     _name = aName;
319 }
320 //_____________________________________________________________________________
321 /**
322  * Get the name of this object.
323  */
324 const string& Object::
getName() const325 getName() const
326 {
327     return(_name);
328 }
329 
330 //_____________________________________________________________________________
331 /**
332  * Wrapper to be used on Java side to display objects in tree.
333  */
334 const string& Object::
toString() const335 toString() const
336 {
337     return(getName());
338 }
339 
340 //-----------------------------------------------------------------------------
341 // DESCRIPTION
342 //-----------------------------------------------------------------------------
343 //_____________________________________________________________________________
344 /**
345  * Set the description of this object.
346  */
347 void Object::
setDescription(const string & aDescrip)348 setDescription(const string &aDescrip)
349 {
350     _description = aDescrip;
351 }
352 //_____________________________________________________________________________
353 /**
354  * Get the description of this object.
355  */
356 const string& Object::
getDescription() const357 getDescription() const
358 {
359     return(_description);
360 }
361 
362 //=============================================================================
363 // PUBLIC PROPERTY ACCESS
364 //=============================================================================
365 // TODO: (sherm 20120315) These currently provide support for the deprecated
366 // PropertySet method of handling properties, not yet fully replaced by the
367 // PropertyTable approach. The interface here hides the fact that there are
368 // to different sets of properties -- instead, it will appear that there is
369 // a single set which will actually be all those from the PropertyTable
370 // followed by all those from the PropertySet, so that the property index of
371 // the first PropertySet property is one larger than that of the last
372 // PropertyTable property. Names will be looked up first in the PropertyTable
373 // and then in the PropertySet.
374 
375 int Object::
getNumProperties() const376 getNumProperties() const {
377     return   _propertyTable.getNumProperties()
378            + _propertySet.getSize(); // TODO: remove
379 }
380 
381 const AbstractProperty& Object::
getPropertyByIndex(int propertyIndex) const382 getPropertyByIndex(int propertyIndex) const {
383     if (!(0 <= propertyIndex && propertyIndex < getNumProperties()))
384         throw Exception("Property index " + SimTK::String(propertyIndex)
385                         + " out of range 0 <= index < "
386                         + SimTK::String(getNumProperties())
387                         + " for Object " + getName());
388 
389     // TODO: remove deprecated code from here ...
390     if (propertyIndex >= _propertyTable.getNumProperties()) {
391         const int setIndex = propertyIndex-_propertyTable.getNumProperties();
392         return *_propertySet.get(setIndex);
393     }
394     // ... to here.
395 
396     return _propertyTable.getAbstractPropertyByIndex(propertyIndex);
397 }
398 
399 AbstractProperty& Object::
updPropertyByIndex(int propertyIndex)400 updPropertyByIndex(int propertyIndex) {
401     if (!(0 <= propertyIndex && propertyIndex < getNumProperties()))
402         throw Exception("Property index " + SimTK::String(propertyIndex)
403                         + " out of range 0 <= index < "
404                         + SimTK::String(getNumProperties())
405                         + " for Object " + getName());
406 
407     // A property is being modified.
408     _objectIsUpToDate = false;
409 
410     // TODO: remove deprecated code from here ...
411     if (propertyIndex >= _propertyTable.getNumProperties()) {
412         const int setIndex = propertyIndex-_propertyTable.getNumProperties();
413         return *_propertySet.get(setIndex);
414     }
415     // ... to here.
416 
417     return _propertyTable.updAbstractPropertyByIndex(propertyIndex);
418 }
419 
420 bool Object::
hasProperty(const std::string & name) const421 hasProperty(const std::string& name) const {
422     if (name.empty())
423         throw OpenSim::Exception
424            ("Object::hasProperty(name): name cannot be empty. For looking up a "
425             "one-object, nameless property by object class name, use the other "
426             " signature hasProperty<T>() with T the expected object type.");
427 
428     if (_propertyTable.hasProperty(name))
429         return true;
430 
431     // TODO: remove deprecated code from here ...
432     if (_propertySet.contains(name))
433         return true;
434     // ... to here.
435 
436     return false;
437 }
438 
439 const AbstractProperty& Object::
getPropertyByName(const std::string & name) const440 getPropertyByName(const std::string& name) const {
441     const AbstractProperty* p = _propertyTable.getPropertyPtr(name);
442     if (p) return *p;
443 
444     // TODO: remove deprecated code from here ...
445     p = _propertySet.contains(name);
446     if (p) return *p;
447     // ... to here.
448 
449     throw Exception("Property '" + name + "' not present in Object "
450                     + getName());
451     return *p; //NOT REACHED
452 }
453 
454 AbstractProperty& Object::
updPropertyByName(const std::string & name)455 updPropertyByName(const std::string& name) {
456     // A property is being modified.
457     _objectIsUpToDate = false;
458 
459     AbstractProperty* p = _propertyTable.updPropertyPtr(name);
460     if (p) return *p;
461 
462     // TODO: remove deprecated code from here ...
463     p = _propertySet.contains(name);
464     if (p) return *p;
465     // ... to here.
466 
467     throw Exception("Property '" + name + "' not present in Object "
468                     + getName());
469     return *p; //NOT REACHED
470 }
471 
472 //=============================================================================
473 // REGISTRATION
474 //=============================================================================
475 //-----------------------------------------------------------------------------
476 // REGISTER TYPE
477 //-----------------------------------------------------------------------------
478 
479 //_____________________________________________________________________________
480 /*
481  * Register a supported object type.  A global list of all supported objects
482  * (i.e., objects derived from Object) is kept mainly for two purposes:
483  *
484  * ---- Object Deserialization ----
485  * Once a type T is registered, that type can be read from XML files
486  * assuming that the type has implemented the following methods:
487  *  1)  copy constructor
488  *  2)  virtual T* clone() const
489  *  3)  static const char* getClassName()
490  *  4)  T& operator=()
491  *
492  * ---- Initialization by Default Object ----
493  * When objects are deserialized, they are constructed based on the registered
494  * type and receive all of the registered type's property values.  These
495  * values are over-ridden only if there is an element within an XML file that
496  * overrides a default element.
497  *
498  * Because this method is static, registration of object types needs to be
499  * done only once per process and an object does not need to be
500  * instantiated to do so.
501  *
502  * This method makes a copy of the specified object.
503  *
504  * @param aObject Object of the type to be registered.  If the type is
505  * already registered, the current object is replaced by a copy of
506  * the specified object.
507  * @see isValidDefault()
508  */
509 /*static*/ void Object::
registerType(const Object & aObject)510 registerType(const Object& aObject)
511 {
512     // GET TYPE
513     const string& type = aObject.getConcreteClassName();
514     if(type.empty()) {
515         printf("Object.registerType: ERR- no type name has been set.\n");
516         return;
517     }
518     if (_debugLevel>=2) {
519         cout << "Object.registerType: " << type << " .\n";
520     }
521 
522     // REPLACE IF A MATCHING TYPE IS ALREADY REGISTERED
523     for(int i=0; i <_registeredTypes.size(); ++i) {
524         Object *object = _registeredTypes.get(i);
525         if(object->getConcreteClassName() == type) {
526             if(_debugLevel>=2) {
527                 cout<<"Object.registerType: replacing registered object of type ";
528                 cout<<type;
529                 cout<<"\n\twith a new default object of the same type."<<endl;
530             }
531             Object* defaultObj = aObject.clone();
532             defaultObj->setName(DEFAULT_NAME);
533             _registeredTypes.set(i,defaultObj);
534             _mapTypesToDefaultObjects[type]= defaultObj;
535             return;
536         }
537     }
538 
539     // REGISTERING FOR THE FIRST TIME -- APPEND
540     Object* defaultObj = aObject.clone();
541     defaultObj->setName(DEFAULT_NAME);
542     _registeredTypes.append(defaultObj);
543     _mapTypesToDefaultObjects[type]= defaultObj;
544 }
545 
546 /*static*/ void Object::
renameType(const std::string & oldTypeName,const std::string & newTypeName)547 renameType(const std::string& oldTypeName, const std::string& newTypeName)
548 {
549     if(oldTypeName == newTypeName)
550         return;
551 
552     std::map<std::string,Object*>::const_iterator p =
553         _mapTypesToDefaultObjects.find(newTypeName);
554 
555     if (p == _mapTypesToDefaultObjects.end())
556         throw OpenSim::Exception(
557             "Object::renameType(): illegal attempt to rename object type "
558             + oldTypeName + " to " + newTypeName + " which is unregistered.",
559             __FILE__, __LINE__);
560 
561     _renamedTypesMap[oldTypeName] = newTypeName;
562 }
563 
564 /*static*/ const Object* Object::
getDefaultInstanceOfType(const std::string & objectTypeTag)565 getDefaultInstanceOfType(const std::string& objectTypeTag) {
566     std::string actualName = objectTypeTag;
567     bool wasRenamed = false; // for a better error message
568 
569     // First apply renames if any.
570 
571     // Avoid an infinite loop if there is a cycle in the rename table.
572     const int MaxRenames = (int)_renamedTypesMap.size();
573     int renameCount = 0;
574     while(true) {
575         std::map<std::string,std::string>::const_iterator newNamep =
576             _renamedTypesMap.find(actualName);
577         if (newNamep == _renamedTypesMap.end())
578             break; // actualName has not been renamed
579 
580         if (++renameCount > MaxRenames) {
581             throw OpenSim::Exception(
582                 "Object::getDefaultInstanceOfType(): cycle in rename table "
583                 "found when looking for '" + objectTypeTag + "'.");
584         }
585 
586         actualName = newNamep->second;
587         wasRenamed = true;
588     }
589 
590     // Look up the "actualName" default object and return it.
591     std::map<std::string,Object*>::const_iterator p =
592         _mapTypesToDefaultObjects.find(actualName);
593     if (p != _mapTypesToDefaultObjects.end())
594         return p->second;
595 
596     // The requested object was not registered. That's OK normally but is
597     // a bug if we went through the rename table since you are only allowed
598     // to rename things to registered objects.
599     if (wasRenamed) {
600         throw OpenSim::Exception(
601             "Object::getDefaultInstanceOfType(): '" + objectTypeTag
602             + "' was renamed to '" + actualName
603             + "' which is not the name of a registered object.");
604     }
605 
606     return NULL;
607 }
608 
609 /*
610  * Create a new instance of the type indicated by objectTypeTag.
611  * The instance is initialized to the default Object of corresponding type.
612  * Note that renaming of old types may occur; the returned object will have
613  * the current type tag.
614  */
615 /*static*/ Object* Object::
newInstanceOfType(const std::string & objectTypeTag)616 newInstanceOfType(const std::string& objectTypeTag)
617 {
618     const Object* defaultObj = getDefaultInstanceOfType(objectTypeTag);
619     if (defaultObj)
620         return defaultObj->clone();
621 
622     cerr << "Object::newInstanceOfType(): object type '" << objectTypeTag
623          << "' is not a registered Object!" << endl;
624 
625     return NULL;
626 }
627 
628 /*
629  * getRegisteredTypenames() is a utility to retrieve all the typenames
630  * registered so far. This is done by traversing the registered objects map,
631  * so only concrete classes are dealt with. The result returned in rTypeNames
632  * should not be cached while more dlls are loaded as they get stale
633  * instead the list should be constructed whenever in doubt.
634  */
635 /*static*/ void Object::
getRegisteredTypenames(Array<std::string> & rTypeNames)636 getRegisteredTypenames(Array<std::string>& rTypeNames)
637 {
638     std::map<string,Object*>::const_iterator p =
639         _mapTypesToDefaultObjects.begin();
640     for (; p != _mapTypesToDefaultObjects.end(); ++p)
641         rTypeNames.append(p->first);
642     // Renamed type names don't appear in the registeredTypes map, unless
643     // they were separately registered.
644 }
645 
646 //=============================================================================
647 // XML
648 //=============================================================================
649 //-----------------------------------------------------------------------------
650 // LOCAL STATIC UTILITY FUNCTIONS
651 //-----------------------------------------------------------------------------
652 template<class T> static void
UpdateFromXMLNodeSimpleProperty(Property_Deprecated * aProperty,SimTK::Xml::Element & aNode,const string & aName)653 UpdateFromXMLNodeSimpleProperty(Property_Deprecated* aProperty,
654                                 SimTK::Xml::Element& aNode,
655                                 const string&        aName)
656 {
657     aProperty->setValueIsDefault(true);
658 
659     SimTK::Xml::element_iterator iter = aNode.element_begin(aName);
660     if (iter == aNode.element_end()) return;    // Not found
661 
662     T value;
663     iter->getValueAs(value); // fails for Nan, infinity, -infinity, true/false
664     aProperty->setValue(value);
665     aProperty->setValueIsDefault(false);
666 }
667 
668 template<class T> static void
UpdateFromXMLNodeArrayProperty(Property_Deprecated * aProperty,SimTK::Xml::Element & aNode,const string & aName)669 UpdateFromXMLNodeArrayProperty(Property_Deprecated* aProperty,
670                                SimTK::Xml::Element& aNode,
671                                const string&        aName)
672 {
673     aProperty->setValueIsDefault(true);
674 
675     SimTK::Xml::element_iterator iter = aNode.element_begin(aName);
676     if (iter == aNode.element_end()) return;    // Not found
677 
678     SimTK::Array_<T> value;
679     iter->getValueAs(value);
680 
681     OpenSim::Array<T> osimValue;
682     osimValue.setSize(value.size());
683     for(unsigned i=0; i< value.size(); i++) osimValue[i]=value[i];
684     aProperty->setValue(osimValue);
685     aProperty->setValueIsDefault(false);
686 }
687 
688 //------------------------------------------------------------------------------
689 // OBJECT XML METHODS
690 //------------------------------------------------------------------------------
691 // Populate this Object from XML element corresponding to an Object Property.
692 // We check for a file="xxx" attribute and read the object from that file
693 // if it is present. Otherwise we punt to updateFromXMLNode() and read the
694 // object directly from the supplied element.
readObjectFromXMLNodeOrFile(SimTK::Xml::Element & objectElement,int versionNumber)695 void Object::readObjectFromXMLNodeOrFile
696    (SimTK::Xml::Element& objectElement,
697     int                  versionNumber)
698 {
699     // If object is from non-inlined, detect it and set attributes
700     // However we need to do that on the finalized object as copying
701     // does not keep track of XML related issues
702     const std::string file =
703         objectElement.getOptionalAttributeValueAs<std::string>("file", "");
704 
705     // otherwise object is described in file and it has root element
706     const bool inlinedObject = (file == "");
707 
708     if (inlinedObject) {
709         // This object comes from the main XML document.
710         updateFromXMLNode(objectElement, versionNumber);
711         return;
712     }
713 
714     // This object specifies an external file from which it should be read.
715 
716     // When including contents from another file it's assumed file path is
717     // relative to the current working directory, which is usually set to be
718     // the directory that contained the top-level XML file.
719     XMLDocument* newDoc=0;
720     try {
721         std::cout << "reading object from file [" << file <<"] cwd ="
722                   << IO::getCwd() << std::endl;
723          newDoc = new XMLDocument(file);
724         _document = newDoc;
725     } catch(const std::exception& ex){
726         std::cout << "failure reading object from file [" << file <<"] cwd ="
727             << IO::getCwd() << "Error:" << ex.what() << std::endl;
728         return;
729     }
730     _inlined=false;
731     SimTK::Xml::Element e = newDoc->getRootDataElement();
732     updateFromXMLNode(e, newDoc->getDocumentVersion());
733 }
734 
735 template<class T> static void
UpdateXMLNodeSimpleProperty(const Property_Deprecated * aProperty,SimTK::Xml::Element & dParentNode,const string & aName)736 UpdateXMLNodeSimpleProperty(const Property_Deprecated*  aProperty,
737                             SimTK::Xml::Element&        dParentNode,
738                             const string&               aName)
739 {
740     const T &value = aProperty->getValue<T>();
741     if(!aProperty->getValueIsDefault()||Object::getSerializeAllDefaults()) {
742         SimTK::Xml::Element elt(aProperty->getName(), value);
743         dParentNode.insertNodeAfter(dParentNode.node_end(), elt);
744     }
745 }
746 
747 template<class T> static void
UpdateXMLNodeArrayProperty(const Property_Deprecated * aProperty,SimTK::Xml::Element & dParentNode,const string & aName)748 UpdateXMLNodeArrayProperty(const Property_Deprecated*   aProperty,
749                            SimTK::Xml::Element&         dParentNode,
750                            const string&                aName)
751 {
752 
753     const Array<T> &value = aProperty->getValueArray<T>();
754 
755     if(!aProperty->getValueIsDefault()||Object::getSerializeAllDefaults()) {
756         SimTK::Xml::Element elt(aProperty->getName(), value);
757         dParentNode.insertNodeAfter(dParentNode.node_end(), elt);
758     }
759 }
760 
761 static void
UpdateXMLNodeVec(const Property_Deprecated * aProperty,SimTK::Xml::Element & dParentNode,const string & aName)762 UpdateXMLNodeVec(const Property_Deprecated*     aProperty,
763                  SimTK::Xml::Element&           dParentNode,
764                  const string&                  aName)
765 {
766     const Array<double> &value = aProperty->getValueArray<double>();
767 
768     if(!aProperty->getValueIsDefault()||Object::getSerializeAllDefaults()) {
769         SimTK::Xml::Element elt(aProperty->getName(), value);
770         dParentNode.insertNodeAfter(dParentNode.node_end(), elt);
771     }
772 
773 }
774 
775 static void
UpdateXMLNodeTransform(const Property_Deprecated * aProperty,SimTK::Xml::Element & dParentNode,const string & aName)776 UpdateXMLNodeTransform(const Property_Deprecated*   aProperty,
777                        SimTK::Xml::Element&         dParentNode,
778                        const string&                aName)
779 {
780 
781     // Get 6 raw numbers into an array and then use those to update the node
782     OpenSim::Array<double> arr(0, 6);
783     ((PropertyTransform *)aProperty)->getRotationsAndTranslationsAsArray6(&arr[0]);
784     if(!aProperty->getValueIsDefault()||Object::getSerializeAllDefaults()) {
785         SimTK::Xml::Element elt(aProperty->getName(), arr);
786         dParentNode.insertNodeAfter(dParentNode.node_end(), elt);
787     }
788 }
789 
790 
791 //-----------------------------------------------------------------------------
792 // UPDATE OBJECT
793 //-----------------------------------------------------------------------------
updateFromXMLNode(SimTK::Xml::Element & aNode,int versionNumber)794 void Object::updateFromXMLNode(SimTK::Xml::Element& aNode, int versionNumber)
795 {
796 try {
797     // NAME
798     const string dName =
799         aNode.getOptionalAttributeValueAs<std::string>("name", "");
800 
801     // Set the name of this object.
802     setName(dName);
803 
804     // UPDATE DEFAULT OBJECTS
805     updateDefaultObjectsFromXMLNode(); // May need to pass in aNode
806 
807     // LOOP THROUGH PROPERTIES
808     for(int i=0; i < _propertyTable.getNumProperties(); ++i) {
809         AbstractProperty& prop = _propertyTable.updAbstractPropertyByIndex(i);
810         prop.readFromXMLParentElement(aNode, versionNumber);
811     }
812 
813     // LOOP THROUGH DEPRECATED PROPERTIES
814     // TODO: get rid of this
815     for(int i=0;i<_propertySet.getSize();i++) {
816 
817         Property_Deprecated* property = _propertySet.get(i);
818 
819         // TYPE
820         Property_Deprecated::PropertyType type = property->getType();
821 
822         // NAME
823         string name = property->getName();
824         SimTK::String valueString;
825         SimTK::String lowerCaseValueString;
826         SimTK::Xml::element_iterator iter;
827         SimTK::Array_<SimTK::String> value;
828         OpenSim::Array<bool> osimValue;
829         // VALUE
830         switch(type) {
831 
832         // Bool
833         case(Property_Deprecated::Bool) :
834             property->setValueIsDefault(true);
835             iter= aNode.element_begin(name);
836             if (iter == aNode.element_end()) break; // Not found
837             iter->getValueAs(valueString); // true/false
838             lowerCaseValueString = valueString.toLower();
839             property->setValue(lowerCaseValueString=="true"?true:false);
840             //UpdateFromXMLNodeSimpleProperty<bool>(property, aNode, name);
841             property->setValueIsDefault(false);
842             break;
843         // Int
844         case(Property_Deprecated::Int) :
845             UpdateFromXMLNodeSimpleProperty<int>(property, aNode, name);
846             break;
847         // Double
848         case(Property_Deprecated::Dbl) :
849             property->setValueIsDefault(true);
850             iter= aNode.element_begin(name);
851             if (iter == aNode.element_end()) continue;  // Not found
852             iter->getValueAs(valueString); // special values
853             lowerCaseValueString = valueString.toLower();
854             if (lowerCaseValueString=="infinity" || lowerCaseValueString=="inf")
855                 property->setValue(SimTK::Infinity);
856             else if (lowerCaseValueString=="-infinity" || lowerCaseValueString=="-inf")
857                 property->setValue(-SimTK::Infinity);
858             else if (lowerCaseValueString=="nan")
859                 property->setValue(SimTK::NaN);
860             else
861                 UpdateFromXMLNodeSimpleProperty<double>(property, aNode, name);
862             property->setValueIsDefault(false);
863             break;
864         // Str
865         case(Property_Deprecated::Str) :
866             UpdateFromXMLNodeSimpleProperty<string>(property, aNode, name);
867             break;
868         // BoolArray
869         case(Property_Deprecated::BoolArray) :
870             // Parse as a String array then map true/false to boolean values
871             property->setValueIsDefault(true);
872             iter = aNode.element_begin(name);
873             if (iter == aNode.element_end()) continue;  // Not found
874             iter->getValueAs(value);
875             //cout << value << endl;
876             osimValue.setSize(value.size());
877             for(unsigned i=0; i< value.size(); i++) osimValue[i]=(value[i]=="true");
878             property->setValue(osimValue);
879             property->setValueIsDefault(false);
880             break;
881         // IntArray
882         case(Property_Deprecated::IntArray) :
883             UpdateFromXMLNodeArrayProperty<int>(property,aNode,name);
884             break;
885         // DblArray
886         case(Property_Deprecated::DblArray) :
887         case(Property_Deprecated::DblVec) :
888         case(Property_Deprecated::Transform) :
889             UpdateFromXMLNodeArrayProperty<double>(property,aNode,name);
890             break;
891         // StrArray
892         case(Property_Deprecated::StrArray) :
893             UpdateFromXMLNodeArrayProperty<string>(property,aNode,name);
894             break;
895 
896         // Obj
897         case(Property_Deprecated::Obj) : {
898             property->setValueIsDefault(true);
899             Object &object = property->getValueObj();
900             SimTK::Xml::element_iterator iter =
901                 aNode.element_begin(object.getConcreteClassName());
902             if (iter == aNode.element_end())
903                 continue;   // No element of this object type found.
904 
905             // If matchName is set, search through elements of this type to find
906             // one that has a "name" attribute that matches the name of this
907             // object.
908             if (property->getMatchName()) {
909                 while(object.getName() !=
910                         iter->getOptionalAttributeValueAs<std::string>("name", dName)
911                       && iter != aNode.element_end())
912                 {
913                     ++iter;
914                 }
915                 if (iter != aNode.element_end())
916                     object.readObjectFromXMLNodeOrFile(*iter, versionNumber);
917                     property->setValueIsDefault(false);
918                 }
919             else {
920                 object.readObjectFromXMLNodeOrFile(*iter, versionNumber);
921                 property->setValueIsDefault(false);
922             }
923             break;
924         }
925 
926         // ObjArray AND ObjPtr (handled very similarly)
927         case(Property_Deprecated::ObjArray) :
928         case(Property_Deprecated::ObjPtr) : {
929             property->setValueIsDefault(true);
930 
931             // FIND THE PROPERTY ELEMENT (in aNode)
932             const SimTK::Xml::element_iterator propElementIter = aNode.element_begin(name);
933             if (propElementIter==aNode.element_end())
934                 break;
935 
936             if(type==Property_Deprecated::ObjArray) {
937                 // CLEAR EXISTING OBJECT ARRAY
938                 // Eran: Moved after elmt check above so that values set by constructor are kept if
939                 // property is not specified in the xml file
940                 property->clearObjArray();
941             }
942 
943             property->setValueIsDefault(false);
944 
945             // LOOP THROUGH PROPERTY ELEMENT'S CHILD ELEMENTS
946             // Each element is expected to be an Object of some type given
947             // by the element's tag.
948             Object *object =NULL;
949             int objectsFound = 0;
950             SimTK::Xml::element_iterator iter = propElementIter->element_begin();
951             while(iter != propElementIter->element_end()){
952                 // Create an Object of the element tag's type.
953                 object = newInstanceOfType(iter->getElementTag());
954                 if (!object) {
955                     std::cerr << "Object type " << iter->getElementTag() << " not recognized"
956                               << std::endl;
957                     iter++;
958                     continue;
959                 }
960                 objectsFound++;
961 
962                 if(type==Property_Deprecated::ObjPtr) {
963                     if(objectsFound > 1){
964                         //throw XMLParsingException("Found multiple objects under "+name+" tag, but expected only one.",objElmt,__FILE__,__LINE__);
965                     }
966                     else{
967                         property->setValue(object);
968                     }
969                 } else {
970                     property->appendValue(object);
971                 }
972                 object->updateFromXMLNode(*iter, versionNumber);
973                 iter++;
974             }
975 
976             break; }
977 
978         // NOT RECOGNIZED
979         default :
980             cout<<"Object.UpdateObject: WARN- unrecognized property type."<<endl;
981             break;
982         }
983     }
984 
985 
986     } catch (const Exception &ex) {
987         // Important to catch exceptions here so we can restore current working directory...
988         // And then we can re-throw the exception
989         throw(ex);
990     }
991 
992 }
993 
994 //-----------------------------------------------------------------------------
995 // UPDATE DEFAULT OBJECTS FROM XML NODE
996 //-----------------------------------------------------------------------------
997 //_____________________________________________________________________________
998 /**
999  * Update the registered default objects based on an object's XML node.
1000  *
1001  * This method looks for an element with a tag name "defaults" and reads
1002  * the objects in that element and registers them using the method
1003  * Object::registerType().
1004  */
1005 void Object::
updateDefaultObjectsFromXMLNode()1006 updateDefaultObjectsFromXMLNode()
1007 {
1008 
1009     // MUST BE ROOT ELEMENT
1010     if(_document==NULL) return;
1011 
1012     // GET DEFAULTS ELEMENT
1013     SimTK::Xml::element_iterator iterDefault =
1014         _document->getRootDataElement().element_begin("defaults");
1015     if (iterDefault==_document->getRootDataElement().element_end() ||
1016         !iterDefault->isValid()) return;    // No defaults, skip over
1017 
1018     if (_document->hasDefaultObjects()) return; // Could be processed by base class, if so skip.
1019 
1020     SimTK::Array_<SimTK::Xml::Element> elts = iterDefault->getAllElements();
1021     for(unsigned it = 0; it < elts.size(); it++) {
1022         SimTK::String stg = elts[it].getElementTag();
1023 
1024         // GET DEFAULT OBJECT
1025         const Object *defaultObject = getDefaultInstanceOfType(stg);
1026         if(defaultObject==NULL) continue;
1027 
1028         // GET ELEMENT
1029         const string& type = defaultObject->getConcreteClassName();
1030         SimTK::Xml::element_iterator iterDefaultType=
1031             iterDefault->element_begin(type);
1032         if(iterDefaultType==iterDefault->element_end()) continue;
1033 
1034         // CONSTRUCT AND REGISTER DEFAULT OBJECT
1035         // Used to call a special copy method that took DOMElement* but
1036         // that ended up causing XML to be parsed twice.  Got rid of that
1037         // copy method! - Eran, Feb/07
1038         Object *object = defaultObject->clone();
1039         object->updateFromXMLNode(*iterDefaultType,
1040                                   _document->getDocumentVersion());
1041         object->setName(DEFAULT_NAME);
1042         registerType(*object);
1043         _document->addDefaultObject(object); // object will be owned by _document
1044     }
1045 }
1046 
1047 //-----------------------------------------------------------------------------
1048 // UPDATE XML NODE
1049 //-----------------------------------------------------------------------------
1050 //_____________________________________________________________________________
1051 
updateXMLNode(SimTK::Xml::Element & aParent,const AbstractProperty * prop) const1052 void Object::updateXMLNode(SimTK::Xml::Element& aParent,
1053                            const AbstractProperty* prop) const
1054 {
1055     // Handle non-inlined object
1056     if(!getInlined()) {
1057         // If object is not inlined we don't want to generate node in original document
1058         // Handle not-inlined objects first.
1059         if (!aParent.isValid()) {
1060             cout<<"Root node must be inlined"<<*this<<endl;
1061         }
1062         else {
1063         // Can we make this more efficient than recreating the node again?
1064         // We can possibly check when setInlined() is invoked if we need to do it or not
1065         // Create a new document and write object to it
1066         string offlineFileName = getDocumentFileName();
1067         if(IO::GetPrintOfflineDocuments()) {
1068             // The problem is that generateChildXMLDocument makes a root which allows print
1069             // to do its job but root is duplicated. If we don't create the node then generateXMLDocument
1070             // is invoked which messes up the whole _childDocument mechanism as _document is overwritten.
1071             _inlined=true;
1072             print(offlineFileName);
1073             _inlined=false;
1074             SimTK::Xml::Element myObjectElement(getConcreteClassName());
1075             myObjectElement.setAttributeValue("file", offlineFileName);
1076             aParent.insertNodeAfter(aParent.node_end(), myObjectElement);
1077         }
1078         /*
1079         if (!_refNode) _refNode = XMLNode::AppendNewElementWithComment(aParent,getType(),getName());
1080         XMLNode::SetAttribute(_refNode,"file",offlineFileName);
1081         XMLNode::RemoveAttribute(_refNode,"name"); // Shouldn't have a name attribute in the reference document
1082         XMLNode::RemoveChildren(_refNode); // Shouldn't have any children in the reference document*/
1083         }
1084         return;
1085     }
1086 
1087     // GENERATE XML NODE for object
1088     SimTK::Xml::Element myObjectElement(getConcreteClassName());
1089 
1090     // if property is provided and it is not of unnamed type, use the property name
1091     if(prop && prop->isOneObjectProperty() && !prop->isUnnamedProperty()) {
1092         myObjectElement.setAttributeValue("name", prop->getName());
1093     } // otherwise if object has a name use it as the name value
1094     else if (!getName().empty()) {
1095         myObjectElement.setAttributeValue("name", getName());
1096     }
1097 
1098     aParent.insertNodeAfter(aParent.node_end(), myObjectElement);
1099 
1100     // DEFAULT OBJECTS
1101     //updateDefaultObjectsXMLNode(aParent);
1102     if (_document) _document->writeDefaultObjects(myObjectElement);
1103 
1104 
1105     // LOOP THROUGH PROPERTIES
1106     bool wroteAnyProperties = false;
1107     for(int i=0; i < _propertyTable.getNumProperties(); ++i) {
1108         const AbstractProperty& prop = _propertyTable.getAbstractPropertyByIndex(i);
1109 
1110         // Don't write out if this is just a default value.
1111         if (!prop.getValueIsDefault() || Object::getSerializeAllDefaults()) {
1112             prop.writeToXMLParentElement(myObjectElement);
1113             wroteAnyProperties = true;
1114         }
1115     }
1116 
1117     // LOOP THROUGH DEPRECATED PROPERTIES
1118     // TODO: get rid of this
1119     for(int i=0;i<_propertySet.getSize();i++) {
1120 
1121         const Property_Deprecated *prop = _propertySet.get(i);
1122         if (prop->getValueIsDefault() && !Object::getSerializeAllDefaults())
1123             continue;
1124 
1125         wroteAnyProperties = true;
1126 
1127         // Add comment if any
1128         if (!prop->getComment().empty())
1129             myObjectElement.insertNodeAfter(myObjectElement.node_end(),
1130                 SimTK::Xml::Comment(prop->getComment()));
1131 
1132         // TYPE
1133         Property_Deprecated::PropertyType type = prop->getType();
1134 
1135         // NAME
1136         string name = prop->getName();
1137 
1138         string stringValue="";
1139         // VALUE
1140         switch(type) {
1141 
1142         // Bool
1143         case(Property_Deprecated::Bool) :
1144             UpdateXMLNodeSimpleProperty<bool>(prop, myObjectElement, name);
1145             break;
1146         // Int
1147         case(Property_Deprecated::Int) :
1148             UpdateXMLNodeSimpleProperty<int>(prop, myObjectElement, name);
1149             break;
1150         // Dbl
1151         case(Property_Deprecated::Dbl) :
1152             if (SimTK::isFinite(prop->getValueDbl()))
1153                 UpdateXMLNodeSimpleProperty<double>(prop, myObjectElement, name);
1154             else {
1155                 if (prop->getValueDbl() == SimTK::Infinity)
1156                     stringValue="Inf";
1157                 else if (prop->getValueDbl() == -SimTK::Infinity)
1158                     stringValue="-Inf";
1159                 else if (SimTK::isNaN(prop->getValueDbl()))
1160                     stringValue="NaN";
1161                 if(!prop->getValueIsDefault()) {
1162                     SimTK::Xml::Element elt(prop->getName(), stringValue);
1163                     myObjectElement.insertNodeAfter(myObjectElement.node_end(), elt);
1164                 }
1165             }
1166             break;
1167         // Str
1168         case(Property_Deprecated::Str) :
1169             UpdateXMLNodeSimpleProperty<string>(prop, myObjectElement, name);
1170             break;
1171         // BoolArray
1172         case(Property_Deprecated::BoolArray) :
1173             // print array as String and add it as such to element
1174             //UpdateXMLNodeArrayProperty<bool>(prop,myObjectElement,name); BoolArray Handling on Write
1175             stringValue = "";
1176             {
1177                 //int n = prop->getArraySize();
1178                 const Array<bool> &valueBs = prop->getValueArray<bool>();
1179                 for (int i=0; i<valueBs.size(); ++i)
1180                     stringValue += (valueBs[i]?"true ":"false ");
1181 
1182                 SimTK::Xml::Element elt(prop->getName(), stringValue);
1183                 myObjectElement.insertNodeAfter(myObjectElement.node_end(), elt);
1184             }
1185             break;
1186         // IntArray
1187         case(Property_Deprecated::IntArray) :
1188             UpdateXMLNodeArrayProperty<int>(prop,myObjectElement,name);
1189             break;
1190         // DblArray
1191         case(Property_Deprecated::DblArray) :
1192             UpdateXMLNodeArrayProperty<double>(prop,myObjectElement,name);
1193             break;
1194         // DblVec3
1195         case(Property_Deprecated::DblVec) :
1196             UpdateXMLNodeVec(prop,myObjectElement,name);
1197             break;
1198         // Transform
1199         case(Property_Deprecated::Transform) :
1200             UpdateXMLNodeTransform(prop,myObjectElement,name);
1201             break;
1202         // StrArray
1203         case(Property_Deprecated::StrArray) :
1204             UpdateXMLNodeArrayProperty<string>(prop,myObjectElement,name);
1205             break;
1206 
1207         // Obj
1208         case(Property_Deprecated::Obj) : {
1209             //PropertyObj *propObj = (PropertyObj*)prop;
1210             const Object &object = prop->getValueObj();
1211             object.updateXMLNode(myObjectElement);
1212             break; }
1213 
1214         // ObjArray AND ObjPtr (handled very similarly)
1215         case(Property_Deprecated::ObjArray) :
1216         case(Property_Deprecated::ObjPtr) : {
1217                 if(type==Property_Deprecated::ObjArray) {
1218                         // Set all the XML nodes to NULL, and then update them all
1219                         // in order, with index=0 so each new one is added to the end
1220                         // of the list (more efficient than inserting each one into
1221                         // the proper slot).
1222                     SimTK::Xml::Element objectArrayElement(prop->getName());
1223                     myObjectElement.insertNodeAfter(myObjectElement.node_end(), objectArrayElement);
1224                        for(int j=0;j<prop->getArraySize();j++)
1225                         prop->getValueObjPtr(j)->updateXMLNode(objectArrayElement);
1226                 } else { // ObjPtr
1227                     const Object *object = prop->getValueObjPtr();
1228                     SimTK::Xml::Element objectBaseElement(prop->getName());
1229                     myObjectElement.insertNodeAfter(myObjectElement.node_end(), objectBaseElement);
1230                     if(object) { // Add node for base classHEREHEREHERE
1231                         object->updateXMLNode(objectBaseElement);
1232                     }
1233                 }
1234             }
1235             break;
1236 
1237         // NOT RECOGNIZED
1238         default :
1239             cout<<"Object.UpdateObject: WARN- unrecognized property type."<<endl;
1240             break;
1241         }
1242     }
1243 
1244     if (!wroteAnyProperties) {
1245         myObjectElement.insertNodeAfter(myObjectElement.node_end(),
1246             SimTK::Xml::Comment
1247                ("All properties of this object have their default values."));
1248     }
1249 }
1250 
1251 //_____________________________________________________________________________
1252 /**
1253  * Update the XML node for defaults object.
1254  */
1255 void Object::
updateDefaultObjectsXMLNode(SimTK::Xml::Element & aParent)1256 updateDefaultObjectsXMLNode(SimTK::Xml::Element& aParent)
1257 {
1258     if (_document==NULL || !_document->hasDefaultObjects())
1259         return;
1260     string defaultsTag = "defaults";
1261     SimTK::Xml::element_iterator elmt = aParent.element_begin(defaultsTag);
1262     // Not root element- remove defaults
1263     //if(elmt==aParent.element_end());
1264     // Root element- write valid defaults
1265 
1266 
1267 }
1268 //-----------------------------------------------------------------------------
1269 // NODE
1270 //-----------------------------------------------------------------------------
1271 
1272 //-----------------------------------------------------------------------------
1273 // DOCUMENT
1274 //-----------------------------------------------------------------------------
1275 //_____________________________________________________________________________
1276 // getDocument(), updDocument() are inline.
1277 
1278 //_____________________________________________________________________________
1279 /**
1280  * Get the document's filename
1281  *
1282  * @return Document's filename for this object.
1283  */
getDocumentFileName() const1284 string Object::getDocumentFileName() const
1285 {
1286     return _document ? _document->getFileName() : "";
1287 }
1288 
1289 
getDocumentFileVersion() const1290 int Object::getDocumentFileVersion() const
1291 {
1292     return _document ? _document->getDocumentVersion() : -1;
1293 }
1294 
1295 
1296 //-----------------------------------------------------------------------------
1297 // GENERATE XML DOCUMENT
1298 //-----------------------------------------------------------------------------
1299 //_____________________________________________________________________________
1300 /**
1301  * Generate a new XML document with this object as the root node.
1302  */
1303 void Object::
generateXMLDocument()1304 generateXMLDocument()
1305 {
1306     // CREATE NEW DOCUMENT
1307     if (_document==NULL)
1308         _document = new XMLDocument();
1309 }
1310 
1311 //=============================================================================
1312 // XML support for inlining/offlining objects
1313 //=============================================================================
1314 /**
1315  * Get the value of the inlined flag
1316  */
1317 bool Object::
getInlined() const1318 getInlined() const
1319 {
1320     return _inlined;
1321 }
1322 
1323 void Object::
setInlined(bool aInlined,const std::string & aFileName)1324 setInlined(bool aInlined, const std::string &aFileName)
1325 {
1326     // Wipe out the previously associated document if we weren't inline.
1327     if (!_inlined && _document) {
1328         delete _document;
1329         _document = NULL;
1330     }
1331 
1332     _inlined = aInlined; // set new inline status
1333 
1334     if(!_inlined) {
1335         _document = new XMLDocument();
1336         _document->setFileName(aFileName);
1337     }
1338 }
1339 
1340 //-----------------------------------------------------------------------------
1341 // setAllPropertiesUseDefault
1342 //-----------------------------------------------------------------------------
1343 void Object::
setAllPropertiesUseDefault(bool aUseDefault)1344 setAllPropertiesUseDefault(bool aUseDefault)
1345 {
1346     // LOOP THROUGH PROPERTIES
1347     const int numProps = getNumProperties();
1348     for (int px = 0; px < numProps; ++px) {
1349         AbstractProperty& myProp = updPropertyByIndex(px);
1350         myProp.setAllPropertiesUseDefault(aUseDefault);
1351     }
1352 }
1353 
1354 //=============================================================================
1355 // IO
1356 //=============================================================================
1357 //-----------------------------------------------------------------------------
1358 // PRINT OBJECT
1359 //-----------------------------------------------------------------------------
1360 //_____________________________________________________________________________
1361 /**
1362  * Print the object.
1363  *
1364  * @param aFileName File name.  If the file name is NULL, which is the
1365  * default, the object is printed to standard out.
1366  */
1367 bool Object::
print(const string & aFileName) const1368 print(const string &aFileName) const
1369 {
1370     // Default to strict exception to avoid creating bad files
1371     // but for debugging allow users to be more lenient.
1372     if (_debugLevel >= 1) {
1373         try {
1374             warnBeforePrint();
1375         } catch (...) {}
1376     }
1377     else
1378         warnBeforePrint();
1379     // Temporarily change current directory so that inlined files are written to correct relative directory
1380     std::string savedCwd = IO::getCwd();
1381     IO::chDir(IO::getParentDirectory(aFileName));
1382     try {
1383         XMLDocument* oldDoc = NULL;
1384         if (_document != NULL){
1385             oldDoc = _document;
1386         }
1387         _document = new XMLDocument();
1388         if (oldDoc){
1389             _document->copyDefaultObjects(*oldDoc);
1390             delete oldDoc;
1391             oldDoc = 0;
1392         }
1393         SimTK::Xml::Element e = _document->getRootElement();
1394         updateXMLNode(e);
1395     } catch (const Exception &ex) {
1396         // Important to catch exceptions here so we can restore current working directory...
1397         // And then we can re-throw the exception
1398         IO::chDir(savedCwd);
1399         throw(ex);
1400     }
1401     IO::chDir(savedCwd);
1402     if(_document==NULL) return false;
1403     _document->print(aFileName);
1404     return true;
1405 }
1406 
1407 //-----------------------------------------------------------------------------
1408 // PRINT PROPERTY INFORMATION
1409 //-----------------------------------------------------------------------------
1410 // Print property information for registered classes. This is used by OpenSim
1411 // tools to provide a nice "help" capability for objects.
1412 
1413 // This signature accepts "className.propertyName", splits out the individual
1414 // segments and calls the other signature.
1415 bool Object::
PrintPropertyInfo(ostream & aOStream,const string & aClassNameDotPropertyName,bool printFlagInfo)1416 PrintPropertyInfo(ostream &aOStream,
1417                   const string &aClassNameDotPropertyName,
1418                   bool printFlagInfo)
1419 {
1420     // PARSE NAMES
1421     string compoundName = aClassNameDotPropertyName;
1422 
1423     string::size_type delimPos = compoundName.find(".");
1424     string className = compoundName.substr(0,delimPos);
1425     string propertyName = "";
1426     if(delimPos!=string::npos) {
1427         propertyName = compoundName.substr(delimPos+1);
1428     }
1429 
1430     return PrintPropertyInfo(aOStream, className, propertyName, printFlagInfo);
1431 }
1432 
1433 // This is the real method.
1434 bool Object::
PrintPropertyInfo(ostream & aOStream,const string & aClassName,const string & aPropertyName,bool printFlagInfo)1435 PrintPropertyInfo(ostream &aOStream,
1436                   const string &aClassName, const string &aPropertyName,
1437                   bool printFlagInfo)
1438 {
1439     if(aClassName=="") {
1440         // NO CLASS
1441         int size = _registeredTypes.getSize();
1442         aOStream<<"REGISTERED CLASSES ("<<size<<")\n";
1443         Object *obj;
1444         for(int i=0;i<size;i++) {
1445             obj = _registeredTypes.get(i);
1446             if(obj==NULL) continue;
1447             aOStream<<obj->getConcreteClassName()<<endl;
1448         }
1449         if (printFlagInfo) {
1450             aOStream<<"\n\nUse '-PropertyInfo ClassName' to list the properties of a particular class.\n\n";
1451         }
1452         return true;
1453     }
1454 
1455     // FIND CLASS
1456     const Object* object = getDefaultInstanceOfType(aClassName);
1457     if(object==NULL) {
1458         if (printFlagInfo) {
1459             aOStream<<"\nA class with the name '"<<aClassName<<"' was not found.\n";
1460             aOStream<<"\nUse '-PropertyInfo' without specifying a class name to print a listing of all registered classes.\n";
1461         }
1462         return false;
1463     }
1464 
1465     PropertySet propertySet = object->getPropertySet();
1466     const Property_Deprecated* prop;
1467     const AbstractProperty* abstractProperty;
1468     if((aPropertyName=="")||(aPropertyName=="*")) {
1469         // NO PROPERTY
1470         int propertySetSize = propertySet.getSize();
1471         int propertyTableSize = object->_propertyTable.getNumProperties();
1472         int size = propertySetSize + propertyTableSize;
1473         aOStream<<"\nPROPERTIES FOR "<<aClassName<<" ("<<size<<")\n";
1474         string comment;
1475         int i;
1476         for(i=0;i<propertyTableSize;i++) {
1477             abstractProperty =
1478                 &object->_propertyTable.getAbstractPropertyByIndex(i);
1479             if(abstractProperty==NULL) continue;
1480             if(aPropertyName=="") {
1481                 aOStream<<i+1<<". "<<abstractProperty->getName()<<endl;
1482             } else {
1483                 aOStream<<"\n"<<i+1<<". "<<abstractProperty->getName()<<"\n";
1484                 comment = abstractProperty->getComment();
1485                 if(!comment.empty()) {
1486                     string formattedComment = IO::formatText(comment,"\t",80);
1487                     aOStream<<"\t"<<formattedComment<<"\n";
1488                 }
1489             }
1490         }
1491 
1492         for(;i<size;i++) {
1493             prop = object->_propertySet.get(i-propertyTableSize);
1494             if(prop==NULL) continue;
1495             if(aPropertyName=="") {
1496                 aOStream<<i+1<<". "<<prop->getName()<<endl;
1497             } else {
1498                 aOStream<<"\n"<<i+1<<". "<<prop->getName()<<"\n";
1499                 comment = prop->getComment();
1500                 if(!comment.empty()) {
1501                     string formattedComment = IO::formatText(comment,"\t",80);
1502                     aOStream<<"\t"<<formattedComment<<"\n";
1503                 }
1504             }
1505         }
1506 
1507         if (printFlagInfo) {
1508             aOStream << "\n\nUse '-PropertyInfo ClassName.PropertyName' to print "
1509                 "info for a particular property.\n";
1510             if(aPropertyName!="*") {
1511                 aOStream << "Use '-PropertyInfo ClassName.*' to print info for all "
1512                     "properties in a class.\n";
1513             }
1514         }
1515         return true;
1516     }
1517 
1518     // FIND PROPERTY
1519     try {
1520         prop = propertySet.get(aPropertyName);
1521         // OUTPUT
1522         //aOStream<<"\nPROPERTY INFO FOR "<<aClassName<<"\n";
1523         aOStream << "\n" << aClassName << "." << aPropertyName << "\n"
1524                  << prop->getComment() << "\n";
1525         return true;
1526     } catch(...) {
1527         try {
1528             abstractProperty = object->_propertyTable.getPropertyPtr(aPropertyName);
1529             if (abstractProperty == nullptr) {
1530                 throw Exception("No property '" + aPropertyName +
1531                         "' class '" + aClassName + "'.");
1532             }
1533             // OUTPUT
1534             //aOStream<<"\nPROPERTY INFO FOR "<<aClassName<<"\n";
1535             aOStream << "\n" <<aClassName << "." << aPropertyName <<"\n"
1536                      << abstractProperty->getComment()<<"\n";
1537             return true;
1538         } catch (...) {
1539             if (printFlagInfo) {
1540                 aOStream << "\nPrintPropertyInfo: no property with the name "
1541                     << aPropertyName;
1542                 aOStream << " was found in class " << aClassName << ".\n";
1543                 aOStream << "Omit the property name to get a listing of all "
1544                     "properties in a class.\n";
1545             }
1546             return false;
1547         }
1548     }
1549 }
1550 
1551 
1552 //=============================================================================
1553 // Utilities, factory methods
1554 //=============================================================================
1555 /**
1556  * makeObjectFromFile creates an OpenSim object based on the type of the object at the root
1557  * node of the XML file passed in. This is useful since the constructor of Object doesn't have the
1558  * proper type info. This works by using the defaults table so that "Object" does not need to know about
1559  * derived classes, however it uses the defaults table to get an instance, so only "Registered" types will
1560  * be considered.
1561  *
1562  * Note: The object created is "New" so whoever makes the call also takes ownership of the object
1563  */
1564 Object* Object::
makeObjectFromFile(const std::string & aFileName)1565 makeObjectFromFile(const std::string &aFileName)
1566 {
1567     /**
1568      * Open the file and get the type of the root element
1569      */
1570     try{
1571         XMLDocument *doc = new XMLDocument(aFileName);
1572         // Here we know the fie exists and is good, chdir to where the file lives
1573         string rootName = doc->getRootTag();
1574         bool newFormat=false;
1575         if (rootName == "OpenSimDocument"){ // New format, get child node instead
1576             rootName = doc->getRootElement().element_begin()->getElementTag();
1577             newFormat=true;
1578         }
1579         Object* newObject = newInstanceOfType(rootName);
1580         if(!newObject) throw Exception("Unrecognized XML element '"+rootName+"' and root of file '"+aFileName+"'",__FILE__,__LINE__);
1581         // Here file is deemed legit, chdir to where the file lives here and restore at the end so offline objects are handled properly
1582         const string saveWorkingDirectory = IO::getCwd();
1583         const string directoryOfXMLFile = IO::getParentDirectory(aFileName);
1584         IO::chDir(directoryOfXMLFile);
1585         //cout << "File name = "<< aFileName << "Cwd is now "<< directoryOfXMLFile << endl;
1586         try {
1587             newObject->_document=doc;
1588             if (newFormat)
1589                 newObject->updateFromXMLNode(*doc->getRootElement().element_begin(), doc->getDocumentVersion());
1590             else {
1591                 SimTK::Xml::Element e = doc->getRootElement();
1592                 newObject->updateFromXMLNode(e, 10500);
1593             }
1594         } catch (...) {
1595             IO::chDir(saveWorkingDirectory);
1596             throw; // re-issue the exception
1597         }
1598         IO::chDir(saveWorkingDirectory);
1599         return (newObject);
1600     }
1601 
1602     catch(const std::exception& x) {
1603         cout << x.what() << endl;
1604         return nullptr;
1605     }
1606     catch(...){ // Document couldn't be opened, or something went really bad
1607         return nullptr;
1608     }
1609     assert(!"Shouldn't be here");
1610     return nullptr;
1611 }
1612 
makeObjectNamesConsistentWithProperties()1613 void Object::makeObjectNamesConsistentWithProperties()
1614 {
1615     // Cycle through this object's Object properties and make sure those
1616     // that are objects have names that are consistent with object property.
1617     for (int i = 0; i < getNumProperties(); ++i) {
1618         auto& prop = updPropertyByIndex(i);
1619         // check if property is of type Object
1620         if (prop.isObjectProperty()) {
1621             // a property is a list so cycle through its contents
1622             for (int j = 0; j < prop.size(); ++j) {
1623                 Object& obj = prop.updValueAsObject(j);
1624                 // If a single object property, set the object's name to the
1625                 // property's name, otherwise it will be inconsistent with
1626                 // what is serialized (property name).
1627                 if (!prop.isUnnamedProperty() && prop.isOneObjectProperty()) {
1628                     obj.setName(prop.getName());
1629                 }
1630                 // In any case, any objects that are properties of this object
1631                 // also need to be processed
1632                 obj.makeObjectNamesConsistentWithProperties();
1633             }
1634         }
1635     }
1636 }
1637 
setObjectIsUpToDateWithProperties()1638 void Object::setObjectIsUpToDateWithProperties()
1639 {
1640     _objectIsUpToDate = true;
1641 }
1642 
updateFromXMLDocument()1643 void Object::updateFromXMLDocument()
1644 {
1645     assert(_document!= 0);
1646 
1647     SimTK::Xml::Element e = _document->getRootDataElement();
1648     const string saveWorkingDirectory = IO::getCwd();
1649     string parentFileName = _document->getFileName();
1650     const string directoryOfXMLFile = IO::getParentDirectory(parentFileName);
1651     IO::chDir(directoryOfXMLFile);
1652     updateFromXMLNode(e, _document->getDocumentVersion());
1653     IO::chDir(saveWorkingDirectory);
1654 }
1655 
dump() const1656 std::string Object::dump() const {
1657     SimTK::String outString;
1658     XMLDocument doc;
1659     Object::setSerializeAllDefaults(true);
1660     SimTK::Xml::Element elem = doc.getRootElement();
1661     updateXMLNode(elem);
1662     Object::setSerializeAllDefaults(false);
1663     doc.getRootElement().node_begin()->writeToString(outString);
1664     return outString;
1665 }
1666 
1667 /**
1668     * The following code accounts for an object made up to call
1669     * RegisterTypes_osimCommon function on entry to the DLL in a cross platform manner
1670     *
1671     */
1672 // Excluding this from Doxygen until it has better documentation! -Sam Hamner
1673     /// @cond
1674 class osimCommonInstantiator
1675 {
1676 public:
1677         osimCommonInstantiator();
1678 private:
1679         void registerDllClasses();
1680 };
1681 
osimCommonInstantiator()1682 osimCommonInstantiator::osimCommonInstantiator()
1683 {
1684         registerDllClasses();
1685 }
1686 
1687 extern "C" OSIMCOMMON_API void RegisterTypes_osimCommon();
registerDllClasses()1688 void osimCommonInstantiator::registerDllClasses()
1689 {
1690         RegisterTypes_osimCommon();
1691 }
1692 
1693 static osimCommonInstantiator instantiator;
1694 /// @endcond
1695