DOM guide: Using custom COLLADA data
The COLLADA format has many locations where a user can store extra custom data. This data can be any well formed XML. In the COLLADA DOM this data is stored in objects of the domAny class.
domAny
If the dom* classes are considered "strongly-typed" element classes then the domAny class represents weakly typed elements. A weakly typed element, because of the lack of associated type information, will contain only string data.
Identifying domAny Elements
domAny elements, currenlty in COLLADA 1.4.1, will only be found as children of domTechnique elements. The getElementType method will return a value of COLLADA_TYPE::ANY, which has an int value of 1. The method for identifying elements based on their _Meta type descriptor pointer will not work for domAny elements, though calling getTypeName will return "any". Because of this daeSafeCast will not work for casting daeElement objects to domAny.
Accessing Data
The domAny class provides the getAttributeCount, getAttributeName, and getAttributeValue methods for accessing a domAny's attribute data. The domAny::setAttribute method is used for creating new attributes and setting existing ones. Currently there is no functionality to remove attributes.
The domAny class provides the getValue and setValue to access and modify a domAny's value.
The domAny class can have any number of domAny children.
Working With Custom Data
All of the locations in the COLLADA standard that allow for custom data can contain arbitrary XML elements, which will be weakly typed, or COLLADA elements, that are strongly typed. When validating COLLADA data against the COLLADA schema the weakly typed elements and their children will not be validated. The strongly typed elements will still be validated. This forces custom data to use different element names than the COLLADA elements.
<extra> <technique profile="custom"> <myFoo bar="true"> <myBar>Woot Woot</myBar> </myFoo> <param type="float" name="value">15.0</param> </technique> </extra>
In this example the elements <myFoo> and <myBar> are weakly typed and will not be validated. The <param> element will be validated as a COLLADA param element.
The COLLADA DOM deals with custom data in the same manner. Here is an object hierarchy of how the previous example would be represented in the DOM
domExtra domTechnique domAny domAny domParam
Here is some sample code that reads an <extra> element. It looks for the "custom" technique profile. For this example the "custom" profile can contain only any arbitary XML structures but only COLLADA param strongly typed elements. As a COLLADA implementor you are free to design and use any elements you would like in your own profile.
//extra is of type domExtra and was obtained from the Object Model in a normal mannor. unsigned int numTeqs = extra->getTechnique_array().getCount(); for ( unsigned int currTeq = 0; currTeq < numTeqs; ++currTeq ) { if ( strcmp( extra->getTechnique_array()[currTeq]->getProfile(), "custom" ) == 0 ) ) { domTechnique *teq = extra->getTechnique_array()[currTeq]; unsigned int numChildren = teq->getContents().getCount(); for( unsigned int currChild = 0; currChild < numChildren; ++currChild ) { if ( teq->getContents()[currChild]->getElementType() == COLLADA_TYPE::ANY ) { domAny *child = (domAny*)(daeElement*)teq->getContents()[currChild]; printDomAny( child ); } else if ( teq->getContents()[currChild]->getElementType() == COLLADA_TYPE::PARAM ) { domParam *param = daeSafeCast<domParam>( teq->getContents()[currChild] ); //use param element } else { printf( "Error: Invalid element for profile custom" ); } } } }
void printDomAny( domAny *anyElement, unsigned int indent = 0 ) { char *indentStr = new char[ indent + 1 ]; indentStr[ 0 ] = '\0'; for( unsigned int i = 0; i < indent; ++i ) { strcat( indentStr, "\t" ); } printf( "%sElement: %s\n", indentStr, anyElement->getElementName() ); unsigned int numAttrs = anyElement->getAttributeCount(); for ( unsigned int currAttr = 0; currAttr < numAttrs; ++currAttr ) { printf( "%s\tAttribute: %s has value: %s\n", indentStr, anyElement->getAttributeName( currAttr ), anyElement->getAttributeValue( currAttr ) ); } unsigned int numChildren = anyElement->getContents().getCount(); for ( unsigned int currChild = 0; currChild < numChildren; ++currChild ) { printDomAny( anyElement->getContents()[currChild], indent + 1 ); } if ( anyAttr->getValue() != NULL ) { printf( "%sValue: %s\n", indentStr, anyElement->getValue() ); } delete []indentStr; }
Here is some sample code to programatically create the custom COLLADA shown earlier.
//currElement is any daeElement or subclass that allows for domExtra as a child. domExtra *extra = daeSafeCast<domExtra>( currElement->createAndPlace( COLLADA_ELEMENT_EXTRA ) ); domTechnique *teq = daeSafeCast<domTechnique( extra->createAndPlace( COLLADA_ELEMENT_TECHNIQUE ) ); teq->setProfile( "custom" ); domAny *myFoo = (domAny*)(daeElement*)teq->createAndPlace( "myFoo" ); myFoo->setAttribute( "blah", "true" ); domAny *myBar = (domAny*)(daeElement*)myFoo->createAndPlace( "myBar" ); myBar->setValue( "Woot Woot" ); domParam *param = daeSafeCast<domParam>( teq->createAndPlace( COLLADA_ELEMENT_PARAM ) ); param->setType( "float" ); param->setName( "value" ); param->setValue( "15.0" );
Note the wierd (domAny*)(daeElement*)
typecast. This is required since daeSmartRef<T> can only be cast to a T* and the elements in a _contents array and value returned by createAndPlace are daeElementRef, daeSmartRef<daeElement*>. Trying to cast directly from a daeElementRef to domAny*, or any other type, will result in a compiler error. It is required to cast daeElementRef to a daeElement* before you can cast to domAny*. Using daeSafeCast does not have such an issue.