<NME> XMLDocument.cpp
<BEF> /* -------------------------------------------------------------------------- *
 *                         OpenSim:  XMLDocument.cpp                          *
 * -------------------------------------------------------------------------- *
 * The OpenSim API is a toolkit for musculoskeletal modeling and simulation.  *
 * See http://opensim.stanford.edu and the NOTICE file for more information.  *
 * OpenSim is developed at Stanford University and supported by the US        *
 * National Institutes of Health (U54 GM072970, R24 HD065690) and by DARPA    *
 * through the Warrior Web program.                                           *
 *                                                                            *
 * Copyright (c) 2005-2017 Stanford University and the Authors                *
 * Author(s): Frank C. Anderson                                               *
 *                                                                            *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may    *
 * not use this file except in compliance with the License. You may obtain a  *
 * copy of the License at http://www.apache.org/licenses/LICENSE-2.0.         *
 *                                                                            *
 * Unless required by applicable law or agreed to in writing, software        *
 * distributed under the License is distributed on an "AS IS" BASIS,          *
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   *
 * See the License for the specific language governing permissions and        *
 * limitations under the License.                                             *
 * -------------------------------------------------------------------------- */

/* Note: This code was originally developed by Realistic Dynamics Inc. 
 * Author: Frank C. Anderson 
 */


//-----------------------------------------------------------------------------
// INCLUDES
//-----------------------------------------------------------------------------
#include "XMLDocument.h"
#include "Object.h"
#include <functional>


using namespace OpenSim;
using namespace std;


//-----------------------------------------------------------------------------
// CONSTANTS
//-----------------------------------------------------------------------------

// This list of version numbers is not complete
// 20301 for separation of RRATool, CMCTool
// 20302 for Muscle's pennation_angle -> pennation_angle_at_optimal
// 20303
// 30000 for OpenSim 3.0 release
// 30500 for OpenSim 4.0 development and Connectors
// 30501 for changing serialization of Marker
// 30502 for changing serialization of Geometry
// 30503 for changing serialization of Ground
// 30502 for Changing serialization of Geometry
// 30503 for Changing serialization of Ground
// 30505 for Changing serialization of Joint to create offset frames
const int XMLDocument::LatestVersion = 30505;   
//=============================================================================
// DESTRUCTOR AND CONSTRUCTOR(S)
//=============================================================================
// 30512 for removing Model::FrameSet and moving frames to components list
// 30513 for removing internal (silent) clamping of Muscle controls (excitations)
// 30514 for removing "reverse" property from Joint
// 30515 for WrapObject color, display_preference, VisibleObject -> Appearance
// 30516 for GeometryPath default_color -> Appearance
// 30517 for removal of _connectee_name suffix to shorten XML for socket, input
// 40000 for OpenSim 4.0 release 40000

const int XMLDocument::LatestVersion = 40000;
//=============================================================================
// DESTRUCTOR AND CONSTRUCTOR(S)
//=============================================================================
//-----------------------------------------------------------------------------
// DESTRUCTOR
//-----------------------------------------------------------------------------
//_____________________________________________________________________________
/**
 * Handle delete of an XMLDocument object.
 */
XMLDocument::~XMLDocument()
{
    for(int i = 0; i < _defaultObjects.size(); i++) {
        delete _defaultObjects.get(i);
    }
    _defaultObjects.setSize(0);
}

//-----------------------------------------------------------------------------
// CONSTRUCTOR(S)
//-----------------------------------------------------------------------------
//_____________________________________________________________________________
/**
 * Construct a XMLDocument object with a locally generated DOMDocument.
 * This constructor is used when an XML document is going to be generated
 * locally in memory without reference to an XML file.  The initial
 * DOMDocument is empty.
 */
XMLDocument::XMLDocument()
{
    setRootTag("OpenSimDocument");
    stringstream latestVersionString;
    latestVersionString << LatestVersion;
    _documentVersion = LatestVersion;
    getRootElement().setAttributeValue("Version", latestVersionString.str());
}

//_____________________________________________________________________________
/**
 * Construct an XMLDocument object from an XML document.
 * A parser is created for the purpose of reading in the XML file.
 *
 * @param aFileName File name of the XML document.
 */
XMLDocument::XMLDocument(const string &aFileName) :
SimTK::Xml::Document(aFileName)
{


    _fileName = aFileName;

    // Update document version based on parsing
    updateDocumentVersion();
}

//_____________________________________________________________________________
/**
 * Construct a copy of an XMLDocument object.  The document an all its nodes
 * are copied; however, the parser associated with the copied document, if
 * any, is not copied.
 */
XMLDocument::XMLDocument(const XMLDocument &aDocument):
SimTK::Xml::Document(aDocument)
{
    _documentVersion = aDocument.getDocumentVersion();
    _fileName = aDocument.getFileName();
}



//=============================================================================
// CONSTRUCTION
//=============================================================================
//_____________________________________________________________________________


//=============================================================================
// SET AND GET
//=============================================================================
//-----------------------------------------------------------------------------
// DOCUMENT
//-----------------------------------------------------------------------------

void XMLDocument::
setFileName(const string &aFileName)
{
    _fileName = aFileName;
}

const string &XMLDocument::
getFileName() const
{
    return _fileName;
}

//=============================================================================
// IO
//=============================================================================
//-----------------------------------------------------------------------------
// PRINT
//-----------------------------------------------------------------------------
//_____________________________________________________________________________
/**
 * Print the XML document to file.
 *
 * @param aFileName File name of the document to which to print
 */
