E57 Foundation API v1.1.312  Aug. 10, 2011
Public Member Functions
Node Class Reference

Generic handle to any of the 8 types of E57 element objects. More...

List of all members.

Public Member Functions

NodeType type () const
 Return the NodeType of a generic Node.
bool isRoot () const
 Is this a root node.
Node parent () const
 Return parent of node, or self if a root node.
ustring pathName () const
 Get absolute pathname of node.
ustring elementName () const
 Get element name of node.
ImageFile destImageFile () const
 Get the ImageFile that was declared as the destination for the node when it was created.
bool isAttached () const
 Has node been attached into the tree of an ImageFile.
void dump (int indent=0, std::ostream &os=std::cout) const
 Diagnostic function to print internal state of object to output stream in an indented format.
void checkInvariant (bool doRecurse=true, bool doDowncast=true)
 Check whether Node class invariant is true.
bool operator== (Node n2) const
 Test if two node handles refer to the same underlying node.
bool operator!= (Node n2) const
 Test if two node handles refer to different underlying nodes.

Detailed Description

Generic handle to any of the 8 types of E57 element objects.

A Node is a generic handle to an underlying object that is any of the eight type of E57 element objects. Each of the eight node types support the all the functions of the Node class. A Node is a vertex in a tree (acyclic graph), which is a hierarchical organization of nodes. At the top of the hierarchy is a single root Node. If a Node is a container type (StructureNode, VectorNode, CompressedVectorNode) it may have child nodes. The following are non-container type nodes (also known as terminal nodes): IntegerNode, ScaledIntegerNode, FloatNode, StringNode, BlobNode. Terminal nodes store various types of values and cannot have children. Each Node has an elementName, which is a string that uniquely identifies it within the children of its parent. Children of a StructureNode have elementNames that are explicitly given by the API user. Children of a VectorNode or CompressedVectorNode have element names that are string reorientations of the Node's positional index, starting at "0". A path name is a sequence elementNames (divided by "/") that must be traversed to get from a Node to one of its descendents.

Data is organized in an E57 format file (an ImageFile) hierarchically. Each ImageFile has a predefined root node that other nodes can be attached to as children (either directly or indirectly). A Node can exist temporarily without being attached to an ImageFile, however the state will not be saved in the associated file, and the state will be lost if the program exits.

A handle to a generic Node may be safely be converted to and from a handle to the Node's true underlying type. Since an attempt to convert a generic Node to a incorrect handle type will fail with an exception, the true type should be interrogated beforehand.

