Warnings
- XML handles and interfaces returned by any XML functions (the XML Interface,
XMLNode , or DataMLNode) are only valid until control is returned to the framework (i.e. within the context of a single event fired on a Component). You should discard all XML handles and interfaces when you return control at the end of an event.
- Strings returned by the XML Interface are only valid until control is returned to the framework OR until another XML Interface call is made, whichever is sooner. You should dereference and copy any such string immediately, and then not dereference it again.
Overview
C++ API (1199)
class DataMLNode;
The DataMLNode is an interface to an XMLNode that implements DataML read and write on that node. Since XMLNode is an interface to an underlying physical node, DataMLNode can be seen as just an alternative interface that is more constrained but much easier to use for tasks that are typically performed by a BRAHMS process.
DataML is the XML format used to communicate (State and Log) with all processes supplied with BRAHMS, and with all processes authored by users, at time of writing. Whilst BRAHMS offers full support for the use of any XML format you might choose (to specify your process State and any Log you might return), if you do use a non-DataML format you will need to perform all your own accesses within process (using XMLNode ) and you will need to write codecs for the 995 bindings if you wish to use them with your process. Therefore, you should seriously consider using DataML to communicate with your process before deciding otherwise.
The DataMLNode interface will only read and write DataML nodes (XML nodes with particular attributes and content). Attempting to associate it with any other XML node will raise E_DATAML . The interface, also, can only modify the underlying XML node in ways that leave it as a valid DataML node. Therefore, the XML node underlying DataMLNode is always a DataML node.
Like XMLNode , DataMLNode is (through XMLNode ) an interface to physical XML nodes that are maintained in trees by the framework. The interface implements accessors of that node in a convenient object-oriented form. If you delete a DataMLNode, or it goes out of scope, the underlying tree is unchanged - you just lose your access to the underlying physical node. If you use the interface to create nodes and do not return these to the framework, they will be cleaned up (harmlessly) at termination (but obviously this is not desirable, since it uses memory).
Interface
Create interface and associate with existing physical node
- DataMLNode()
- Create a new DataMLNode that does not interface an XML node. Any call on the interface other than to assign it to an XML node will raise an error. Assignment can be to an existing DataMLNode, or by assigning to an XML node using
operator= (below).
- DataMLNode(
XMLNode & xmlNode) - Create a new interface to the specified XML node (which must be a DataML node, otherwise
E_DATAML is raised). Note that a brand new (empty) XML node is a valid DataML node (it describes an empty string), so it is possible to get an interface to an empty XML node for later modification of that node.
- DataMLNode& operator=(DataMLNode& dataMLNode)
- Let the DataMLNode interface the same XML node as the passed DataMLNode. The two interfaces are identical (and symmetrical), following this call.
- DataMLNode& operator=(
XMLNode & xmlNode) - Let the DataMLNode interface the passed XML node. Restrictions are as for the constructor, above.
Miscellaneous
- void setRootTags()
- Set the "Root Tags" on the DataMLNode. The Root Tags are the tags "Format" and "Version", which allow a general XML parser to identify the node as the root node of a DataML document. Call this function just before returning a new DataML document to the framework (see example, below).
Structure Accessors
DataML nodes represent array objects; therefore, they have a structure much like a Matlab array.
TYPE getType() const- Return the node's Numeric Type Constant (including non-numeric extensions).
TYPE getElementType() const- Return the node's element type (see Numeric Type Constant).
- bool getComplexity() const
- Return true if the node is complex.
- Dims getDims() const
- Return the node's dimensions.
UINT32 getBytesPerElement() const- Return the number of bytes per real element.
UINT32 getNumberOfElementsReal() const- Return the number of real elements.
UINT32 getNumberOfElementsTotal() const- Return the number of real and imaginary elements (just the real count, if the array is not complex).
UINT32 getNumberOfBytesReal() const- Return the number of bytes used to store the real data.
UINT32 getNumberOfBytesTotal() const- Return the number of bytes used to store the real and imaginary data (just the real count, if the array is not complex).
Structure Validation
- const DataMLNode& validate(const Dims& dims) const
- Validate that the dimensions of the node are as specified (else raise
E_DATAML ).
- const DataMLNode& validate(
TYPE type) const - Validate that the type of the node is as specified (else raise
E_DATAML ).
- const DataMLNode& validate(
TYPE type, const Dims& dims) const - Validate both at once.
Accessing "structure" type nodes
- bool isStruct() const
- Return true if the node is
TYPE_STRUCT .
VSTRING getFieldNames() const- Return a list of field names (raises
E_DATAML if the node is not TYPE_STRUCT ).
- bool hasField(const char* nodeName) const
- Returns true if the node has the specified field (raises
E_DATAML if the node is not TYPE_STRUCT ).
- DataMLNode getField(const char* nodeName,
UINT32 index = 0) const - Returns the specified field (raises
E_DATAML if the node is not TYPE_STRUCT , or if the field is not found).
- DataMLNode addField(const char* tagName)
- Adds and returns the specified field (raises
E_DATAML if the node is not TYPE_STRUCT , or if the field already exists). Currently, also raises E_DATAML if the node is not scalar, since I've not been bothered to code this more general case yet.
- void becomeStruct(const Dims& dims,
VSTRING & fieldNames) - Change the underlying node into a structure node - this will clear any existing data on the underlying node, including deleting any descendants it might have.
Accessing "cell" type nodes
- bool isCellArray() const
- Return true if the node is
TYPE_CELL .
- DataMLNode getCell(
UINT32 index) const - Return the contents of the
index th cell(raises E_DATAML if the node is not TYPE_CELL , or the index is out of range).
- void setCell(
UINT32 index, DataMLNode node) - Set the contents of the
index th cell (raises E_DATAML if the node is not TYPE_CELL , or the index is out of range).
- void becomeCell(const Dims& dims)
- Change the underlying node into a cell node - this will clear any existing data on the underlying node, including deleting any descendants it might have.
Accessing "string" type nodes
- std::string getSTRING() const
- Return the node's value as a string (raises
E_DATAML if the node is not a 1xN or empty string).
- void setSTRING(const char* text)
- Set the node's value to be a string (i.e. make the node become a string node).
Accessing "numeric" type nodes (real scalar interface)
These functions raise E_DATAML if the node is not real, scalar, and of the expected element type.
DOUBLE getDOUBLE() const
UINT32 getUINT32() const
INT32 getINT32() const
- bool getBOOL() const
- Return the value of the single element of the node.
Accessing "numeric" type nodes (small array interface)
This interface is convenient for accessing small numeric arrays, but involves a copy operation. Use the large array interface (or fill() ) for larger arrays, to avoid the copy operation.
In strict mode (when F_STRICT is set), this interface will raise E_DATAML if the data type is not as expected. In non-strict mode, this interface will attempt to convert between data types and raise E_DATAML only if the data is non-compliant (cannot be converted without loss of information). Thus, a user can specify a parameter as a DOUBLE (the default data type in Matlab), and have it accepted as a UINT32 , provided it is an integer between 0 and 2^32-1 .
In real mode (when F_REAL is set), this interface will raise E_DATAML if the data is not real. Thus, if you intend to accept both real and complex data, you should unset F_REAL before use.
The number of elements in the returned vector is equal to the value returned by getNumberOfElementsTotal() .
- VX getArrayX() const
- Return the node's contents as a vector, where X is any of
DOUBLE , SINGLE , UINT64 , UINT32 , UINT16 , UINT8 , INT64 , INT32 , INT16 , INT8 .
Vbool getArrayBOOL() const- Return the node's contents as a vector of "bool" type.
- Dims getArrayDims() const
- Return the node's contents as a Dims object (this is equivalent, apart from return type, to a call to
getArrayINT64() , but provides the data in a form that is appropriate if they represent a set of dimensions).
Accessing "numeric" type nodes (large array interface)
This interface is identical in operation to the small array interface, but avoids the copy operation. The return value is equal to the argument.
- VX& getArray(VX& dst) const
- Return the node's contents as a vector.
Vbool & getArray(Vbool & dst) const- Return the node's contents as a vector.
- Dims& getArray(Dims& dst) const
- Return the node's contents as a Dims object.
Accessing "numeric" type nodes (set interface)
- void setArray(const Dims& dims, const VX& src)
- Set the node's real and imaginary data from the passed vector, where X is any of
DOUBLE , SINGLE , UINT64 , UINT32 , UINT16 , UINT8 , INT64 , INT32 , INT16 , INT8 , bool. src must contain either dims.getNumberOfElements() elements, or twice that number, from which the complexity is inferred (else E_INVALID_ARG is raised).
- void setArray(const Dims& dims, const Dims& src)
- As above, but the data are provided in a Dims object (
dims must indicate that the data is one-dimensional, and the single dimension, N , must be equal to the number of elements in src - complex data cannot be represented).
Accessing "numeric" type nodes (raw interface)
- void getRaw(
BYTE * p_real, BYTE * p_imag = NULL) const - Fill the passed memory blocks with the node's real and imaginary data. The number of bytes that will be written to each memory block is equal to the return value of
getNumberOfElementsReal() . p_imag must be NULL if the data is real, and non-NULL if the data is complex (else E_DATAML is raised).
- void setRaw(const Dims& dims,
TYPE type, const BYTE * p_real, const BYTE * p_imag = NULL) - Set the node's real and, if complex, imaginary data from the passed memory blocks. If type indicates
TYPE_REAL , N bytes are read from p_real , and p_imag must be NULL. If type indicates TYPE_COMPLEX , N bytes are read from each pointer, unless p_imag is NULL, in which case Nx2 bytes are read from p_real . If type indicates TYPE_COMPLEX_INTERLEAVED, Nx2 bytes are read from p_real , and p_imag must be NULL. N is equal to dims.getNumberOfElements() * TYPE_BYTES (type) .
Accessing "numeric" type nodes (binary set interface)
If the caller has already written the node data into a binary file (during execution, for instance), it can set the node to be an unencapsulated node by passing the name of the binary file.
- void setBinaryFile(const Dims& dims,
TYPE type, const char* filename) - Set the unencapsulated binary file of the node. The size of the binary file should match the passed
dims and type , if the node is to be readable, but is not checked by the function.
Mode Changes
- DataMLNode& set(
UINT16 flag), DataMLNode& unset(UINT16 flag) - Set or unset (clear) the specified node flag. Returns a reference to the node. Flags are listed below.
- DataMLNode& precision(
INT16 precision) - Set the precision at which the array should write data during serialization, if requested.
precision must be between 0 and 31 (inclusive), or PRECISION_NOT_SET .
Flags
- F_STRICT
- (initially UNSET) If set, the node is in "strict" mode, and compliant but incorrect numeric data will not be passed during calls that perform auto-validation.
- F_REAL
- (initially SET) If set, complex data will be rejected on a call to the real scalar interface or the small or large array interfaces. Thus, to accept real and complex data, you must unset this flag (to accept only complex data, you should use node.unset(F_REAL).validate(
TYPE_COMPLEX ).get(...)).
Example
Read
The typical way to use DataMLNode to read a tree is by laying it over an XMLNode , and using this newly created interface to read from the XML, as follows.
C++ Source Code (against 1199)
case EVENT_STATE_SET:
{
EventStateSet * data = (EventStateSet *) event ->data ;
XMLNode xmlNode (hComponent , data ->state );
DataMLNode nodeState (xmlNode );
string fieldValue = nodeState .getField ("fieldName").getSTRING ();
DataMLNode nodeSubTree = nodeState .getField ("subTree");
VDOUBLE v = nodeSubTree .getField ("fieldName").getArrayDOUBLE ();
return C_OK ;
}
Write
The typical way to write a tree is by starting an XMLNode tree (which implicitly creates a BRAHMS XML node), wrapping that in a DataMLNode interface, and writing through the interface, as follows.
C++ Source Code (against 1199)
case EVENT_LOG_TERM:
{
XMLNode xmlNode (hComponent , "Log");
DataMLNode nodeLog (xmlNode );
VSTRING fieldNames ;
fieldNames .push_back ("abc");
fieldNames .push_back ("def");
fieldNames .push_back ("ghi");
nodeLog .becomeStruct (Dims(1 ), fieldNames );
nodeLog .getField ("abc").setSTRING ("ABC");
nodeLog .getField ("def").setSTRING ("DEF");
DataMLNode nodeCells = nodeLog .getField ("ghi");
nodeCells .becomeCell (Dims(2 , 3 ));
DataMLNode nodeCell = nodeCells .getCell (0 );
nodeCell .setSTRING ("the first cell");
nodeCell = nodeCells .getCell (5 );
VDOUBLE v ;
v .push_back (1 .23456789 );
v .push_back (13 );
v .push_back (42 .592 );
nodeCell .precision (3 );
nodeCell .setArray (Dims(3 ), v );
nodeLog .setRootTags ();
EventLog * data = (EventLog *) event ->data ;
data ->result = xmlNode .element ;
return C_OK ;
}
Abuse
This example below shows how to misuse the interface, not how to use it. Since DataMLNode is only an interface to physical XML nodes, it is possible to use an interface to a node that no longer exists. This won't typically happen because you are either creating nodes to return to the framework (i.e. not deleting them), or you are reading nodes that you were passed (i.e. not deleting them). However, just to make the point, here's how you make things go wrong.
C++ API (1199)
XMLNode rootNode (hComponent , (const char *) NULL );
DataMLNode node1 (rootNode );
node1 .becomeCell (Dims(3 ));
DataMLNode node2 = node1 .getCell (1 );
node1 .setArray (Dims(2 ), VDOUBLE (2 , 42 ));
node2 .becomeCell (Dims(3 ));
The result is as follows.
Matlab Console
E_INVALID_HANDLE: invalid object handle/index (0x20000076, range was 117)
at register.cpp:161
in brahms::XMLNode::clear() at brahms-1199.h:784
|