bool XMLDocument::
print(const string &aFileName)
{
    // Standard Out
    if(aFileName.empty()) {
        std::stringstream ss;
        ss << *this;
        log_cout(ss.str());
    // File
    } else {
        setIndentString("\t");
        writeToFile(aFileName);
    }
    return true;
}
//_____________________________________________________________________________

//-----------------------------------------------------------------------------
// FORMATTER
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// STREAM OUTPUT
//-----------------------------------------------------------------------------
//_____________________________________________________________________________

//--------------------------------------------------------------------------
// VERSIONING /BACKWARD COMPATIBILITY SUPPORT
//--------------------------------------------------------------------------    
//_____________________________________________________________________________
/**
 * Convert passed in version number to a string 
 * The string is usually more compact e.g. 010100 -> 1_1 (rather than 11 which would confuse 110000 with 010100)
 */
void XMLDocument::
getVersionAsString(const int aVersion, std::string& aString)
{
    char pad[3];
    int ver = aVersion;
    aString = "";
    int div = 10000;
    for(int i=0; i<3; i++)
    {
        int digits = ver / div;
        sprintf(pad, "%02d",digits); 
        ver -= div*(ver / div);
        div /=100;
        aString += string(pad);
        if (ver ==0) break;
        aString +=(i<2?"_":"");
    }
}
//_____________________________________________________________________________
/**
 * Update member variable  _documentVersion based on parsing
 * Key assumption is that parsing has finished but no Object parsing is done yet
 */
void XMLDocument::
updateDocumentVersion()
{
    // Check root node if it's OpenSimDocument
    std::string rootTag = getRootTag();
    if (rootTag == "OpenSimDocument"){
        _documentVersion = getRootElement().getRequiredAttributeValueAs<int>("Version");
    }
    else {
        _documentVersion = 10500;  // Old version pre 1.6
    }

    // Validate >=  10500 and < latest as sanity check
    assert(_documentVersion >= 10500 && _documentVersion <= LatestVersion);
}
//_____________________________________________________________________________
/**
 * getRootDataElement returns a pointer to the real root node that contains objects 
 * works as a wrapper to get around the new root node <OpenSimDocument introduced in 1.6
 */
SimTK::Xml::Element  XMLDocument::
getRootDataElement()
{
    // Check root node if it's OpenSimDocument
    std::string rootTag = getRootTag();
    if (rootTag == "OpenSimDocument"){
        _documentVersion = getRootElement().getRequiredAttributeValueAs<int>("Version");
        return (*getRootElement().element_begin());
        }
    else {
        _documentVersion = 10500;  // Old version pre 1.6
        return  getRootElement();
    }
}
/**
 */
void XMLDocument::addDefaultObject(OpenSim::Object *aDefaultObject)
{
    _defaultObjects.append(aDefaultObject);
}
void XMLDocument::writeDefaultObjects(SimTK::Xml::Element& elmt)
{
    if (_defaultObjects.getSize()==0) return;
    // Make node for "defaults"
    SimTK::Xml::Element defaultsElement("defaults");
    
    elmt.insertNodeAfter(elmt.node_end(), defaultsElement);
    for(int i=0; i < _defaultObjects.getSize(); i++){
        _defaultObjects.get(i)->updateXMLNode(defaultsElement);
    }
}

void XMLDocument::copyDefaultObjects(const XMLDocument &aDocument){
        _defaultObjects.setSize(0);
        for (int i=0; i< aDocument._defaultObjects.getSize(); i++)
            _defaultObjects.append(aDocument._defaultObjects.get(i)->clone());
}

/*static*/ 
void  XMLDocument::renameChildNode(SimTK::Xml::Element& aNode, std::string oldElementName, std::string newElementName)
{
    SimTK::Xml::element_iterator elmtIter(aNode.element_begin(oldElementName));
    if (elmtIter!=aNode.element_end()){
        elmtIter->setElementTag(newElementName);
    }
}

bool XMLDocument::isEqualTo(XMLDocument& aOtherDocument, double toleranceForDoubles, bool compareDefaults, bool compareVersionNumbers) 
{
    bool equal = true;

    if (compareVersionNumbers)
        equal = (_documentVersion == aOtherDocument._documentVersion);
    if (!equal) return false;
    // Get Roots 
    SimTK::Xml::Element root1=  getRootElement();
    SimTK::Xml::Element root2=  aOtherDocument.getRootElement();

    //if (!equal) return false;
    // Cycle through children and compare. Order is assumed to be the same for now
    SimTK::Array_<SimTK::Xml::Element> elts1 = root1.getAllElements();
    SimTK::Array_<SimTK::Xml::Element> elts2 = root2.getAllElements();
    if (elts1.size() != elts2.size()){
        log_info("Different number of children at Top level");
        equal = false;
    }
    if (!equal) return false;
    // Recursively compare Elements
    SimTK::String s1,s2;
    for(unsigned it = 0; it < elts1.size(); it++){
        elts1[it].writeToString(s1);
        elts2[it].writeToString(s2);

        if (elts1[it].getElementTag()==elts2[it].getElementTag() && elts1[it].getElementTag()=="defaults" && !compareDefaults) 
            continue;
        equal = isElementEqual(elts1[it], elts2[it], toleranceForDoubles);
        if (!equal){ 
            log_info("{} is different", elts1[it].getElementTag());
            return false;
        }
    }
    return true;
}

