/*! @file
@ id $ Id $
*/
// 1 2 3 4 5 6 7 8
// 45678901234567890123456789012345678901234567890123456789012345678901234567890
# ifndef LIB_XML_CXX_HXX
# define LIB_XML_CXX_HXX
# include <istream>
# include <sstream>
# include <string>
# include <vector>
# include <map>
# include <memory>
/*! @page limitations Known Limitations
- Templates cannot specify number of sub - elements .
- XML - Comments are only ignored .
- Mixed tags and text is not supported . Tags may either contain
other tags only ( type xml : : Node ) or text only ( type
xml : : String ) .
- Unlimited recursion is not possible
( e . g . & ltp & gt ; & ltp & gt ; & ltp & gt ; & lt / p & gt ; & lt / p & gt ; & lt / p & gt ; )
- No check yet for optional and mandatory attributes in xml : : Factory
- Exceptions should be optional , best effort otherwise ( option " strict " ) */
//! Represents classes for handling C++ access to XML files with strict schema.
/*! The schema ist not presented through xsd, but it can be declared in C++.
A xml : : Factory represents a factory that owns a template and can
instanciate XML trees that are valid for the given template from
streams . If anything is not valid , an exception is thrown . The @ c
what ( ) method of the exception gives additional information about
the problem .
In the following example , we want to represent XML data that are
contained in a & lt ; persons & gt ; tag , and may contain a list of @ c
person . Each @ c person has a mandatory attribute @ c id and
optional @ c member - of . @ c person has a @ c name and may contain a
list of @ c friends , where each @ c friend has an attribute @ c
id . ( The @ c id attribute of course should reference to the @ c id
of another @ c name , but this relationship cannot be declared . )
All tags are by default specified as 0. . n ( optional and any number
there of ) .
@ code
# include <xml-cxx/xml.hxx>
# include <iostream>
[ . . . ]
xml : : Factory test ( xml : : Node ( " persons " ) // root node
< < ( xml : : Node ( " person " ) // child of persons
. attr ( " id " , xml : : mandatory )
. attr ( " member-of " , xml : : optional ) )
< < xml : : String ( " name " ) // the person's name
< < ( xml : : Node ( " friends " ) // friends of person
< < ( xml : : Node ( " friend " ) // a friend
. attr ( " id " , xml : : mandatory ) ) ) ) ) ;
[ . . . ]
try {
std : : auto_ptr < xml : : Node > persons ( test . read ( std : : ifstream ( " file.xml)));
// Here we can be sure, that our structure is valid,
// but we must check optional elements before access, otherwise
// we get an exception.
[ . . . ]
for ( xml : : Node : : size_type i ( 0 ) ; i < persons . children ( ) ; + + i ) {
std : : cout < < " Person: " < < * persons [ i ] [ " name " ] ; // exception if no "name"
if ( persons [ i ] ( " friends " ) ) // check if "friends" is set
std : : cout < < " has " < < persons [ i ] [ " friends " ] . children ( ) < < " friends "
else
std : : cout < < " has no friend list " ;
std : : cout < < std : : endl ;
}
[ . . . ]
} catch ( const std : : exception & x ) {
std : : cerr < < " **** Error in file \" file.xml \" : " < < std : : endl
< < x . what ( ) < < std : : endl ;
}
@ endcode */
namespace xml {
//============================================================================
//! Type of an xml node.
/*! Only start nodes and empty nodes may have attributes. */
enum NodeType {
START , //!< start node, such as <code><node></code>
END , //!< end node, such as <code></node></code>
EMPTY , //!< empty node, such as <code><node/></code>
SPECIAL //!< special node, such as
//! a comment <code><!-- ... --></code>,
//! a xml start indication <code><?xml?></code>
//! or a document type declaration <code><!DOCTYPE ...></code>
} ;
//! Declares an attribute to be mandatory.
const bool mandatory ( true ) ;
//! Declares an attribute to be optional.
const bool optional ( false ) ;
//================================================================= EXCEPTIONS
struct Tag ;
class Attributes ;
class Node ;
class Factory ;
//----------------------------------------------------------------------------
class exception : public std : : exception {
public :
exception ( std : : string reason ) throw ( ) ;
exception ( std : : string reason , const Node & t ) throw ( ) ;
~ exception ( ) throw ( ) ;
void line ( unsigned long line ) throw ( ) ;
const char * what ( ) const throw ( ) ;
private :
std : : string _what ;
Node * _node ;
} ;
//----------------------------------------------------------------------------
class no_parent : public exception {
public :
no_parent ( const Node & t ) throw ( ) : exception ( " node has no parent " , t ) { }
} ;
//----------------------------------------------------------------------------
class tag_expected : public exception {
public :
tag_expected ( const Node & t , const std : : string & txt ) throw ( ) :
exception ( " tag ('<') expected, text not allowed \n text: " + txt , t ) { }
} ;
//----------------------------------------------------------------------------
class type_mismatch : public exception {
public :
type_mismatch ( const Node & t , const std : : string & txt ,
const std : : string & comment ) throw ( ) :
exception ( " wrong type, text contains mismatching character \n " + comment
+ " \n text: " + txt , t ) { }
} ;
//----------------------------------------------------------------------------
class access_error : public exception {
public :
access_error ( const Node & t , const std : : string & name ) throw ( ) ;
~ access_error ( ) throw ( ) { }
const char * what ( ) const throw ( ) ;
private :
std : : string _name ;
} ;
//----------------------------------------------------------------------------
class out_of_range : public exception {
public :
out_of_range ( const Node & t , size_t pos ) throw ( ) ;
~ out_of_range ( ) throw ( ) { }
const char * what ( ) const throw ( ) ;
private :
size_t _pos ;
} ;
//----------------------------------------------------------------------------
class cannot_have_children : public exception {
public :
cannot_have_children ( const Node & parent , const Node & child ) throw ( ) ;
~ cannot_have_children ( ) throw ( ) ;
const char * what ( ) const throw ( ) ;
private :
Node * _child ;
} ;
//----------------------------------------------------------------------------
class attribute_not_available : public exception {
public :
attribute_not_available ( const Node & t , const std : : string & attr ) throw ( ) :
exception ( " attribute \" " + attr + " \" not set " , t ) {
}
} ;
//----------------------------------------------------------------------------
class stream_error : public exception {
public :
stream_error ( const std : : string & reason , const Node & t ,
std : : istream & is , const Tag & tag , char c = 0 ) throw ( ) ;
~ stream_error ( ) throw ( ) ;
const char * what ( ) const throw ( ) ;
private :
std : : istream : : streampos _pos ;
Tag * _tag ;
char _char ;
} ;
//----------------------------------------------------------------------------
class wrong_end_tag : public stream_error {
public :
wrong_end_tag ( const Node & t , std : : istream & is , const Tag & tag , char c = 0 )
throw ( ) :
stream_error ( " mismatching end tag " , t , is , tag , c ) {
}
} ;
class missing_end_tag : public stream_error {
public :
missing_end_tag ( const Node & t , std : : istream & is , const Tag & tag , char c = 0 )
throw ( ) :
stream_error ( " missing end tag, end of file reached " , t , is , tag , c ) {
}
} ;
class wrong_start_tag : public stream_error {
public :
wrong_start_tag ( const Node & t , std : : istream & is , const Tag & tag , char c = 0 )
throw ( ) :
stream_error ( " start tag does not match expected tag " , t , is , tag , c ) {
}
} ;
class second_slash_in_tag : public stream_error {
public :
second_slash_in_tag ( const Node & t , std : : istream & is , const Tag & tag ,
char c = 0 )
throw ( ) :
stream_error ( " a tag may have no more than one slash " , t , is , tag , c ) {
}
} ;
class character_after_slash : public stream_error {
public :
character_after_slash ( const Node & t , std : : istream & is , const Tag & tag ,
char c = 0 )
throw ( ) :
stream_error ( " unexpected character after empty-slash " ,
t , is , tag , c ) {
}
} ;
class attributes_in_end_tag : public stream_error {
public :
attributes_in_end_tag ( const Node & t , std : : istream & is , const Tag & tag )
throw ( ) :
stream_error ( " attributes are not allowed in end tags " , t , is , tag ) {
}
} ;
class attribute_value_not_quoted : public stream_error {
public :
attribute_value_not_quoted ( const Node & t , std : : istream & is ,
const Tag & tag ,
char c , std : : string attr ) throw ( ) :
stream_error ( " attribute values must be quoted ( \" ) \n attribute: " + attr ,
t , is , tag , c ) {
}
} ;
class duplicate_attribute : public stream_error {
public :
duplicate_attribute ( const Node & t , std : : istream & is , const Tag & tag ,
std : : string attr ) throw ( ) :
stream_error ( " attribute duplicated \n attribute: " + attr ,
t , is , tag , 0 ) {
}
} ;
class illegal_attribute : public stream_error {
public :
illegal_attribute ( const Node & t , std : : istream & is , const Tag & tag ,
std : : string attr ) throw ( ) :
stream_error ( " illegal attribute found \n attribute: " + attr ,
t , is , tag , 0 ) {
}
} ;
class mandatory_attribute_missing : public stream_error {
public :
mandatory_attribute_missing ( const Node & t , std : : istream & is ,
const Tag & tag , std : : string attr ) throw ( ) :
stream_error ( " mandatory attribute missing \n attribute: " + attr ,
t , is , tag , 0 ) {
}
} ;
class wrong_node_number : public stream_error {
public :
wrong_node_number ( const Node & t , std : : istream & is ,
const Tag & tag , const std : : string & name ,
unsigned long num ,
unsigned long min , unsigned long max ) throw ( ) :
stream_error ( ( ( std : : stringstream & )
( std : : stringstream ( )
< < " wrong number of child nodes \n name of child: " < < name
< < " \n number of nodes: " < < num
< < " \n minimuml number: " < < min
< < " \n maximum number: " < < max ) ) . str ( ) ,
t , is , tag , 0 ) {
}
} ;
//============================================================================
//----------------------------------------------------------------------------
//! Map for attribute values.
/*! Attributes can be set using method xml::Node::attr(). Check for
an attribute with xml : : Node : : hasAttr ( ) . Attributes must be
unique , which means that every attribute must be set at maximum
once . This is corect : < code > & lt ; node
attribute = " value " & gt ; < / code > , this is not allowed :
< code > & lt ; node attribute = " value " attribute = " value " & gt ; < / code > */
class Attributes : public std : : map < std : : string , std : : string > {
public :
//! Attributes may contain a list of space separated values.
typedef std : : vector < std : : string > List ;
//! Attribute values ar mainly a std::pair.
/*! In addition to a normal std::pair, attributes offer an
assignment operator to set the value , and can be constructed
as empty attribute , given only a key .
@ note Simply use xml : : Attr instead of xml : : Attributes : : Value . */
class Value : public value_type {
public :
Value ( const value_type & o ) throw ( ) ;
Value ( const std : : string & name ) throw ( ) ;
Value ( const std : : string & name , const std : : string & namevalue ) throw ( ) ;
Value & operator = ( const std : : string & value ) throw ( ) ;
const std : : string & name ( ) const throw ( ) ;
const std : : string & value ( ) const throw ( ) ;
std : : string & value ( ) throw ( ) ;
operator bool ( ) const throw ( ) ;
operator unsigned long ( ) const throw ( ) ;
operator List ( ) const throw ( ) ;
List toList ( const std : : string & separators = " \t \n \r " ) const throw ( ) ;
private :
Value ( ) ; // not implemented, key must always be given
} ;
Attributes ( ) throw ( ) ;
Attributes ( const std : : string & empty ) throw ( ) ;
Attributes ( const std : : string & key , const std : : string & value ) throw ( ) ;
Attributes & operator < < ( const Value & v ) throw ( ) ;
Attributes & operator < < ( const std : : string & key ) throw ( ) ;
} ;
//! Simplification: Use xml::Attr instead of xml::Attributes::Value.
typedef Attributes : : Value Attr ;
//----------------------------------------------------------------------------
//! @internal structure for parsing tags
struct Tag {
std : : string name ;
NodeType type ;
std : : string text ;
Attributes attributes ;
std : : string special ;
bool found ;
} ;
//----------------------------------------------------------------------------
//! An xml Node.
/*! XML Nodes may contain either text or other nodes, but not both
at the same time . This node can hold other nodes . For a Node for
text contents , see xml : : String . */
class Node {
private :
typedef std : : vector < Node * > Contents ;
public :
typedef Contents : : size_type size_type ;
typedef std : : vector < Node * > List ;
Node ( std : : string name , size_type min = 0 , size_type max = 0 ) throw ( ) ;
Node ( const Node & o ) throw ( ) ;
Node & operator = ( const Node & o ) throw ( ) ;
virtual ~ Node ( ) throw ( ) ;
virtual std : : auto_ptr < Node > clone ( ) const throw ( ) ;
virtual std : : ostream & out ( std : : ostream & o , unsigned int level = 0 ) const
throw ( ) ;
virtual std : : string text ( ) const throw ( ) ;
virtual Node & text ( const std : : string & txt ) throw ( tag_expected ,
type_mismatch ) ;
virtual Node & append ( const Node & o ) throw ( cannot_have_children ) ;
virtual Node & set ( const Attributes & o ) throw ( ) ;
Node & clear ( ) throw ( ) ;
std : : string name ( ) const throw ( ) ;
bool isChild ( ) const throw ( ) ;
Node & parent ( ) const throw ( no_parent ) ;
bool hasAttr ( const std : : string & name ) const throw ( ) ;
Node & attr ( const std : : string & name , bool mandatory ) throw ( ) ;
Node & attr ( const std : : string & name , const std : : string & deflt ) throw ( ) ;
std : : string attr ( const std : : string & name ) const throw ( ) ;
std : : string & attr ( const std : : string & name ) throw ( ) ;
const Attributes : : Value attribute ( const std : : string & name )
const throw ( attribute_not_available ) ;
const Attributes & attributes ( ) const throw ( ) ;
Attributes & attributes ( ) throw ( ) ;
Node & limits ( size_type min = 0 , size_type max = 0 ) throw ( ) ;
List list ( const std : : string & name ) const throw ( ) ;
bool operator ( ) ( const std : : string & child ) const throw ( ) ;
Node & operator < < ( const Node & o ) throw ( cannot_have_children ) ;
Node & operator < < ( const Attributes & o ) throw ( ) ;
//! Get the number of children.
size_type children ( ) const throw ( ) ;
//! Get the n-th child.
const Node & operator [ ] ( size_type child ) const throw ( out_of_range ) ;
//! Get the n-th child.
Node & operator [ ] ( size_type child ) throw ( out_of_range ) ;
//! Get the first named child.
const Node & operator [ ] ( const std : : string & child ) const
throw ( access_error ) ;
//! Get the first named child.
Node & operator [ ] ( const std : : string & child ) throw ( access_error ) ;
std : : string operator * ( ) const throw ( ) ;
Node & operator = ( const std : : string & contents ) throw ( tag_expected ,
type_mismatch ) ;
friend std : : ostream & operator < < ( std : : ostream & o , const Node & t ) throw ( ) ;
protected :
Attributes _attributes ;
private :
Node * find ( const std : : string & child ) const throw ( ) ;
virtual std : : auto_ptr < Node > clone ( Node * p ) const throw ( ) ;
Node ( ) ; // not implemented
Contents _contents ;
std : : string _name ;
Node * _parent ;
typedef std : : pair < Node : : size_type , Node : : size_type > Limits ;
Limits _limits ;
} ;
//----------------------------------------------------------------------------
class String : public Node {
public :
String ( std : : string name , const std : : string & text = std : : string ( ) ) throw ( ) ;
virtual std : : auto_ptr < Node > clone ( ) const throw ( ) ;
virtual ~ String ( ) throw ( ) { }
virtual std : : string text ( ) const throw ( ) ;
virtual String & text ( const std : : string & txt ) throw ( tag_expected ,
type_mismatch ) ;
virtual std : : ostream & out ( std : : ostream & o , unsigned int level = 0 ) const
throw ( ) ;
virtual String & append ( const Node & o ) throw ( cannot_have_children ) ;
Node & operator = ( const std : : string & contents ) throw ( ) ;
protected :
std : : string _text ;
} ;
//----------------------------------------------------------------------------
class UnsignedInteger : public String {
public :
UnsignedInteger ( std : : string name , unsigned long i = 0 ) throw ( ) ;
virtual std : : auto_ptr < Node > clone ( ) const throw ( ) ;
virtual ~ UnsignedInteger ( ) throw ( ) { }
virtual UnsignedInteger & text ( const std : : string & txt )
throw ( tag_expected , type_mismatch ) ;
unsigned long number ( ) const throw ( ) ;
static unsigned long number ( const Node & node ) throw ( ) ;
} ;
//----------------------------------------------------------------------------
class Factory {
public :
Factory ( const Node & t ) throw ( ) ;
const Node & operator * ( ) const throw ( ) ;
std : : auto_ptr < Node > read ( std : : istream & is )
throw ( wrong_end_tag , wrong_start_tag , tag_expected , type_mismatch ,
second_slash_in_tag , character_after_slash ,
missing_end_tag , attribute_value_not_quoted , access_error ,
duplicate_attribute , attributes_in_end_tag ,
illegal_attribute , mandatory_attribute_missing ,
wrong_node_number ) ;
private :
friend class stream_error ;
Factory ( ) ; // not implemented
Factory ( const Factory & ) ; // not implemented
bool ws ( char c ) throw ( ) ;
std : : auto_ptr < Node > read ( std : : istream & is , const Node & position )
throw ( wrong_end_tag , wrong_start_tag , tag_expected , type_mismatch ,
second_slash_in_tag , character_after_slash ,
missing_end_tag ,
attribute_value_not_quoted , access_error , duplicate_attribute ,
attributes_in_end_tag ,
illegal_attribute , mandatory_attribute_missing ,
wrong_node_number ) ;
Tag tag ( std : : istream & is , const Node & position )
throw ( second_slash_in_tag , wrong_start_tag , character_after_slash ,
missing_end_tag , attributes_in_end_tag , tag_expected ,
attribute_value_not_quoted , access_error , duplicate_attribute ,
illegal_attribute , mandatory_attribute_missing ,
wrong_node_number ) ;
Node _template ;
unsigned long _line ;
long _open ;
} ;
}
# endif