Tekkotsu Homepage
Demos
Overview
Downloads
Dev. Resources
Reference
Credits

plist Namespace Reference


Detailed Description

A collection of classes to implement the Propery List data storage format, a XML standard used by Apple and others.

The plist namespace provides convenient serialization for persistent storage, dynamic introspection, and polymorphic primitive values. The classes happen to use Apple's XML-based "property list" format for serialization, using libxml2 for the low level parsing.

The choice of storage format was based on a desire for human readability, including per-value comments and inline documentation, as well as the desire to make use of syntax hilighting or high-level editing tools.

Individual values are based on templated instances of the Primitive<T> class, while groups of values are based on the Collection interface. Currently, the concrete collections available are Array (one dimensional ordered list of mixed primitive types), ArrayOf<T> (ordered list of a single type), or Dictionary (unordered mapping from string names to mixed primitives).

Generally, there are two ways you might use the functionality contained within this namespace. One is as an external storage interface, such as the STL vector and map classes are used. This might be only a intermediary instance for conversion to and from existing data structures, as a higher-level interface than directly accessing libxml.

However, to fully benefit from this namespace's functionality, you will generally want your classes to inherit from plist::Dictionary, define your members as public plist Primitives and Collections, and register listeners for notification when these values are modified (either by loading from file or programmatic modification.)

Example usage is shown below, you can find this code and more in Tekkotsu/tools/test/plist/.

#include "Shared/plist.h"

using namespace std;
// you might want to 'using namespace plist'...
// we'll be using plist:: scoping everywhere below just for clarity

/* Generic base class for "shapes"... we specify a generic Collection,
 * as its base, allows either Dictionary or Array-based! */
class Shape : virtual public plist::Collection {};


/* We will use a float for coordinates, wrapped by a plist::Primitive<>
 * The Primitive class provides transparent conversions and operations,
 * so we can usually pretend this is just a regular float! */
typedef plist::Primitive<float> coord_t;


/* A point is defined as an array of coordinates, one for each dimension.
 * We'll assume 2D points unless otherwise directed.
 * An alternative definition could use explicit 'x' and 'y' members, and/or
 * use a Dictionary with labels instead of an Array with subscripts... */
class Point : public Shape, virtual public plist::ArrayOf<coord_t> {
public:
  explicit Point(size_t n=2) : plist::ArrayOf<coord_t>(n,0), Shape() { }
  Point(float xVal, float yVal) : plist::ArrayOf<coord_t>(2,0), Shape() {
    getEntry(0)=xVal;
    getEntry(1)=yVal;
  }
};


/* We'll define a rectangle by the lower-left point and the upper-right point.
 * Further, we'll set up an accessor to maintain this invariant. */
class Rectangle : public Shape, public virtual plist::DictionaryOf<Point> {
public:
  Rectangle()
    : plist::DictionaryOf<Point>(), Shape(), lower(), upper(),
    xm(lower[0],upper[0]), ym(lower[1],upper[1])
  {
    /* The next line 'fixes' the entries during load, save, or assignment.
     * The default is to allow dynamic resizing, so you should call this when
     * you expect the entry structure to be immutable. (e.g. if you are using
     * members as collection entries.) */
    setLoadSavePolicy(FIXED,SYNC);
    addEntry("lower",lower); // still can always 'explicitly' add or remove entries
    addEntry("upper",upper); // ... the LoadSavePolicy only restricts 'implicit' changes
  }
  
  /* Can provide public access to members, the Monitor will provide
   * the flexibility usually provided by get/set accessor functions. */
  Point lower, upper;
  
  /* Note automatic conversion to value type, will never be negative due to our Monitor */
  float getWidth() const { return upper[0] - lower[0]; }
  float getHeight() const { return upper[1] - lower[1]; }
  
protected:
  /* Implements plist::PrimitiveListener to receive notification when
   * a member's value is changed.  We could also do this by having
   * rectangle itself be the listener instead of an inner class. */
  class Monitor : public plist::PrimitiveListener {
  public:
    Monitor(coord_t& low, coord_t& high) : _low(low), _high(high) {
      _low.addPrimitiveListener(this);
      _high.addPrimitiveListener(this);
    }
    virtual void plistValueChanged(const plist::PrimitiveBase&) {
      if(_low>_high) std::swap(_low,_high);
    }
  protected:
    coord_t &_low, &_high;
  } xm, ym;
};


/* Here's an example of a purely dynamic class... just a resizeable list
 * of points!  For a "real" implementation, you'd probably want to use
 * a dictionary, add a few more properties (e.g. 'isClosed'), and have
 * the point list as a member variable... */