bool XMLDocument::isElementEqual(SimTK::Xml::Element& elt1, SimTK::Xml::Element& elt2, double toleranceForDoubles)
{
    SimTK::String s1,s2;
    elt1.writeToString(s1);
    elt2.writeToString(s2);
    SimTK::Xml::attribute_iterator att1 = elt1.attribute_begin();
    SimTK::Xml::attribute_iterator att2 = elt2.attribute_begin();
    // Handle different # attributes
    if ( (att1 == elt1.attribute_end() && att2 != elt2.attribute_end()) ||
         (att1 != elt1.attribute_end() && att2 == elt2.attribute_end()) ){
            log_info("Number of attributes is different, element {}", elt1.getElementTag());
            return false;
    }
    bool equal =true;
    // Same size attributes including none
    for(att1 = elt1.attribute_begin(); att1 != elt1.attribute_end() && equal; att1++, att2++){
        equal = (att1->getName() == att2->getName());
        equal = equal && (att1->getValue() == att2->getValue());
        if (!equal) {
            log_info("Attribute {} is different {} vs. {}", att1->getName(),
                    att1->getValue(), att2->getValue());
        }
    }
    if (!equal) return false;

    // Attributes match now children
    SimTK::Array_<SimTK::Xml::Element> elts1 = elt1.getAllElements();
    SimTK::Array_<SimTK::Xml::Element> elts2 = elt2.getAllElements();
    if (elts1.size() != elts2.size()){
        log_info("Different number of children for Element {}",
                elt1.getElementTag());
        equal = false;
    }
    if (!equal) return false;
    // Recursively compare Elements unless Value Elements in that case do direct compare
    for(unsigned it = 0; it < elts1.size() && equal; it++){
        SimTK::String elt1Tag = elts1[it].getElementTag();
        log_info("Compare {}", elt1Tag);
        SimTK::Xml::element_iterator elt2_iter = elt2.element_begin(elt1Tag);
        if (elt2_iter==elt2.element_end()){
            log_info("Element {} was not found in reference document", elt1Tag);
            equal = false;
            break;
        }
        bool value1 = elts1[it].isValueElement();
        bool value2 = elt2_iter->isValueElement();
        equal = (value1 == value2);
        if (!equal){
            log_info("{} is different. One is Value Element the other isn't",
                     elts1[it].getElementTag());
            return false;
        }
        if (value1){
            // We should check if this's a double or array of doubles in that case we can getValueAs<double>
            try {
                SimTK::Array_<double> v1, v2;
                elts1[it].getValueAs(v1);
                elt2_iter->getValueAs(v2);
                for(unsigned ix=0; ix<v1.size() && equal; ix++)
                    equal = (std::fabs(v1[ix]-v2[ix]) < toleranceForDoubles);
            }
            catch(...){
                equal = (elts1[it].getValue() == elt2_iter->getValue());
            }
        }
        else    // recur
            equal = isElementEqual(elts1[it], elts2[it], toleranceForDoubles);
        if (!equal){ 
            log_info("{} is different", elts1[it].getElementTag());
            SimTK::String pad;
            elts1[it].writeToString(pad);
            log_info(pad);
            log_info("------------------- vs. ------");
            elts2[it].writeToString(pad);
            log_info(pad);
            return equal;
        }
    }

    return equal;
}

/*
 * Helper function to add connector to the xmlElement passed in
 */
void XMLDocument::addConnector(SimTK::Xml::Element& element,
    const std::string& connectorTag, const std::string& connectorName, 
    const std::string& connectorValue)
{
    SimTK::Xml::element_iterator  connectors_node =  element.element_begin("connectors");
    //SimTK::String debug; //Only used for debugging
    if (connectors_node == element.element_end()){
        SimTK::Xml::Element connectorsElement("connectors");
        element.insertNodeBefore(element.element_begin(), connectorsElement);
        connectors_node =  element.element_begin("connectors");
    }
    // Here we're guaranteed connectors node exists, add individual connector
    SimTK::Xml::Element newConnectorElement(connectorTag);
    newConnectorElement.setAttributeValue("name", connectorName);
    //newConnectorElement.writeToString(debug);

    SimTK::Xml::Element connecteeElement("connectee_name");
    connecteeElement.insertNodeAfter(connecteeElement.element_end(), SimTK::Xml::Text(connectorValue));
    // Insert text under newConnectorElement
    newConnectorElement.insertNodeAfter(newConnectorElement.element_end(), connecteeElement);
    connectors_node->insertNodeAfter(connectors_node->element_end(), newConnectorElement);
    //connectors_node->writeToString(debug);
}

void XMLDocument::updateConnectors30508(SimTK::Xml::Element& componentElt)
{
    using ElementItr = SimTK::Xml::element_iterator;
    
    ElementItr connectors_node = componentElt.element_begin("connectors");
    
    // See if there's a <connectors> element.
    if (connectors_node == componentElt.element_end()) return;
    
    for (ElementItr connectorElt = connectors_node->element_begin();
            connectorElt != componentElt.element_end();
            ++connectorElt) {
        // Grab name of Connector.
        const auto& connectorName =
                connectorElt->getRequiredAttributeValue("name");
        // Grab value of connectee_name property.
        ElementItr connecteeNameElt =
                connectorElt->element_begin("connectee_name");
        SimTK::String connecteeName;
        connecteeNameElt->getValueAs<std::string>(connecteeName);
        
        // Create new element for this connector's connectee name.
        SimTK::Xml::Element newConnecteeNameElt(
                "connector_" + connectorName + "_connectee_name");
        newConnecteeNameElt.setValue(connecteeName);
        componentElt.insertNodeAfter(connectors_node, newConnecteeNameElt);
    }
    
    // No longer want the old syntax for connectors.
    componentElt.eraseNode(connectors_node);
}

