/* * Copyright 2006 Sony Computer Entertainment Inc. * * Licensed under the MIT Open Source License, for details please see license.txt or the website * http://www.opensource.org/licenses/mit-license.php * */ #include <iomanip> #include <dae/daeElement.h> #include <dae/daeArray.h> #include <dae/daeMetaAttribute.h> #include <dae/daeMetaElementAttribute.h> #include <dae/daeMetaElement.h> #include <dae/daeDatabase.h> #include <dae/daeErrorHandler.h> #include <dae/daeURI.h> #include <dae/domAny.h> #include <dae/daeUtils.h> using namespace std; daeElement* daeElement::simpleAdd(daeString name, int index) { if (daeElementRef elt = _meta->create(name)) return add(elt, index); return NULL; } daeElement* daeElement::add(daeString names_, int index) { list<string> names; cdom::tokenize(names_, " ", names); cdom::tokenIter iter = names.begin(); daeElement* root = simpleAdd(iter->c_str(), index); if (!root) return NULL; iter++; daeElement* elt = root; for (; iter != names.end(); iter++) { elt = elt->simpleAdd(iter->c_str()); if (!elt) { removeChildElement(root); return NULL; } } return elt; } daeElement* daeElement::add(daeElement* elt, int index) { if (!elt) return NULL; if (elt == this) return this; bool result = (index == -1 ? _meta->place(this, elt) : _meta->placeAt(index, this, elt)); return result ? elt : NULL; } daeElement* daeElement::addBefore(daeElement* elt, daeElement* index) { if (!index || !elt || index->getParent() != this) return NULL; return _meta->placeBefore(index, this, elt) ? elt : NULL; } daeElement* daeElement::addAfter(daeElement* elt, daeElement* index) { if (!index || !elt || index->getParent() != this) return NULL; return _meta->placeAfter(index, this, elt) ? elt : NULL; } daeElementRef daeElement::createElement(daeString className) { daeElementRef elem = _meta->create(className); // Bug #225 work around // if ( elem != NULL) // elem->ref(); // change premature delete into memory leak. return elem; } daeElement* daeElement::createAndPlace(daeString className) { return add(className); } daeElement* daeElement::createAndPlaceAt(daeInt index, daeString className) { return add(className, index); } daeBool daeElement::placeElement(daeElement* e) { return add(e) != NULL; } daeBool daeElement::placeElementAt(daeInt index, daeElement* e) { return add(e, index) != NULL; } daeBool daeElement::placeElementBefore( daeElement *marker, daeElement *element ) { return addBefore(element, marker) != NULL; } daeBool daeElement::placeElementAfter( daeElement *marker, daeElement *element ) { return addAfter(element, marker) != NULL; } daeInt daeElement::findLastIndexOf( daeString elementName ) { if ( _meta->getContents() != NULL ) { daeElementRefArray* contents = (daeElementRefArray*)_meta->getContents()->getWritableMemory(this); for ( int i = (int)contents->getCount()-1; i >= 0; --i ) { if ( strcmp( contents->get(i)->getElementName(), elementName ) == 0 ) { return i; } } } return -1; } daeBool daeElement::removeChildElement(daeElement* element) { // error traps if(element==NULL) return false; if(element->_parent != this) return false; return _meta->remove( this, element ); } void daeElement::setDocument( daeDocument *c, bool notifyDocument ) { if( _document == c ) return; // Notify our parent document if necessary. if ( _document != NULL && notifyDocument ) _document->removeElement(this); _document = c; if ( _document != NULL && notifyDocument ) _document->insertElement(this); // Notify our attributes daeMetaAttributeRefArray& metaAttrs = getMeta()->getMetaAttributes(); for (size_t i = 0; i < metaAttrs.getCount(); i++) metaAttrs[i]->setDocument(this, c); // Notify our char data object if (getCharDataObject()) getCharDataObject()->setDocument(this, c); // Notify our children daeElementRefArray ea; getChildren( ea ); for ( size_t x = 0; x < ea.getCount(); x++ ) { // Since inserting and removing elements works recursively in the database, // we don't need to notify it about inserts/removals as we process the // children of this element. ea[x]->setDocument( c, false ); } } void daeElement::deleteCMDataArray(daeTArray<daeCharArray*>& cmData) { for (unsigned int i = 0; i < cmData.getCount(); i++) delete cmData.get(i); cmData.clear(); } size_t daeElement::getAttributeCount() { return getMeta()->getMetaAttributes().getCount(); } namespace { // A helper function to get the index of an attribute given the attribute name. size_t getAttributeIndex(daeElement& el, daeString name) { if (el.getMeta()) { daeMetaAttributeRefArray& metaAttrs = el.getMeta()->getMetaAttributes(); for (size_t i = 0; i < metaAttrs.getCount(); i++) if (metaAttrs[i]->getName() && strcmp(metaAttrs[i]->getName(), name) == 0) return i; } return (size_t)-1; } } daeMetaAttribute* daeElement::getAttributeObject(size_t i) { daeMetaAttributeRefArray& attrs = getMeta()->getMetaAttributes(); if (i >= attrs.getCount()) return NULL; return attrs[i]; } daeMetaAttribute* daeElement::getAttributeObject(daeString name) { return getAttributeObject(getAttributeIndex(*this, name)); } std::string daeElement::getAttributeName(size_t i) { if (daeMetaAttribute* attr = getAttributeObject(i)) return (daeString)attr->getName(); return ""; } daeBool daeElement::hasAttribute(daeString name) { return getAttributeObject(name) != 0; } daeBool daeElement::isAttributeSet(daeString name) { size_t i = getAttributeIndex(*this, name); if (i != (size_t)-1) return _validAttributeArray[i]; return false; } std::string daeElement::getAttribute(size_t i) { std::string value; getAttribute(i, value); return value; } void daeElement::getAttribute(size_t i, std::string& value) { value = ""; if (daeMetaAttribute* attr = getAttributeObject(i)) { std::ostringstream buffer; attr->memoryToString(this, buffer); value = buffer.str(); } } std::string daeElement::getAttribute(daeString name) { std::string value; getAttribute(name, value); return value; } void daeElement::getAttribute(daeString name, std::string& value) { getAttribute(getAttributeIndex(*this, name), value); } daeElement::attr::attr() { } daeElement::attr::attr(const std::string& name, const std::string& value) : name(name), value(value) { } daeTArray<daeElement::attr> daeElement::getAttributes() { daeTArray<daeElement::attr> attrs; getAttributes(attrs); return attrs; } void daeElement::getAttributes(daeTArray<attr>& attrs) { attrs.clear(); for (size_t i = 0; i < getAttributeCount(); i++) { std::string value; getAttribute(i, value); attrs.append(attr(getAttributeName(i), value)); } } daeBool daeElement::setAttribute(size_t i, daeString value) { if (daeMetaAttribute* attr = getAttributeObject(i)) { if (attr->getType()) { attr->stringToMemory(this, value); _validAttributeArray.set(i, true); return true; } } return false; } daeBool daeElement::setAttribute(daeString name, daeString value) { return setAttribute(getAttributeIndex(*this, name), value); } // Deprecated daeMemoryRef daeElement::getAttributeValue(daeString name) { if (daeMetaAttribute* attr = getAttributeObject(name)) return attr->get(this); return NULL; } daeMetaAttribute* daeElement::getCharDataObject() { if (_meta) return _meta->getValueAttribute(); return NULL; } daeBool daeElement::hasCharData() { return getCharDataObject() != NULL; } std::string daeElement::getCharData() { std::string result; getCharData(result); return result; } void daeElement::getCharData(std::string& data) { data = ""; if (daeMetaAttribute* charDataAttr = getCharDataObject()) { std::ostringstream buffer; charDataAttr->memoryToString(this, buffer); data = buffer.str(); } } daeBool daeElement::setCharData(const std::string& data) { if (daeMetaAttribute* charDataAttr = getCharDataObject()) { charDataAttr->stringToMemory(this, data.c_str()); return true; } return false; } daeBool daeElement::hasValue() { return hasCharData(); } daeMemoryRef daeElement::getValuePointer() { if (daeMetaAttribute* charDataAttr = getCharDataObject()) return charDataAttr->get(this); return NULL; } void daeElement::setup(daeMetaElement* meta) { if (_meta) return; _meta = meta; daeMetaAttributeRefArray& attrs = meta->getMetaAttributes(); int macnt = (int)attrs.getCount(); _validAttributeArray.setCount(macnt, false); for (int i = 0; i < macnt; i++) { if (attrs[i]->getDefaultValue() != NULL) attrs[i]->copyDefault(this); } //set up the _CMData array if there is one if ( _meta->getMetaCMData() != NULL ) { daeTArray< daeCharArray *> *CMData = (daeTArray< daeCharArray *>*)_meta->getMetaCMData()->getWritableMemory(this); CMData->setCount( _meta->getNumChoices() ); for ( unsigned int i = 0; i < _meta->getNumChoices(); i++ ) { CMData->set( i, new daeCharArray() ); } } } void daeElement::init() { _parent = NULL; _document = NULL; _meta = NULL; _elementName = NULL; _userData = NULL; } daeElement::daeElement() { init(); } daeElement::daeElement(DAE& dae) { init(); } daeElement::~daeElement() { if (_elementName) { delete[] _elementName; _elementName = NULL; } } //function used until we clarify what's a type and what's a name for an element daeString daeElement::getTypeName() const { return _meta->getName(); } daeString daeElement::getElementName() const { return _elementName ? _elementName : (daeString)_meta->getName(); } void daeElement::setElementName( daeString nm ) { if ( nm == NULL ) { if ( _elementName ) delete[] _elementName; _elementName = NULL; return; } if ( !_elementName ) _elementName = new daeChar[128]; strcpy( (char*)_elementName, nm ); } daeString daeElement::getID() const { daeElement* this_ = const_cast<daeElement*>(this); if (_meta) if (daeMetaAttribute* idAttr = this_->getAttributeObject("id")) return *(daeStringRef*)idAttr->get(this_); return NULL; } daeElementRefArray daeElement::getChildren() { daeElementRefArray array; getChildren(array); return array; } void daeElement::getChildren( daeElementRefArray &array ) { _meta->getChildren( this, array ); } daeSmartRef<daeElement> daeElement::clone(daeString idSuffix, daeString nameSuffix) { // Use the meta object system to create a new instance of this element. We need to // create a new meta if we're cloning a domAny object because domAnys never share meta objects. // Ideally we'd be able to clone the _meta for domAny objects. Then we wouldn't need // any additional special case code for cloning domAny. Unfortunately, we don't have a // daeMetaElement::clone method. bool any = typeID() == domAny::ID(); daeElementRef ret = any ? domAny::registerElement(*getDAE())->create() : _meta->create(); ret->setElementName( _elementName ); // Copy the attributes and character data. Requires special care for domAny. if (any) { domAny* thisAny = (domAny*)this; domAny* retAny = (domAny*)ret.cast(); for (daeUInt i = 0; i < (daeUInt)thisAny->getAttributeCount(); i++) retAny->setAttribute(thisAny->getAttributeName(i), thisAny->getAttributeValue(i)); retAny->setValue(thisAny->getValue()); } else { // Use the meta system to copy attributes daeMetaAttributeRefArray &attrs = _meta->getMetaAttributes(); for (unsigned int i = 0; i < attrs.getCount(); i++) { attrs[i]->copy( ret, this ); ret->_validAttributeArray[i] = _validAttributeArray[i]; } if (daeMetaAttribute* valueAttr = getCharDataObject()) valueAttr->copy( ret, this ); } daeElementRefArray children; _meta->getChildren( this, children ); for ( size_t x = 0; x < children.getCount(); x++ ) { ret->placeElement( children.get(x)->clone( idSuffix, nameSuffix ) ); } // Mangle the id if (idSuffix) { std::string id = ret->getAttribute("id"); if (!id.empty()) ret->setAttribute("id", (id + idSuffix).c_str()); } // Mangle the name if (nameSuffix) { std::string name = ret->getAttribute("name"); if (!name.empty()) ret->setAttribute("name", (name + nameSuffix).c_str()); } return ret; } // Element comparison namespace { // Utility functions int getNecessaryColumnWidth(const vector<string>& tokens) { int result = 0; for (size_t i = 0; i < tokens.size(); i++) { int tokenLength = int(tokens[i].length() > 0 ? tokens[i].length()+2 : 0); result = max(tokenLength, result); } return result; } string formatToken(const string& token) { if (token.length() <= 50) return token; return token.substr(0, 47) + "..."; } } // namespace { daeElement::compareResult::compareResult() : compareValue(0), elt1(NULL), elt2(NULL), nameMismatch(false), attrMismatch(""), charDataMismatch(false), childCountMismatch(false) { } string daeElement::compareResult::format() { if (!elt1 || !elt2) return ""; // Gather the data we'll be printing string name1 = formatToken(elt1->getElementName()), name2 = formatToken(elt2->getElementName()), type1 = formatToken(elt1->getTypeName()), type2 = formatToken(elt2->getTypeName()), id1 = formatToken(elt1->getAttribute("id")), id2 = formatToken(elt2->getAttribute("id")), attrName1 = formatToken(attrMismatch), attrName2 = formatToken(attrMismatch), attrValue1 = formatToken(elt1->getAttribute(attrMismatch.c_str())), attrValue2 = formatToken(elt2->getAttribute(attrMismatch.c_str())), charData1 = formatToken(elt1->getCharData()), charData2 = formatToken(elt2->getCharData()), childCount1 = formatToken(cdom::toString(elt1->getChildren().getCount())), childCount2 = formatToken(cdom::toString(elt2->getChildren().getCount())); // Compute formatting information vector<string> col1Tokens = cdom::makeStringArray("Name", "Type", "ID", "Attr name", "Attr value", "Char data", "Child count", 0); vector<string> col2Tokens = cdom::makeStringArray("Element 1", name1.c_str(), type1.c_str(), id1.c_str(), attrName1.c_str(), attrValue1.c_str(), charData1.c_str(), childCount1.c_str(), 0); int c1w = getNecessaryColumnWidth(col1Tokens), c2w = getNecessaryColumnWidth(col2Tokens); ostringstream msg; msg << setw(c1w) << left << "" << setw(c2w) << left << "Element 1" << "Element 2\n" << setw(c1w) << left << "" << setw(c2w) << left << "---------" << "---------\n" << setw(c1w) << left << "Name" << setw(c2w) << left << name1 << name2 << endl << setw(c1w) << left << "Type" << setw(c2w) << left << type1 << type2 << endl << setw(c1w) << left << "ID" << setw(c2w) << left << id1 << id2 << endl << setw(c1w) << left << "Attr name" << setw(c2w) << left << attrName1 << attrName2 << endl << setw(c1w) << left << "Attr value" << setw(c2w) << left << attrValue1 << attrValue2 << endl << setw(c1w) << left << "Char data" << setw(c2w) << left << charData1 << charData2 << endl << setw(c1w) << left << "Child count" << setw(c2w) << left << childCount1 << childCount2; return msg.str(); } namespace { daeElement::compareResult compareMatch() { daeElement::compareResult result; result.compareValue = 0; return result; } daeElement::compareResult nameMismatch(daeElement& elt1, daeElement& elt2) { daeElement::compareResult result; result.elt1 = &elt1; result.elt2 = &elt2; result.compareValue = strcmp(elt1.getElementName(), elt2.getElementName()); result.nameMismatch = true; return result; } daeElement::compareResult attrMismatch(daeElement& elt1, daeElement& elt2, const string& attr) { daeElement::compareResult result; result.elt1 = &elt1; result.elt2 = &elt2; result.compareValue = strcmp(elt1.getAttribute(attr.c_str()).c_str(), elt2.getAttribute(attr.c_str()).c_str()); result.attrMismatch = attr; return result; } daeElement::compareResult charDataMismatch(daeElement& elt1, daeElement& elt2) { daeElement::compareResult result; result.elt1 = &elt1; result.elt2 = &elt2; result.compareValue = strcmp(elt1.getCharData().c_str(), elt2.getCharData().c_str()); result.charDataMismatch = true; return result; } daeElement::compareResult childCountMismatch(daeElement& elt1, daeElement& elt2) { daeElement::compareResult result; result.elt1 = &elt1; result.elt2 = &elt2; daeElementRefArray children1 = elt1.getChildren(), children2 = elt2.getChildren(); result.compareValue = int(children1.getCount()) - int(children2.getCount()); result.childCountMismatch = true; return result; } daeElement::compareResult compareElementsSameType(daeElement& elt1, daeElement& elt2) { // Compare attributes for (size_t i = 0; i < elt1.getAttributeCount(); i++) if (elt1.getAttributeObject(i)->compare(&elt1, &elt2) != 0) return attrMismatch(elt1, elt2, elt1.getAttributeName(i)); // Compare character data if (elt1.getCharDataObject()) if (elt1.getCharDataObject()->compare(&elt1, &elt2) != 0) return charDataMismatch(elt1, elt2); // Compare children daeElementRefArray children1 = elt1.getChildren(), children2 = elt2.getChildren(); if (children1.getCount() != children2.getCount()) return childCountMismatch(elt1, elt2); for (size_t i = 0; i < children1.getCount(); i++) { daeElement::compareResult result = daeElement::compareWithFullResult(*children1[i], *children2[i]); if (result.compareValue != 0) return result; } return compareMatch(); } daeElement::compareResult compareElementsDifferentTypes(daeElement& elt1, daeElement& elt2) { string value1, value2; // Compare attributes. Be careful because each element could have a // different number of attributes. if (elt1.getAttributeCount() > elt2.getAttributeCount()) return attrMismatch(elt1, elt2, elt1.getAttributeName(elt2.getAttributeCount())); if (elt2.getAttributeCount() > elt1.getAttributeCount()) return attrMismatch(elt1, elt2, elt2.getAttributeName(elt1.getAttributeCount())); for (size_t i = 0; i < elt1.getAttributeCount(); i++) { elt1.getAttribute(i, value1); elt2.getAttribute(elt1.getAttributeName(i).c_str(), value2); if (value1 != value2) return attrMismatch(elt1, elt2, elt1.getAttributeName(i)); } // Compare character data elt1.getCharData(value1); elt2.getCharData(value2); if (value1 != value2) return charDataMismatch(elt1, elt2); // Compare children daeElementRefArray children1 = elt1.getChildren(), children2 = elt2.getChildren(); if (children1.getCount() != children2.getCount()) return childCountMismatch(elt1, elt2); for (size_t i = 0; i < children1.getCount(); i++) { daeElement::compareResult result = daeElement::compareWithFullResult(*children1[i], *children2[i]); if (result.compareValue != 0) return result; } return compareMatch(); } } // namespace { int daeElement::compare(daeElement& elt1, daeElement& elt2) { return compareWithFullResult(elt1, elt2).compareValue; } daeElement::compareResult daeElement::compareWithFullResult(daeElement& elt1, daeElement& elt2) { // Check the element name if (strcmp(elt1.getElementName(), elt2.getElementName()) != 0) return nameMismatch(elt1, elt2); // Dispatch to a specific function based on whether or not the types are the same if ((elt1.typeID() != elt2.typeID()) || elt1.typeID() == domAny::ID()) return compareElementsDifferentTypes(elt1, elt2); else return compareElementsSameType(elt1, elt2); } daeURI *daeElement::getDocumentURI() const { if ( _document == NULL ) { return NULL; } return _document->getDocumentURI(); } daeElement::matchName::matchName(daeString name) : name(name) { } bool daeElement::matchName::operator()(daeElement* elt) const { return strcmp(elt->getElementName(), name.c_str()) == 0; } daeElement::matchType::matchType(daeInt typeID) : typeID(typeID) { } bool daeElement::matchType::operator()(daeElement* elt) const { return elt->typeID() == typeID; } daeElement* daeElement::getChild(const matchElement& matcher) { daeElementRefArray children; getChildren(children); for (size_t i = 0; i < children.getCount(); i++) if (matcher(children[i])) return children[i]; return NULL; } daeElement* daeElement::getDescendant(const matchElement& matcher) { daeElementRefArray elts; getChildren(elts); for (size_t i = 0; i < elts.getCount(); i++) { // Check the current element for a match if (matcher(elts[i])) return elts[i]; // Append the element's children to the queue daeElementRefArray children; elts[i]->getChildren(children); size_t oldCount = elts.getCount(); elts.setCount(elts.getCount() + children.getCount()); for (size_t j = 0; j < children.getCount(); j++) elts[oldCount + j] = children[j]; } return NULL; } daeElement* daeElement::getAncestor(const matchElement& matcher) { daeElement* elt = getParent(); while (elt) { if (matcher(elt)) return elt; elt = elt->getParent(); } return NULL; } daeElement* daeElement::getParent() { return _parent; } daeElement* daeElement::getChild(daeString eltName) { if (!eltName) return NULL; matchName test(eltName); return getChild(matchName(eltName)); } daeElement* daeElement::getDescendant(daeString eltName) { if (!eltName) return NULL; return getDescendant(matchName(eltName)); } daeElement* daeElement::getAncestor(daeString eltName) { if (!eltName) return NULL; return getAncestor(matchName(eltName)); } DAE* daeElement::getDAE() { return _meta->getDAE(); } void daeElement::setUserData(void* data) { _userData = data; } void* daeElement::getUserData() { return _userData; }