blob: f0c730373fa5e74811a69d471bb1f8f65ce33d50 [file] [log] [blame]
/*
* 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;
}