class Polygon : public Shape, public virtual plist::ArrayOf<Point> {};


// Here's some sample usage...
int main(int argc, const char** argv) {
  Point p;
  p[0] = 1; // transparent conversion from value type to plist::Primitive
  p[1] = 2.5;
  p.saveFile("point.plist");
  
  Rectangle r;
  r.upper = Point(4,5); // we can assign to directly to member...
  r["lower"] = p; // ... or dynamically via operator[] lookup
  r.saveFile("rectangle.plist");
  
  // test the low/high invariant
  cout << "Original r, width: " << r.getWidth() << '\n' << r;
  /* Original r, width: 3
   * lower.0 = 1
   * lower.1 = 2.5
   * upper.0 = 4
   * upper.1 = 5 */
  
  r.upper[0] = -4; // note we assign to upper.x, but it's lower.x that will be -4
  // (because of the PrimitiveListener added by Rectangle's constructor)
  cout << "Negated r, width: " << r.getWidth() << '\n' << r;
  /* Negated r, width: 5
   * lower.0 = -4
   * lower.1 = 2.5
   * upper.0 = 1
   * upper.1 = 5 */
  
  // dynamic resizing:
  Polygon poly;
  /* Notice we are adding pointers here, vs. references in Rectangle,
   * vs. float values in Point.  (De)allocation from pointers or converted 
   * values is handled by the collection, whereas entries added from
   * references are *not* deallocated. */
  poly.addEntry(new Point(1,2));
  poly.addEntry(new Point(3,4));
  poly.addEntry(new Point(5,0));
  poly.saveFile("poly.plist");
  
  return 0;
}


Classes

class  PolymorphicLoader
 allows us to use the LoadSave suite for loading and parsing general XML functions, but forwards loadXML to plist::loadXML() and stores the result as a member More...
class  Listener
 Base class for the plist listener callbacks. More...
class  PrimitiveListener
 If you wish to be notified any time a particular plist primitive's value has been changed, inherit from this and implement the callback, then register it with the plist object through Primitive::addPrimitiveListener(). More...
class  CollectionListener
 If you wish to be notified any time an entry is added, removed, or replaced from a Dictionary, Array, or Vector, inherit from this and implement one or both of the functions, then register it with the collection's addCollectionListener(). More...
class  ObjectBase
 This base class provides the root functionality for all plist entities -- Dictionary and the various templated subclasses of PrimitiveBase. More...
class  PrimitiveBase
 Provides common functionality to all primitive value classes (implemented in a templated subclass Primitive). More...
class  Collection
 Provides a common base class for the collection-oriented primitives, Dictionary and Array. More...
class  DictionaryBase
 Maintains a set of (key,value) pairs, see DictionaryOf, and the Dictionary typedef. More...
class  DictionaryOf
 A dictionary which requires all elements to be subtypes of the PO template argument. More...
class  ArrayBase
 Maintains an array of value, see ArrayOf, and the Array typedef. More...
class  ArrayOf
 A collection of plist objects, similar to a Dictionary, but no keys -- order matters!, see plist::Array. More...
class  Primitive
class  Primitive< char >
class  Primitive< unsigned char >
 provides an unsigned char specialization of plist::Primitive<T>, adds a unique numeric property to the usual template implementation More...
class  Primitive< std::string >
 Provides a std::string specialization of Primitive<T>. More...
class  NamedEnumerationBase
 provides some accessors common across NamedEnumeration types More...
class  NamedEnumeration
 Provides an interface for the use of enumerations in a plist -- you can specify values by either the string name or the corresponding integer value. More...

Typedefs

typedef DictionaryOf< ObjectBaseDictionary
typedef ArrayOf< ObjectBaseArray

Functions

ObjectBaseloadXML (xmlNode *node)
 From the name of node, will instantiate a new ObjectBase subclass to load it.
ObjectBaseloadFile (const std::string &file)
 Attempts to parse the file found at file, using plist::loadXML().
ObjectBaseloadBuffer (const char *buf, unsigned int len)
 Attempts to parse the buffer found at buf, using plist::loadXML().
std::ostream & operator<< (std::ostream &os, const ObjectBase &pb)
 output of an ObjectBase to a stream
std::ostream & operator<< (std::ostream &os, const PrimitiveBase &pb)
 output stringified value (from PrimitiveBase::get()) to stream
std::istream & operator>> (std::istream &is, PrimitiveBase &pb)
 input value from next word in is, via PrimitiveBase::set()