void XMLDocument::addPhysicalOffsetFrame30505_30517(SimTK::Xml::Element& element,
    const std::string& frameName,
    const std::string& parentFrameName, 
    const SimTK::Vec3& location, const SimTK::Vec3& orientation)
{
    SimTK::Xml::element_iterator  frames_node = element.element_begin("frames");
    //SimTK::String debug; //Only used for debugging
    
    if (frames_node == element.element_end()) {
        SimTK::Xml::Element framesElement("frames");
        element.insertNodeBefore(element.element_begin(), framesElement);
        frames_node = element.element_begin("frames");
    }
    // Here we're guaranteed frames node exists, add individual frame
    SimTK::Xml::Element newFrameElement("PhysicalOffsetFrame");
    newFrameElement.setAttributeValue("name", frameName);
    //newFrameElement.writeToString(debug);

    // This function always adds the frame as a subcomponent of a component
    // that is a member of the forceset, making this frame three levels
    // deep. The connectee is either ground or a member of bodyset.
    if(parentFrameName == "ground")
        XMLDocument::addConnector(newFrameElement,
            "Connector_PhysicalFrame_", "parent", "../../../"
            + parentFrameName);
    else
        XMLDocument::addConnector(newFrameElement,
            "Connector_PhysicalFrame_", "parent", "../../../bodyset/"
            + parentFrameName);

    std::ostringstream transValue;
    transValue << location[0] << " " << location[1] << " " << location[2];
    SimTK::Xml::Element translationElement("translation", transValue.str());
    newFrameElement.insertNodeAfter(newFrameElement.element_end(), translationElement);

    std::ostringstream orientValue; 
    orientValue << orientation[0] << " " << orientation[1] << " " << orientation[2];
    SimTK::Xml::Element orientationElement("orientation", orientValue.str());
    newFrameElement.insertNodeAfter(newFrameElement.element_end(), orientationElement);

    frames_node->insertNodeAfter(frames_node->element_end(), newFrameElement);
    //frames_node->writeToString(debug);
}

string XMLDocument::updateConnecteePath30517(
        const std::string& connecteeSetName,
        const std::string& connecteeName) {
    std::string connecteePath;
    if (connecteeSetName == "bodyset" && connecteeName == "ground") {
        connecteePath = "/" + connecteeName;
    } else{
        connecteePath = "/" + connecteeSetName + "/" + connecteeName;
    }
    return connecteePath;
}

SimTK::Xml::Element XMLDocument::findElementWithName(
        SimTK::Xml::Element& element, const std::string& name) {
    using namespace SimTK;

    if (name.empty()) return Xml::Element();

    // First, get to the root of the XML document.
    Xml::Element current = element;
    while (current.hasParentElement())
        current = current.getParentElement();
    Xml::Element root = current;

    // This will be a recursive lambda function.
    std::function<Xml::Element(Xml::Element&, const std::string&)>
        searchForElement;
    // For recursion, must capture the function itself.
    // Returns an invalid Element if no element with `name` could be found.
    searchForElement = [&searchForElement](
            Xml::Element& elem, const std::string& name) -> Xml::Element {
        // This is a depth-first search.
        for (auto it = elem.element_begin(); it != elem.element_end();
                ++it) {
            std::string elemName = it->getOptionalAttributeValue("name");
            if (elemName == name)
                return elem;
            Xml::Element foundElem = searchForElement(*it, name);
            if (foundElem.isValid())
                return foundElem;
            // Keep searching other branches.
        }
        return Xml::Element(); // Did not find.
    };
    return searchForElement(root, name);
}

<MSG> Incrementing XMLDocument number per @aymanhab's request in #1002.

<DFF> @@ -54,7 +54,8 @@ using namespace std;
 // 30502 for Changing serialization of Geometry
 // 30503 for Changing serialization of Ground
 // 30505 for Changing serialization of Joint to create offset frames
-const int XMLDocument::LatestVersion = 30505;   
+// 30506 for testing 30505 conversion code
+const int XMLDocument::LatestVersion = 30506;
 //=============================================================================
 // DESTRUCTOR AND CONSTRUCTOR(S)
 //=============================================================================