Due to the set-once design of the Foundation API, terminal nodes are immutable (i.e. their values and attributes can't change after creation). Once a parent-child relationship has been established, it cannot be changed.

Only generic operations are available for a Node, to access more specific operations (e.g. StructureNode::childCount) the generic handle must be converted to the node type of the underlying object. This conversion is done in a type-safe way using "downcasting" (see discussion below).

Downcasting

The conversion from a general handle type to a specific handle type is called "downcasting". Each of the 8 specific node types have a downcast function (see IntegerNode::IntegerNode(const Node&) for example). If a downcast is requested to an incorrect type (e.g. taking a Node handle that is actually a FloatNode and trying to downcast it to a IntegerNode), an E57Exception is thrown with an ErrorCode of E57_ERROR_BAD_NODE_DOWNCAST. Depending on the program design, throwing a bad downcast exception might be acceptable, if an element must be a specific type and no recovery is possible. If a standard requires an element be one several types, then Node::type() should be used to interrogate the type in an if or switch statement. Downcasting is "dangerous" (can fail with an exception) so the API requires the programmer to explicitly call the downcast functions rather than have the c++ compiler insert them automatically.

Upcasting

The conversion of a specific node handle (e.g. IntegerNode) to a general Node handle is called "upcasting". Each of the 8 specific node types have an upcast function (see IntegerNode::operator Node() for example). Upcasting is "safe" (can't cause an exception) so the API allows the c++ compiler to insert them automatically. Upcasting is useful if you have a specific node handle and want to call a function that takes a generic Node handle argument. In this case, the function can be called with the specific handle and the compiler will automatically insert the upcast conversion. This implicit conversion allows one function, with an argument of type Node, to handle operations that apply to all 8 types of nodes (e.g. StructureNode::set()).

Class Invariant

A class invariant is a list of statements about an object that are always true before and after any operation on the object. An invariant is useful for testing correct operation of an implementation. Statements in an invariant can involve only externally visible state, or can refer to internal implementation-specific state that is not visible to the API user. The following C++ code checks externally visible state for consistency and throws an exception if the invariant is violated:

void Node::checkInvariant(bool doRecurse, bool doDowncast) {
    ImageFile imf = destImageFile();

    // If destImageFile not open, can't test invariant (almost every call would throw)
    if (!imf.isOpen())
        return;

    // Parent attachment state is same as this attachment state
    if (isAttached() != parent().isAttached())
        throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);

    // Parent destination ImageFile is same as this
    if (imf != parent().destImageFile())
        throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);

    // If this is the ImageFile root node
    if (*this == imf.root()) {
        // Must be attached
        if (!isAttached())
           throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);

        // Must be is a root node
        if(!isRoot())
           throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);
    }

    // If this is a root node
    if (isRoot()) {
        // Absolute pathName is "/"
        if (pathName() != "/")
           throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);

        // parent() returns this node
        if (*this != parent())
           throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);
    } else {
        // Non-root can't be own parent
        if (*this == parent())
           throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);

        // pathName is concatenation of parent pathName and this elementName
        if (parent().isRoot()) {
            if (pathName() != "/" + elementName())
                throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);
        } else {
            if (pathName() != parent().pathName() + "/" + elementName())
                throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);
        }

        // Non-root nodes must be children of either a VectorNode or StructureNode
        if (parent().type() == E57_VECTOR) {
            VectorNode v = static_cast<VectorNode>(parent());

            // Must be defined in parent VectorNode with this elementName
            if (!v.isDefined(elementName()))
                throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);

            // Getting child of parent with this elementName must return this
            if (v.get(elementName()) != *this)
                throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);
        } else if (parent().type() == E57_STRUCTURE) {
            StructureNode s = static_cast<StructureNode>(parent());

            // Must be defined in parent VectorNode with this elementName
            if (!s.isDefined(elementName()))
                throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);

            // Getting child of parent with this elementName must return this
            if (s.get(elementName()) != *this)
                throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);
        } else
            throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);
    }

    // If this is attached
    if (isAttached()) {
        // Get root of this
        Node n = *this;
        while (!n.isRoot())
            n = n.parent();

        // If in tree of ImageFile (could be in a prototype instead)
        if (n == imf.root()) {
            // pathName must be defined
            if (!imf.root().isDefined(pathName()))
                throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);

            // Getting by absolute pathName must be this
            if (imf.root().get(pathName()) != *this)
                throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);
        }
    }

    // If requested, check invariants of derived types:
    if (doDowncast) {
        switch (type()) {
            case E57_STRUCTURE:         {StructureNode          s(*this);   s.checkInvariant(doRecurse, false); } break;
            case E57_VECTOR:            {VectorNode             v(*this);   v.checkInvariant(doRecurse, false); } break;
            case E57_COMPRESSED_VECTOR: {CompressedVectorNode   cv(*this);  cv.checkInvariant(doRecurse, false);} break;
            case E57_INTEGER:           {IntegerNode            i(*this);   i.checkInvariant(doRecurse, false); } break;
            case E57_SCALED_INTEGER:    {ScaledIntegerNode      si(*this);  si.checkInvariant(doRecurse, false);} break;
            case E57_FLOAT:             {FloatNode              f(*this);   f.checkInvariant(doRecurse, false); } break;
            case E57_STRING:            {StringNode             s(*this);   s.checkInvariant(doRecurse, false); } break;
            case E57_BLOB:              {BlobNode               b(*this);   b.checkInvariant(doRecurse, false); } break;
            default: break;
        }
    }
} // end Node::checkInvariant

See also:
StructureNode, VectorNode, CompressedVectorNode, IntegerNode, ScaledIntegerNode, FloatNode, StringNode, BlobNode
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Defines