std::ostream & filteredDisplay (std::ostream &os, const ObjectBase &c, const std::string &sel, int selType, unsigned int depth)
 take a regex and maximum depth for display (displays entries whos names match the filter sel
std::ostream & filteredDisplay (std::ostream &os, const ObjectBase &c, const regex_t *reg, unsigned int depth)
 take a compiled regex and maximum depth for display
std::ostream & operator<< (std::ostream &os, const DictionaryBase &d)
 provides textual output
std::ostream & operator<< (std::ostream &os, const ArrayBase &d)
 provides textual output
template<typename T>
const char * getTypeName ()
 returns a string indicating the plist entry type to use for the specified type


Typedef Documentation

plist::Array can handle any mixture of types, whereas plist::ArrayOf<PO> will only accept the plist objects of type PO (or their subclasses). This typedef is simply for convenience and passes ObjectBase for the template parameter.

Definition at line 1174 of file plistCollections.h.

plist::Dictionary can handle any mixture of types, whereas plist::DictionaryOf<PO> will only accept the plist objects of type PO (or their subclasses). This typedef is simply for convenience and passes ObjectBase for the template parameter.

Definition at line 514 of file plistCollections.h.


Function Documentation

std::ostream & plist::filteredDisplay ( std::ostream &  os,
const ObjectBase &  c,
const regex_t *  reg,
unsigned int  depth 
)

take a compiled regex and maximum depth for display

Parameters:
os the ostream to write into
c the collection to dump
reg a pre-compiled regular expression, or NULL to match everything
depth maximum recursion depth for sub-collections

Definition at line 802 of file plistCollections.cc.

std::ostream & plist::filteredDisplay ( std::ostream &  os,
const ObjectBase &  c,
const std::string &  sel,
int  selType,
unsigned int  depth 
)

take a regex and maximum depth for display (displays entries whos names match the filter sel

Parameters:
os the ostream to write into
c the collection to dump
sel the regular expression in string form
selType how to interpret sel, this is the flags argument to regcomp(), e.g. REG_EXTENDED or REG_BASIC
depth maximum recursion depth for sub-collections

Definition at line 792 of file plistCollections.cc.

Referenced by filteredDisplay(), and operator<<().

template<typename T>
const char* plist::getTypeName (  )  [inline]

returns a string indicating the plist entry type to use for the specified type

some primitives (bool, char) aren't handled because they require a specialization of Primitive and won't use this function. If you want to use a plist Primitive of some custom type, you might be able to just define a new specialization of this function and provide iostream <</>> operators for your type...

ObjectBase * plist::loadBuffer ( const char *  buf,
unsigned int  len 
)

Attempts to parse the buffer found at buf, using plist::loadXML().

Definition at line 52 of file plist.cc.

ObjectBase * plist::loadFile ( const std::string &  file  ) 

Attempts to parse the file found at file, using plist::loadXML().

Definition at line 45 of file plist.cc.

ObjectBase * plist::loadXML ( xmlNode node  ) 

From the name of node, will instantiate a new ObjectBase subclass to load it.

The mapping from node names to actual instantiated types is:

If successful, returns a pointer to a newly allocated region, which the caller is responsible for freeing. If an error occurs, NULL is returned.

Definition at line 7 of file plist.cc.

Referenced by plist::PolymorphicLoader::loadXML(), plist::ArrayOf< PO, Alloc >::loadXMLNode(), and plist::DictionaryOf< PO, Alloc >::loadXMLNode().

std::ostream& plist::operator<< ( std::ostream &  os,
const ArrayBase &  d 
) [inline]

provides textual output

Definition at line 1417 of file plistCollections.h.

std::ostream& plist::operator<< ( std::ostream &  os,
const DictionaryBase &  d 
) [inline]

provides textual output

Definition at line 1414 of file plistCollections.h.

std::ostream& plist::operator<< ( std::ostream &  os,
const PrimitiveBase &  pb 
) [inline]

output stringified value (from PrimitiveBase::get()) to stream

Definition at line 275 of file plistBase.h.

std::ostream& plist::operator<< ( std::ostream &  os,
const ObjectBase &  pb 
) [inline]

output of an ObjectBase to a stream

Definition at line 195 of file plistBase.h.

std::istream& plist::operator>> ( std::istream &  is,
PrimitiveBase &  pb 
) [inline]

input value from next word in is, via PrimitiveBase::set()

Definition at line 279 of file plistBase.h.


Tekkotsu v4.0
Generated Thu Nov 22 00:58:51 2007 by Doxygen 1.5.4