{'commit': 'diff --git a/OpenSim/Common/XMLDocument.cpp b/OpenSim/Common/XMLDocument.cpp\nindex 635edf615..e6a909f02 100644\n--- a/OpenSim/Common/XMLDocument.cpp\n+++ b/OpenSim/Common/XMLDocument.cpp\n@@ -54,7 +54,8 @@ using namespace std;\n // 30502 for Changing serialization of Geometry\n // 30503 for Changing serialization of Ground\n // 30505 for Changing serialization of Joint to create offset frames\n-const int XMLDocument::LatestVersion = 30505;   \n+// 30506 for testing 30505 conversion code\n+const int XMLDocument::LatestVersion = 30506;\n //=============================================================================\n // DESTRUCTOR AND CONSTRUCTOR(S)\n //=============================================================================\n', 'commit_subject': "Incrementing XMLDocument number per @aymanhab's request in #1002.", 'repo_name': 'opensim-org/opensim-core', 'committer': {'name': 'Tom Uchida', 'email': 'e5a9f087e6112d34b8b71a2bd0996bfe1bbd2e15@gmail.com', 'time_sec': '1465842167', 'tz_offset': '-420', 'date': {'seconds': '1465842167'}}, 'commit_tree': 'a78f1c6c416a768dd5791b86bd18ef2b19befe2f', 'commit_message': "Incrementing XMLDocument number per @aymanhab's request in #1002.\n", 'language_name': 'C', 'license': 'apache-2.0', 'hunks': '@@ -54,7 +54,8 @@ using namespace std;\n // 30502 for Changing serialization of Geometry\n // 30503 for Changing serialization of Ground\n // 30505 for Changing serialization of Joint to create offset frames\n-const int XMLDocument::LatestVersion = 30505;   \n+// 30506 for testing 30505 conversion code\n+const int XMLDocument::LatestVersion = 30506;\n //=============================================================================\n // DESTRUCTOR AND CONSTRUCTOR(S)\n //=============================================================================\n', 'addition_count': 2, 'deletion_count': 1, 'src_file': 'a/OpenSim/Common/XMLDocument.cpp', 'tgt_file': 'b/OpenSim/Common/XMLDocument.cpp', 'file_extension': '.cpp', 'before_file': '/* -------------------------------------------------------------------------- *\n *                         OpenSim:  XMLDocument.cpp                          *\n * -------------------------------------------------------------------------- *\n * The OpenSim API is a toolkit for musculoskeletal modeling and simulation.  *\n * See http://opensim.stanford.edu and the NOTICE file for more information.  *\n * OpenSim is developed at Stanford University and supported by the US        *\n * National Institutes of Health (U54 GM072970, R24 HD065690) and by DARPA    *\n * through the Warrior Web program.                                           *\n *                                                                            *\n * Copyright (c) 2005-2017 Stanford University and the Authors                *\n * Author(s): Frank C. Anderson                                               *\n *                                                                            *\n * Licensed under the Apache License, Version 2.0 (the "License"); you may    *\n * not use this file except in compliance with the License. You may obtain a  *\n * copy of the License at http://www.apache.org/licenses/LICENSE-2.0.         *\n *                                                                            *\n * Unless required by applicable law or agreed to in writing, software        *\n * distributed under the License is distributed on an "AS IS" BASIS,          *\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   *\n * See the License for the specific language governing permissions and        *\n * limitations under the License.                                             *\n * -------------------------------------------------------------------------- */\n\n/* Note: This code was originally developed by Realistic Dynamics Inc. \n * Author: Frank C. Anderson \n */\n\n\n//-----------------------------------------------------------------------------\n// INCLUDES\n//-----------------------------------------------------------------------------\n#include "XMLDocument.h"\n#include "Object.h"\n#include <functional>\n\n\nusing namespace OpenSim;\nusing namespace std;\n\n\n//-----------------------------------------------------------------------------\n// CONSTANTS\n//-----------------------------------------------------------------------------\n\n// This list of version numbers is not complete\n// 20301 for separation of RRATool, CMCTool\n// 20302 for Muscle\'s pennation_angle -> pennation_angle_at_optimal\n// 20303\n// 30000 for OpenSim 3.0 release\n// 30500 for OpenSim 4.0 development and Connectors\n// 30501 for changing serialization of Marker\n// 30502 for changing serialization of Geometry\n// 30503 for changing serialization of Ground\n// 30502 for Changing serialization of Geometry\n// 30503 for Changing serialization of Ground\n// 30505 for Changing serialization of Joint to create offset frames\nconst int XMLDocument::LatestVersion = 30505;   \n//=============================================================================\n// DESTRUCTOR AND CONSTRUCTOR(S)\n//=============================================================================\n// 30512 for removing Model::FrameSet and moving frames to components list\n// 30513 for removing internal (silent) clamping of Muscle controls (excitations)\n// 30514 for removing "reverse" property from Joint\n// 30515 for WrapObject color, display_preference, VisibleObject -> Appearance\n// 30516 for GeometryPath default_color -> Appearance\n// 30517 for removal of _connectee_name suffix to shorten XML for socket, input\n// 40000 for OpenSim 4.0 release 40000\n\nconst int XMLDocument::LatestVersion = 40000;\n//=============================================================================\n// DESTRUCTOR AND CONSTRUCTOR(S)\n//=============================================================================\n//-----------------------------------------------------------------------------\n// DESTRUCTOR\n//-----------------------------------------------------------------------------\n//_____________________________________________________________________________\n/**\n * Handle delete of an XMLDocument object.\n */\nXMLDocument::~XMLDocument()\n{\n    for(int i = 0; i < _defaultObjects.size(); i++) {\n        delete _defaultObjects.get(i);\n    }\n    _defaultObjects.setSize(0);\n}\n\n//-----------------------------------------------------------------------------\n// CONSTRUCTOR(S)\n//-----------------------------------------------------------------------------\n//_____________________________________________________________________________\n/**\n * Construct a XMLDocument object with a locally generated DOMDocument.\n * This constructor is used when an XML document is going to be generated\n * locally in memory without reference to an XML file.  The initial\n * DOMDocument is empty.\n */\nXMLDocument::XMLDocument()\n{\n    setRootTag("OpenSimDocument");\n    stringstream latestVersionString;\n    latestVersionString << LatestVersion;\n    _documentVersion = LatestVersion;\n    getRootElement().setAttributeValue("Version", latestVersionString.str());\n}\n\n//_____________________________________________________________________________\n/**\n * Construct an XMLDocument object from an XML document.\n * A parser is created for the purpose of reading in the XML file.\n *\n * @param aFileName File name of the XML document.\n */\nXMLDocument::XMLDocument(const string &aFileName) :\nSimTK::Xml::Document(aFileName)\n{\n\n\n    _fileName = aFileName;\n\n    // Update document version based on parsing\n    updateDocumentVersion();\n}\n\n//_____________________________________________________________________________\n/**\n * Construct a copy of an XMLDocument object.  The document an all its nodes\n * are copied; however, the parser associated with the copied document, if\n * any, is not copied.\n */\nXMLDocument::XMLDocument(const XMLDocument &aDocument):\nSimTK::Xml::Document(aDocument)\n{\n    _documentVersion = aDocument.getDocumentVersion();\n    _fileName = aDocument.getFileName();\n}\n\n\n\n//=============================================================================\n// CONSTRUCTION\n//=============================================================================\n//_____________________________________________________________________________\n\n\n//=============================================================================\n// SET AND GET\n//=============================================================================\n//-----------------------------------------------------------------------------\n// DOCUMENT\n//-----------------------------------------------------------------------------\n\nvoid XMLDocument::\nsetFileName(const string &aFileName)\n{\n    _fileName = aFileName;\n}\n\nconst string &XMLDocument::\ngetFileName() const\n{\n    return _fileName;\n}\n\n//=============================================================================\n// IO\n//=============================================================================\n//-----------------------------------------------------------------------------\n// PRINT\n//-----------------------------------------------------------------------------\n//_____________________________________________________________________________\n/**\n * Print the XML document to file.\n *\n * @param aFileName File name of the document to which to print\n */\nbool XMLDocument::\nprint(const string &aFileName)\n{\n    // Standard Out\n    if(aFileName.empty()) {\n        std::stringstream ss;\n        ss << *this;\n        log_cout(ss.str());\n    // File\n    } else {\n        setIndentString("\\t");\n        writeToFile(aFileName);\n    }\n    return true;\n}\n//_____________________________________________________________________________\n\n//-----------------------------------------------------------------------------\n// FORMATTER\n//-----------------------------------------------------------------------------\n//-----------------------------------------------------------------------------\n// STREAM OUTPUT\n//-----------------------------------------------------------------------------\n//_____________________________________________________________________________\n\n//--------------------------------------------------------------------------\n// VERSIONING /BACKWARD COMPATIBILITY SUPPORT\n//--------------------------------------------------------------------------    \n//_____________________________________________________________________________\n/**\n * Convert passed in version number to a string \n * The string is usually more compact e.g. 010100 -> 1_1 (rather than 11 which would confuse 110000 with 010100)\n */\nvoid XMLDocument::\ngetVersionAsString(const int aVersion, std::string& aString)\n{\n    char pad[3];\n    int ver = aVersion;\n    aString = "";\n    int div = 10000;\n    for(int i=0; i<3; i++)\n    {\n        int digits = ver / div;\n        sprintf(pad, "%02d",digits); \n        ver -= div*(ver / div);\n        div /=100;\n        aString += string(pad);\n        if (ver ==0) break;\n        aString +=(i<2?"_":"");\n    }\n}\n//_____________________________________________________________________________\n/**\n * Update member variable  _documentVersion based on parsing\n * Key assumption is that parsing has finished but no Object parsing is done yet\n */\nvoid XMLDocument::\nupdateDocumentVersion()\n{\n    // Check root node if it\'s OpenSimDocument\n    std::string rootTag = getRootTag();\n    if (rootTag == "OpenSimDocument"){\n        _documentVersion = getRootElement().getRequiredAttributeValueAs<int>("Version");\n    }\n    else {\n        _documentVersion = 10500;  // Old version pre 1.6\n    }\n\n    // Validate >=  10500 and < latest as sanity check\n    assert(_documentVersion >= 10500 && _documentVersion <= LatestVersion);\n}\n//_____________________________________________________________________________\n/**\n * getRootDataElement returns a pointer to the real root node that contains objects \n * works as a wrapper to get around the new root node <OpenSimDocument introduced in 1.6\n */\nSimTK::Xml::Element  XMLDocument::\ngetRootDataElement()\n{\n    // Check root node if it\'s OpenSimDocument\n    std::string rootTag = getRootTag();\n    if (rootTag == "OpenSimDocument"){\n        _documentVersion = getRootElement().getRequiredAttributeValueAs<int>("Version");\n        return (*getRootElement().element_begin());\n        }\n    else {\n        _documentVersion = 10500;  // Old version pre 1.6\n        return  getRootElement();\n    }\n}\n/**\n */\nvoid XMLDocument::addDefaultObject(OpenSim::Object *aDefaultObject)\n{\n    _defaultObjects.append(aDefaultObject);\n}\nvoid XMLDocument::writeDefaultObjects(SimTK::Xml::Element& elmt)\n{\n    if (_defaultObjects.getSize()==0) return;\n    // Make node for "defaults"\n    SimTK::Xml::Element defaultsElement("defaults");\n    \n    elmt.insertNodeAfter(elmt.node_end(), defaultsElement);\n    for(int i=0; i < _defaultObjects.getSize(); i++){\n        _defaultObjects.get(i)->updateXMLNode(defaultsElement);\n    }\n}\n\nvoid XMLDocument::copyDefaultObjects(const XMLDocument &aDocument){\n        _defaultObjects.setSize(0);\n        for (int i=0; i< aDocument._defaultObjects.getSize(); i++)\n            _defaultObjects.append(aDocument._defaultObjects.get(i)->clone());\n}\n\n/*static*/ \nvoid  XMLDocument::renameChildNode(SimTK::Xml::Element& aNode, std::string oldElementName, std::string newElementName)\n{\n    SimTK::Xml::element_iterator elmtIter(aNode.element_begin(oldElementName));\n    if (elmtIter!=aNode.element_end()){\n        elmtIter->setElementTag(newElementName);\n    }\n}\n\nbool XMLDocument::isEqualTo(XMLDocument& aOtherDocument, double toleranceForDoubles, bool compareDefaults, bool compareVersionNumbers) \n{\n    bool equal = true;\n\n    if (compareVersionNumbers)\n        equal = (_documentVersion == aOtherDocument._documentVersion);\n    if (!equal) return false;\n    // Get Roots \n    SimTK::Xml::Element root1=  getRootElement();\n    SimTK::Xml::Element root2=  aOtherDocument.getRootElement();\n\n    //if (!equal) return false;\n    // Cycle through children and compare. Order is assumed to be the same for now\n    SimTK::Array_<SimTK::Xml::Element> elts1 = root1.getAllElements();\n    SimTK::Array_<SimTK::Xml::Element> elts2 = root2.getAllElements();\n    if (elts1.size() != elts2.size()){\n        log_info("Different number of children at Top level");\n        equal = false;\n    }\n    if (!equal) return false;\n    // Recursively compare Elements\n    SimTK::String s1,s2;\n    for(unsigned it = 0; it < elts1.size(); it++){\n        elts1[it].writeToString(s1);\n        elts2[it].writeToString(s2);\n\n        if (elts1[it].getElementTag()==elts2[it].getElementTag() && elts1[it].getElementTag()=="defaults" && !compareDefaults) \n            continue;\n        equal = isElementEqual(elts1[it], elts2[it], toleranceForDoubles);\n        if (!equal){ \n            log_info("{} is different", elts1[it].getElementTag());\n            return false;\n        }\n    }\n    return true;\n}\n\nbool XMLDocument::isElementEqual(SimTK::Xml::Element& elt1, SimTK::Xml::Element& elt2, double toleranceForDoubles)\n{\n    SimTK::String s1,s2;\n    elt1.writeToString(s1);\n    elt2.writeToString(s2);\n    SimTK::Xml::attribute_iterator att1 = elt1.attribute_begin();\n    SimTK::Xml::attribute_iterator att2 = elt2.attribute_begin();\n    // Handle different # attributes\n    if ( (att1 == elt1.attribute_end() && att2 != elt2.attribute_end()) ||\n         (att1 != elt1.attribute_end() && att2 == elt2.attribute_end()) ){\n            log_info("Number of attributes is different, element {}", elt1.getElementTag());\n            return false;\n    }\n    bool equal =true;\n    // Same size attributes including none\n    for(att1 = elt1.attribute_begin(); att1 != elt1.attribute_end() && equal; att1++, att2++){\n        equal = (att1->getName() == att2->getName());\n        equal = equal && (att1->getValue() == att2->getValue());\n        if (!equal) {\n            log_info("Attribute {} is different {} vs. {}", att1->getName(),\n                    att1->getValue(), att2->getValue());\n        }\n    }\n    if (!equal) return false;\n\n    // Attributes match now children\n    SimTK::Array_<SimTK::Xml::Element> elts1 = elt1.getAllElements();\n    SimTK::Array_<SimTK::Xml::Element> elts2 = elt2.getAllElements();\n    if (elts1.size() != elts2.size()){\n        log_info("Different number of children for Element {}",\n                elt1.getElementTag());\n        equal = false;\n    }\n    if (!equal) return false;\n    // Recursively compare Elements unless Value Elements in that case do direct compare\n    for(unsigned it = 0; it < elts1.size() && equal; it++){\n        SimTK::String elt1Tag = elts1[it].getElementTag();\n        log_info("Compare {}", elt1Tag);\n        SimTK::Xml::element_iterator elt2_iter = elt2.element_begin(elt1Tag);\n        if (elt2_iter==elt2.element_end()){\n            log_info("Element {} was not found in reference document", elt1Tag);\n            equal = false;\n            break;\n        }\n        bool value1 = elts1[it].isValueElement();\n        bool value2 = elt2_iter->isValueElement();\n        equal = (value1 == value2);\n        if (!equal){\n            log_info("{} is different. One is Value Element the other isn\'t",\n                     elts1[it].getElementTag());\n            return false;\n        }\n        if (value1){\n            // We should check if this\'s a double or array of doubles in that case we can getValueAs<double>\n            try {\n                SimTK::Array_<double> v1, v2;\n                elts1[it].getValueAs(v1);\n                elt2_iter->getValueAs(v2);\n                for(unsigned ix=0; ix<v1.size() && equal; ix++)\n                    equal = (std::fabs(v1[ix]-v2[ix]) < toleranceForDoubles);\n            }\n            catch(...){\n                equal = (elts1[it].getValue() == elt2_iter->getValue());\n            }\n        }\n        else    // recur\n            equal = isElementEqual(elts1[it], elts2[it], toleranceForDoubles);\n        if (!equal){ \n            log_info("{} is different", elts1[it].getElementTag());\n            SimTK::String pad;\n            elts1[it].writeToString(pad);\n            log_info(pad);\n            log_info("------------------- vs. ------");\n            elts2[it].writeToString(pad);\n            log_info(pad);\n            return equal;\n        }\n    }\n\n    return equal;\n}\n\n/*\n * Helper function to add connector to the xmlElement passed in\n */\nvoid XMLDocument::addConnector(SimTK::Xml::Element& element,\n    const std::string& connectorTag, const std::string& connectorName, \n    const std::string& connectorValue)\n{\n    SimTK::Xml::element_iterator  connectors_node =  element.element_begin("connectors");\n    //SimTK::String debug; //Only used for debugging\n    if (connectors_node == element.element_end()){\n        SimTK::Xml::Element connectorsElement("connectors");\n        element.insertNodeBefore(element.element_begin(), connectorsElement);\n        connectors_node =  element.element_begin("connectors");\n    }\n    // Here we\'re guaranteed connectors node exists, add individual connector\n    SimTK::Xml::Element newConnectorElement(connectorTag);\n    newConnectorElement.setAttributeValue("name", connectorName);\n    //newConnectorElement.writeToString(debug);\n\n    SimTK::Xml::Element connecteeElement("connectee_name");\n    connecteeElement.insertNodeAfter(connecteeElement.element_end(), SimTK::Xml::Text(connectorValue));\n    // Insert text under newConnectorElement\n    newConnectorElement.insertNodeAfter(newConnectorElement.element_end(), connecteeElement);\n    connectors_node->insertNodeAfter(connectors_node->element_end(), newConnectorElement);\n    //connectors_node->writeToString(debug);\n}\n\nvoid XMLDocument::updateConnectors30508(SimTK::Xml::Element& componentElt)\n{\n    using ElementItr = SimTK::Xml::element_iterator;\n    \n    ElementItr connectors_node = componentElt.element_begin("connectors");\n    \n    // See if there\'s a <connectors> element.\n    if (connectors_node == componentElt.element_end()) return;\n    \n    for (ElementItr connectorElt = connectors_node->element_begin();\n            connectorElt != componentElt.element_end();\n            ++connectorElt) {\n        // Grab name of Connector.\n        const auto& connectorName =\n                connectorElt->getRequiredAttributeValue("name");\n        // Grab value of connectee_name property.\n        ElementItr connecteeNameElt =\n                connectorElt->element_begin("connectee_name");\n        SimTK::String connecteeName;\n        connecteeNameElt->getValueAs<std::string>(connecteeName);\n        \n        // Create new element for this connector\'s connectee name.\n        SimTK::Xml::Element newConnecteeNameElt(\n                "connector_" + connectorName + "_connectee_name");\n        newConnecteeNameElt.setValue(connecteeName);\n        componentElt.insertNodeAfter(connectors_node, newConnecteeNameElt);\n    }\n    \n    // No longer want the old syntax for connectors.\n    componentElt.eraseNode(connectors_node);\n}\n\nvoid XMLDocument::addPhysicalOffsetFrame30505_30517(SimTK::Xml::Element& element,\n    const std::string& frameName,\n    const std::string& parentFrameName, \n    const SimTK::Vec3& location, const SimTK::Vec3& orientation)\n{\n    SimTK::Xml::element_iterator  frames_node = element.element_begin("frames");\n    //SimTK::String debug; //Only used for debugging\n    \n    if (frames_node == element.element_end()) {\n        SimTK::Xml::Element framesElement("frames");\n        element.insertNodeBefore(element.element_begin(), framesElement);\n        frames_node = element.element_begin("frames");\n    }\n    // Here we\'re guaranteed frames node exists, add individual frame\n    SimTK::Xml::Element newFrameElement("PhysicalOffsetFrame");\n    newFrameElement.setAttributeValue("name", frameName);\n    //newFrameElement.writeToString(debug);\n\n    // This function always adds the frame as a subcomponent of a component\n    // that is a member of the forceset, making this frame three levels\n    // deep. The connectee is either ground or a member of bodyset.\n    if(parentFrameName == "ground")\n        XMLDocument::addConnector(newFrameElement,\n            "Connector_PhysicalFrame_", "parent", "../../../"\n            + parentFrameName);\n    else\n        XMLDocument::addConnector(newFrameElement,\n            "Connector_PhysicalFrame_", "parent", "../../../bodyset/"\n            + parentFrameName);\n\n    std::ostringstream transValue;\n    transValue << location[0] << " " << location[1] << " " << location[2];\n    SimTK::Xml::Element translationElement("translation", transValue.str());\n    newFrameElement.insertNodeAfter(newFrameElement.element_end(), translationElement);\n\n    std::ostringstream orientValue; \n    orientValue << orientation[0] << " " << orientation[1] << " " << orientation[2];\n    SimTK::Xml::Element orientationElement("orientation", orientValue.str());\n    newFrameElement.insertNodeAfter(newFrameElement.element_end(), orientationElement);\n\n    frames_node->insertNodeAfter(frames_node->element_end(), newFrameElement);\n    //frames_node->writeToString(debug);\n}\n\nstring XMLDocument::updateConnecteePath30517(\n        const std::string& connecteeSetName,\n        const std::string& connecteeName) {\n    std::string connecteePath;\n    if (connecteeSetName == "bodyset" && connecteeName == "ground") {\n        connecteePath = "/" + connecteeName;\n    } else{\n        connecteePath = "/" + connecteeSetName + "/" + connecteeName;\n    }\n    return connecteePath;\n}\n\nSimTK::Xml::Element XMLDocument::findElementWithName(\n        SimTK::Xml::Element& element, const std::string& name) {\n    using namespace SimTK;\n\n    if (name.empty()) return Xml::Element();\n\n    // First, get to the root of the XML document.\n    Xml::Element current = element;\n    while (current.hasParentElement())\n        current = current.getParentElement();\n    Xml::Element root = current;\n\n    // This will be a recursive lambda function.\n    std::function<Xml::Element(Xml::Element&, const std::string&)>\n        searchForElement;\n    // For recursion, must capture the function itself.\n    // Returns an invalid Element if no element with `name` could be found.\n    searchForElement = [&searchForElement](\n            Xml::Element& elem, const std::string& name) -> Xml::Element {\n        // This is a depth-first search.\n        for (auto it = elem.element_begin(); it != elem.element_end();\n                ++it) {\n            std::string elemName = it->getOptionalAttributeValue("name");\n            if (elemName == name)\n                return elem;\n            Xml::Element foundElem = searchForElement(*it, name);\n            if (foundElem.isValid())\n                return foundElem;\n            // Keep searching other branches.\n        }\n        return Xml::Element(); // Did not find.\n    };\n    return searchForElement(root, name);\n}\n', 'message': "Incrementing XMLDocument number per @aymanhab's request in #1002.\n", 'author_name': 'Tom Uchida', 'lang': 'cpp'}