8.3. Creating Trees from Base Objects Using TreeNodes

To create a class of Base objects that supports persistence and that can be stored and restored using the SaveBase and LoadBase modules (see Section 8.2, “Composing, Storing and Retrieving Base Objects”), the following steps need to be taken:

The following example shows how to implement persistence to a simple class SegmentedObject. The class SegmentedObject is derived from BaseItem which is derived from Base:

Base (abstract class, no members)
     |
BaseItem (name, id)
     |
SegmentedObject (objectGrayValue, voxelCount, boundingBox)

Example 8.1. How to Implement Persistence for Base Objects

Header file of a class SegmentedObject:

class SegmentedObject : public BaseItem {

...

public:
      //! Implement export functionality (as used by the SaveBase module):
      virtual void addStateToTree(TreeNode* parent) const;

      // Set current version number
      ML_SET_ADDSTATE_VERSION(1);

      //! Implement import functionality (as used by the LoadBase module):
      virtual void readStateFromTree(TreeNode* parent);

private:
      //! ML runtime system related stuff
      ML_CLASS_HEADER(SegmentedObject);

      // Members to be (re-)stored:
      //! The identifying gray value of this object
      long _objectGrayValue;

      //! Number of voxels
      long _voxelCount;

      //! Bounding box respective to original image
      SubImageBox* _boundingBox;

...

}


Source file of class SegmentedObject:

Adding the state to the tree:

//! Implement export functionality:
void SegmentedObject::addStateToTree(TreeNode* parent) const
{
  // Write version number (as set in the header)
  ML_ADDSTATE_VERSION(SegmentedObject);

  // Add superclass members:
  ML_ADDSTATE_SUPER(BaseItem);

  // Add this class' members:
  parent->addChild(_objectGrayValue, "ObjectGrayValue");
  parent->addChild(_voxelCount, "VoxelCount");

  // The bounding box is optional, do not write if the pointer is NULL:
  if (_boundingBox) { parent->addChild(*_boundingBox, "BoundingBox"); }
}

Reading the state from the tree:

//! Implement import functionality:
void SegmentedObject::readStateFromTree(TreeNode* parent)
{
  // Read version number
  int version = parent->getVersion("SegmentedObject");

  // Read super class members:
  ML_READSTATE_SUPER(BaseItem);

  // Handle version differences:
  // In this example, version 0 used a different tag for _objectGrayValue
  // and did not write the VoxelCount value.
  switch (version) {
    case 0 :
      // Read object gray value from old tag name
      parent->readChild(_objectGrayValue, "GrayValue");
      break;
    case 1 :
      parent->readChild(_objectGrayValue, "ObjectGrayValue");
      break;
    default:
      // Throw exception: A version upgrade was performed without adapting the version handling
      throw TreeNodeException(TNE_UnsupportedClassVersion);
  }

  // Handle this version difference (voxelCount available or not)
  // by calling the macro ML_READCHILD_OPTIONAL which sets
  // the given variable to a default value (third parameter)
  // in case the tag "VoxelCount" was not found.
  ML_READCHILD_OPTIONAL(_voxelCount, "VoxelCount", 0);

  // Bounding box is optional:
  // However, ML_READCHILD_OPTIONAL is not designed for objects references,
  // hence we have to handle the case manually:
  if (!_boundingBox) { ML_CHECK_NEW(_boundingBox, new SubImageBox()); }
  try {
    parent->readChild(*_boundingBox, "BB");
  }
  catch (const TreeNodeException& e) {
    // Some other exception? Pass problem to caller.
    if (e.getCode() != TNE_ChildNotFound) { throw; }

    // No, a Child Not Found exception occurred, we handle it manually:
    ML_DELETE(_boundingBox);
  }
}

Registering the class in the runtime type system:

  ML_CLASS_SOURCE(SegmentedObject, BaseItem)