From 9ac94d758bab8d8e67d9031aef251d8ee19dde68 Mon Sep 17 00:00:00 2001 From: Jiri Srain Date: Tue, 15 Nov 2005 09:31:14 +0000 Subject: [PATCH] Added XML parser from liby2util --- configure.ac | 3 +- test/devel.jsrain/PatchRead.cc | 25 ++- zypp/Makefile.am | 3 +- zypp/parser/LibXMLHelper.cc | 67 ++++++ zypp/parser/LibXMLHelper.h | 101 +++++++++ zypp/parser/Makefile.am | 23 ++ zypp/parser/XMLNodeIterator.cc | 276 ++++++++++++++++++++++++ zypp/parser/XMLNodeIterator.h | 480 +++++++++++++++++++++++++++++++++++++++++ 8 files changed, 967 insertions(+), 11 deletions(-) create mode 100644 zypp/parser/LibXMLHelper.cc create mode 100644 zypp/parser/LibXMLHelper.h create mode 100644 zypp/parser/Makefile.am create mode 100644 zypp/parser/XMLNodeIterator.cc create mode 100644 zypp/parser/XMLNodeIterator.h diff --git a/configure.ac b/configure.ac index 63de336..8a23021 100644 --- a/configure.ac +++ b/configure.ac @@ -105,7 +105,8 @@ AC_OUTPUT( \ zypp/Makefile \ zypp/base/Makefile \ zypp/detail/Makefile \ - zypp/capability/Makefile + zypp/capability/Makefile \ + zypp/parser/Makefile ) dnl ================================================== diff --git a/test/devel.jsrain/PatchRead.cc b/test/devel.jsrain/PatchRead.cc index 583e0d6..5fae299 100644 --- a/test/devel.jsrain/PatchRead.cc +++ b/test/devel.jsrain/PatchRead.cc @@ -165,22 +165,21 @@ class MyPatchImpl : public detail::PatchImpl // FIXME mve this piece of code after solutions are selected // this orders the atoms of a patch - ResolvablePtr previous( new Patch( MyPatchImplPtr( this ))); + ResolvablePtr previous; + bool first = true; for (atom_list::iterator it = atoms.begin(); it != atoms.end(); it++) { - DBG << previous->kind() << endl; - if ((string)(previous->kind()) != "patch") + if (! first) { Dependencies deps = (*it)->deps(); - CapSet req = deps.requires(); - req.insert( Capability( _f.parse( "foo", ResKind( "package" )))); - deps.setRequires( req ); -// res->setDeps( deps ); - + CapSet req = deps.prerequires(); + req.insert( Capability( _f.parse( previous->name(), previous->kind()))); + deps.setPrerequires( req ); + (*it)->setDeps( deps ); } - + first = false; previous = *it; } @@ -259,6 +258,14 @@ PatchPtr patch1 (new Patch (q)); DBG << patch1 << endl; DBG << *patch1 << endl; +atom_list at = patch1->atoms(); +for (atom_list::iterator it = at.begin(); + it != at.end(); + it++) +{ + DBG << **it << endl; + DBG << (**it).deps() << endl; +} INT << "===[END]============================================" << endl; return 0; diff --git a/zypp/Makefile.am b/zypp/Makefile.am index 9d97dfc..75589e1 100644 --- a/zypp/Makefile.am +++ b/zypp/Makefile.am @@ -1,7 +1,7 @@ ## Process this file with automake to produce Makefile.in ## ################################################## -SUBDIRS = base detail capability +SUBDIRS = base detail capability parser ## ################################################## @@ -50,5 +50,6 @@ lib@PACKAGE@_la_LDFLAGS = @LIB_VERSION_INFO@ lib@PACKAGE@_la_LIBADD = base/lib@PACKAGE@_base.la \ detail/lib@PACKAGE@_detail.la \ capability/lib@PACKAGE@_capability.la + parser/lib@PACKAGE@_parser.la ## ################################################## diff --git a/zypp/parser/LibXMLHelper.cc b/zypp/parser/LibXMLHelper.cc new file mode 100644 index 0000000..b01338b --- /dev/null +++ b/zypp/parser/LibXMLHelper.cc @@ -0,0 +1,67 @@ +#include +#include +#include +#include +#include + +namespace zypp { + + namespace parser { + + using namespace std; + + LibXMLHelper::LibXMLHelper() + { } + + LibXMLHelper::~LibXMLHelper() + { } + + std::string LibXMLHelper::attribute(xmlNode * nodePtr, + const string &name, + const string &defaultValue) const + { + assert(nodePtr); + xmlChar *xmlRes = xmlGetProp(nodePtr, BAD_CAST(name.c_str())); + if (xmlRes == 0) + return defaultValue; + else { + string res((const char *)xmlRes); + xmlFree(xmlRes); + return res; + } + } + + + std::string LibXMLHelper::content(xmlNode * nodePtr) const + { + assert(nodePtr); + xmlChar *xmlRes = xmlNodeGetContent(nodePtr); + if (xmlRes == 0) + return string(); + else { + string res((const char*) xmlRes); + xmlFree(xmlRes); + return res; + } + } + + std::string LibXMLHelper::name(const xmlNode * nodePtr) const + { + assert(nodePtr); + return string((const char*) nodePtr->name); + } + + + bool LibXMLHelper::isElement(const xmlNode * nodePtr) const + { + return nodePtr->type == XML_ELEMENT_NODE; + } + + std::string LibXMLHelper::positionInfo(const xmlNode * nodePtr) const + { + stringstream strm; + strm << nodePtr->line; + return string("at line ") + strm.str(); + } + } +} diff --git a/zypp/parser/LibXMLHelper.h b/zypp/parser/LibXMLHelper.h new file mode 100644 index 0000000..d277203 --- /dev/null +++ b/zypp/parser/LibXMLHelper.h @@ -0,0 +1,101 @@ +/*---------------------------------------------------------------------\ +| | +| __ __ ____ _____ ____ | +| \ \ / /_ _/ ___|_ _|___ \ | +| \ V / _` \___ \ | | __) | | +| | | (_| |___) || | / __/ | +| |_|\__,_|____/ |_| |_____| | +| | +| core system | +| (C) SuSE GmbH | +\----------------------------------------------------------------------/ + +File: LibXMLHelper.h + +Author: Michael Radziej +Maintainer: Michael Radziej + +Purpose: Helper class to deal with libxml2 with C++ + +/-*/ +#ifndef LibXMLHelper_h +#define LibXMLHelper_h +#include + +extern "C" { + struct _xmlNode; + typedef _xmlNode xmlNode; +} + + namespace zypp { + + namespace parser { + + /** + * @short Easy access to xmlNodes for C++ + */ + + class LibXMLHelper { + public: + /** + * The default constructor will return an object that does not + * look into the namespace properties of the nodes. Later, another + * constructor will be added that takes a list of namespaces as parameters + * (and maybe also character encoding information), and all nodes of different + * namespaces will be ignored (i.e., attributes will not be used, and for elements + * in different namespaces isElement() will return false). + */ + LibXMLHelper(); + + /** + * Destructor + */ + virtual ~LibXMLHelper(); + + /** + * Fetch an attribute + * @param node the xmlNode + * @param name name of the attribute + * @param defaultValue the value to return if this attribute does not exist + * @return the value of the attribute + */ + std::string attribute(xmlNode * node, + const std::string &name, + const std::string &defaultValue = std::string()) const; + + /** + * @short The TEXT content of the node and all child nodes + * Read the value of a node, this can be either the text carried directly by this node if + * it's a TEXT node or the aggregate string of the values carried by this node child's + * (TEXT and ENTITY_REF). Entity references are substituted. + * @param nodePtr the xmlNode + * @return the content + */ + std::string content(xmlNode * nodePtr) const; + + /** + * The name of the node + * @param nodePtr the xmlNode + * @return the name + */ + std::string name(const xmlNode * nodePtr) const; + + /** + * returns whether this is an element node (and not, e.g., a attribute or namespace node) + * @param nodePtr the xmlNode + * @return true if it is an element node + */ + bool isElement(const xmlNode * nodePtr) const; + + /** + * returns a string that identifies the position of an element nodes + * e.g. for error messages + * @param nodePtr the xmlNode + * @return the position information + */ + std::string positionInfo(const xmlNode * nodePtr) const; + }; + } +} + +#endif diff --git a/zypp/parser/Makefile.am b/zypp/parser/Makefile.am new file mode 100644 index 0000000..df1349d --- /dev/null +++ b/zypp/parser/Makefile.am @@ -0,0 +1,23 @@ +## Process this file with automake to produce Makefile.in +## ################################################## + +SUBDIRS = + +INCLUDES = -I$(oldincludedir)/libxml2 + +## ################################################## + +include_HEADERS = \ + XMLNodeIterator.h \ + LibXMLHelper.h + + +noinst_LTLIBRARIES = lib@PACKAGE@_parser.la + +## ################################################## + +lib@PACKAGE@_parser_la_SOURCES = \ + XMLNodeIterator.cc \ + LibXMLHelper.cc + +## ################################################## diff --git a/zypp/parser/XMLNodeIterator.cc b/zypp/parser/XMLNodeIterator.cc new file mode 100644 index 0000000..2b46b63 --- /dev/null +++ b/zypp/parser/XMLNodeIterator.cc @@ -0,0 +1,276 @@ +/*---------------------------------------------------------------------\ +| | +| __ __ ____ _____ ____ | +| \ \ / /_ _/ ___|_ _|___ \ | +| \ V / _` \___ \ | | __) | | +| | | (_| |___) || | / __/ | +| |_|\__,_|____/ |_| |_____| | +| | +| core system | +| (C) SuSE GmbH | +\----------------------------------------------------------------------/ + + File: XMLNodeIterator.cc + + Author: Michael Radziej + Maintainer: Michael Radziej + + Purpose: Provides an iterator interface for XML files + +*/ + +#include +#include <../base/Logger.h> +#include +#include + +namespace zypp { + + namespace parser { + + using namespace std; + + namespace{ + /** + * Internal function to read from the input stream. + * This feeds the xmlTextReader used in the XMLNodeIterator. + * @param context points to the istream to read from + * @param buffer is to be filled with what's been read + * @param bufferLen max memory bytes to read + * @return + */ + int ioread(void *context, + char *buffer, + int bufferLen) + { + assert(buffer); + std::istream *streamPtr = (std::istream *) context; + assert(streamPtr); + streamPtr->read(buffer,bufferLen); + return streamPtr->gcount(); + } + + /** + * Internal function to finish reading. + * This is required by the xmlTextReader API, but + * not needed since the stream will be created when + * the istream object vanishes. + * @param context points to the istream to read from + * @return 0 on success. + */ + int ioclose(void * context) + { + /* don't close. destructor will take care. */ + return 0; + } + } + + XMLParserError::XMLParserError(const char *msg, + int severity, + xmlTextReaderLocatorPtr locator, + int docLine, + int docColumn) + throw() + : _msg(msg), _severity(severity), _locator(locator), + _docLine(docLine), _docColumn(docColumn) + { } + + XMLParserError::~XMLParserError() throw() + { } + + std::string XMLParserError::msg() const throw() + { + return _msg; + } + + int XMLParserError::severity() const throw() + { + return _severity; + } + + xmlTextReaderLocatorPtr XMLParserError::locator() const throw() + { + return _locator; + } + + int XMLParserError::docLine() const throw() + { + return _docLine; + } + + int XMLParserError::docColumn() const throw() + { + return _docColumn; + } + + std::string XMLParserError::position() const throw() + { + if (_docLine!=-1 && _docLine!=-1) { + std::stringstream strm; + strm << "at line " << _docLine + <<", column " << _docColumn; + return strm.str(); + } + else + return ""; + } + + std::ostream& operator<<(std::ostream &out, const XMLParserError& error) + { + const char *errOrWarn = (error.severity() & XML_PARSER_SEVERITY_ERROR) ? "error" : "warning"; + out << "XML syntax " << errOrWarn << ": " << error.msg(); + if (error.docLine()!=-1) { + out << "at line " << error.docLine() + << ", column " << error.docColumn(); + } + out << std::endl; + return out; + } + + + XMLNodeIteratorBase::XMLNodeIteratorBase(std::istream &input, + const std::string &baseUrl, + const char *validationPath) + : _error(0), + _input(& input), + _reader(xmlReaderForIO(ioread, ioclose, _input, baseUrl.c_str(), "utf-8", + XML_PARSE_PEDANTIC)), + _baseUrl(baseUrl) + { + xmlTextReaderSetErrorHandler(_reader, (xmlTextReaderErrorFunc) errorHandler, this); + // xmlTextReaderSetStructuredErrorHandler(_reader, structuredErrorHandler, this); + if (_reader && validationPath) + if (xmlTextReaderRelaxNGValidate + (_reader,validationPath)==-1) + WAR << "Could not enable validation of repomd document" << std::endl; + + /* Derived classes must call fetchNext() in their constructors themselves, + XMLNodeIterator has no access to their virtual functions during + construction */ + } + + XMLNodeIteratorBase::XMLNodeIteratorBase() + : _error(0), _input(0), _reader(0) + { } + + + + XMLNodeIteratorBase::~XMLNodeIteratorBase() + { + if (_reader != 0) + xmlFreeTextReader(_reader); + } + + + bool + XMLNodeIteratorBase::atEnd() const + { + return (_error.get() != 0 + || getCurrent() == 0); + } + + + bool + XMLNodeIteratorBase::operator==(const XMLNodeIteratorBase &other) const + { + if (atEnd()) + return other.atEnd(); + else + return this != & other; + } + + + const XMLParserError * + XMLNodeIteratorBase::errorStatus() const + { + return _error.get(); + } + + + void XMLNodeIteratorBase::fetchNext() + { + assert(_reader); + int status; + /* throw away the old entry */ + setCurrent(0); + + if (_reader == 0) { + /* this is a trivial iterator over (max) only one element, + and we reach the end now. */ + ; + } + else { + /* repeat as long as we successfully read nodes + breaks out when an interesting node has been found */ + while ((status = xmlTextReaderRead(_reader))==1) { + xmlNodePtr node = xmlTextReaderCurrentNode(_reader); + if (isInterested(node)) { + // xmlDebugDumpNode(stdout,node,5); + _process(_reader); + // _currentDataPtr.reset(new ENTRYTYPE(process(_reader))); + status = xmlTextReaderNext(_reader); + break; + } + } + if (status == -1) { // error occured + if (_error.get() == 0) { + errorHandler(this, "Unknown error while parsing xml file\n", + XML_PARSER_SEVERITY_ERROR, 0); + } + } + } + } + + + void + XMLNodeIteratorBase::errorHandler(void * arg, + const char * msg, + int severity, + xmlTextReaderLocatorPtr locator) + { + XMLNodeIteratorBase *obj; + obj = (XMLNodeIteratorBase*) arg; + assert(obj); + xmlTextReaderPtr reader = obj->_reader; + if (strcmp("%s",msg) == 0) { + /* This works around a buglet in libxml2, you often get "%s" as message + and the message is in "severity". Does this work for other + architectures??? FIXME */ + WAR << "libxml2 error reporting defect, got '%s' as message" << endl; + msg = (char *) severity; + severity = XML_PARSER_SEVERITY_WARNING; + } + const char *errOrWarn = (severity & XML_PARSER_SEVERITY_ERROR) ? "error" : "warning"; + std::ostream& out = (severity & XML_PARSER_SEVERITY_ERROR) ? ERR : WAR; + + /* Log it */ + out << "XML syntax " << errOrWarn << ": " << msg; + if (obj->_error.get()) { + out << "(encountered during error recovery!)" << std::endl; + } + if (reader && msg[0] != 0) { + out << "at "; + if (! obj->_baseUrl.empty()) + out << obj->_baseUrl << ", "; + out << "line " << xmlTextReaderGetParserLineNumber(reader) + << ", column " << xmlTextReaderGetParserColumnNumber(reader); + } + out << std::endl; + + /* save it */ + if ((severity & XML_PARSER_SEVERITY_ERROR) + && ! obj->_error.get()) { + if (reader) + obj->_error.reset(new XMLParserError + (msg, severity,locator, + xmlTextReaderLocatorLineNumber(locator), + xmlTextReaderGetParserColumnNumber(reader))); + else + obj->_error.reset(new XMLParserError + (msg, severity, locator, + -1, -1)); + } + } + + } +} diff --git a/zypp/parser/XMLNodeIterator.h b/zypp/parser/XMLNodeIterator.h new file mode 100644 index 0000000..14a7af1 --- /dev/null +++ b/zypp/parser/XMLNodeIterator.h @@ -0,0 +1,480 @@ +/*---------------------------------------------------------------------\ +| | +| __ __ ____ _____ ____ | +| \ \ / /_ _/ ___|_ _|___ \ | +| \ V / _` \___ \ | | __) | | +| | | (_| |___) || | / __/ | +| |_|\__,_|____/ |_| |_____| | +| | +| core system | +| (C) SuSE GmbH | +\----------------------------------------------------------------------/ + + File: XMLNodeIterator.h + + Author: Michael Radziej + Maintainer: Michael Radziej + + Purpose: Provides an iterator interface for XML files + +*/ + +#ifndef XMLNodeIterator_h +#define XMLNodeIterator_h + +#include +#include +#include +#include +#include +#include + +extern "C" { + typedef void * xmlTextReaderLocatorPtr; + struct _xmlNode; + typedef struct _xmlNode xmlNode; + typedef xmlNode *xmlNodePtr; + + struct _xmlTextReader; + typedef _xmlTextReader xmlTextReader; + typedef xmlTextReader *xmlTextReaderPtr; + + struct _xmlError; + typedef _xmlError xmlError; + typedef xmlError *xmlErrorPtr; +} + +namespace zypp { + + namespace parser { + + + /** + * @short class for reporting syntax errors in XMLNodeIterator. + */ + class XMLParserError { + public: + /** + * Constructor + */ + XMLParserError(const char *msg, + int severity, + xmlTextReaderLocatorPtr locator, + int docLine, + int docColumn) throw(); + + ~XMLParserError() throw(); + + /** + * The message of the errors + */ + std::string msg() const throw(); + + /** + * The severity of this error + */ + int severity() const throw(); + + /** + * See libxml2 documentation + */ + xmlTextReaderLocatorPtr locator() const throw(); + + /** + * The line number in the xml document where the error occurred. + */ + int docLine() const throw(); + + /** + * The column number in the xml document where the error occurred. + */ + int docColumn() const throw(); + + /** + * Gives a string describing the position in the xml document. + * (either empty, or "at line ..., column ...") + **/ + std::string position() const throw(); + + private: + + std::string _msg; + int _severity; + xmlTextReaderLocatorPtr _locator; + int _docLine; + int _docColumn; + }; + + + std::ostream& operator<<(std::ostream &out, const XMLParserError& error); + + + + /** + * + * @short Abstract class to iterate over an xml stream + * + * Derive from XMLNodeIterator to get an iterator + * that returns ENTRYTYPE objects. A derived class must provide + * isInterested() and process(). It should also provide a + * Constructor Derived(std::stream,std::string baseUrl) which + * must call fetchNext(). + * + * The derived iterator class should be compatible with an stl + * input iterator. Use like this: + * + * for (Iterator iter(anIstream, baseUrl), + * iter != Iterator.end(), // or: iter() != 0, or ! iter.atEnd() + * ++iter) { + * doSomething(*iter) + * } + * + * The iterator owns the pointer (i.e., caller must not delete it) + * until the next ++ operator is called. At this time, it will be + * destroyed (and a new ENTRYTYPE is created.) + * + * If the input is fundamentally flawed so that it makes no sense to + * continue parsing, XMLNodeIterator will log it and consider the input as finished. + * You can query the exit status with errorStatus(). + */ + + + class XMLNodeIteratorBase { + public: + /** + * Constructor. Derived classes must call fetchNext() here. + * @param input is the input stream (contains the xml stuff) + * @param baseUrl is the base URL of the xml document + * FIXME: use XMLParserError::operator<< instead of doing it on my own. + */ + XMLNodeIteratorBase(std::istream &input, + const std::string &baseUrl, + const char *validationPath = 0); + + /** + * Constructor for an empty iterator. + * An empty iterator is already at its end. + * This is what end() returns ... + */ + XMLNodeIteratorBase(); + + /** + * Destructor + */ + virtual ~XMLNodeIteratorBase(); + + /** + * Have we reached the end? + * A parser error also means "end reached" + * @return whether the end has been reached. + */ + bool atEnd() const; + + /** + * Two iterators are equal if both are at the end + * or if they are identical. + * Since you cannot copy an XMLNodeIterator, everything + * else is not equal. + * @param other the other iterator + * @return true if equal + */ + bool + operator==(const XMLNodeIteratorBase &other) const; + + /** + * Opposit of operator== + * @param other the other iterator + * @return true if not equal + */ + bool + operator!=(const XMLNodeIteratorBase &otherNode) const + { + return ! operator==(otherNode); + } + + /** + * returns the error status or 0 if no error + * the returned pointer is not-owning, + * it will be deleted upon destruction of the XMLNodeIterator. + * @return pointer to error status (if exists) + */ + const XMLParserError * + errorStatus() const; + + protected: + + /** + * filter for the xml nodes + * The derived class decides which xml nodes it is actually interested in. + * For each that is selected, process() will be called an the resulting ENTRYTYPE + * object used as the next value for the iterator. + * Documentation for the node structure can be found in the libxml2 documentation. + * Have a look at LibXMLHelper to access node attributes and contents. + * @param nodePtr points to the xml node in question. Only the node is available, not the subtree. + * See libxml2 documentation. + * @return true if interested + */ + virtual bool + isInterested(const xmlNodePtr nodePtr) = 0; + + /** + * process an xml node and set it as next element + * The derived class has to produce the ENTRYTYPE object here. + * Details about the xml reader is in the libxml2 documentation. + * You'll most probably want to use xmlTextReaderExpand(reader) to + * request the full subtree, and then use the links in the resulting + * node structure to traverse, and class LibXMLHelper to access the + * attributes and element contents. + * fetchNext() cannot throw an error since it will be called in the constructor. + * Instead, in case of a fundamental syntax error the error is saved + * and will be thrown with the next checkError(). + * @param readerPtr points to the xmlTextReader that reads the xml stream. + */ + virtual void + _process(const xmlTextReaderPtr readerPtr) = 0; + + /** + * Fetch the next element and save it as next element + */ + void fetchNext(); + + /** + * Internal function to set the _error variable + * in case of a parser error. It logs the message + * and saves errors in _error, so that they will + * be thrown by checkError(). + * @param arg set to this with xmlReaderSetErrorHandler() + * @param msg the error message + * @param severity the severity + * @param locator as defined by libxml2 + */ + static void + errorHandler(void * arg, + const char * msg, + int severity, + xmlTextReaderLocatorPtr locator); + + + virtual void setCurrent(const void *data) = 0; + virtual void* getCurrent() const = 0; + + private: + + /** + * assignment is forbidden. + * Reason: We can't copy an xmlTextReader + */ + XMLNodeIteratorBase & operator=(const XMLNodeIteratorBase& otherNode); + + /** + * copy constructor is forbidden. + * Reason: We can't copy an xmlTextReader + * FIXME: This prevents implementing the end() method for derived classes. + * + * @param otherNode + * @return + */ + XMLNodeIteratorBase(const XMLNodeIteratorBase& otherNode); + + /** + * if an error occured, this contains the error. + */ + std::auto_ptr _error; + + /** + * contains the istream to read the xml file from. + * Can be 0 if at end or if the current element is the only element left. + **/ + std::istream* _input; + + /** + * contains the xmlTextReader used to parse the xml file. + **/ + xmlTextReaderPtr _reader; + + /** + * contains the base URL of the xml documentation + */ + std::string _baseUrl; + }; /* end class XMLNodeIteratorBase */ + + + + /* --------------------------------------------------------------------------- */ + + template + class XMLNodeIterator : public XMLNodeIteratorBase, + public std::iterator { + public: + /** + * Constructor. Derived classes must call fetchNext() here. + * @param input is the input stream (contains the xml stuff) + * @param baseUrl is the base URL of the xml document + * FIXME: use XMLParserError::operator<< instead of doing it on my own. + */ + XMLNodeIterator(std::istream &input, + const std::string &baseUrl, + const char *validationPath = 0) + : XMLNodeIteratorBase(input, baseUrl, validationPath), _current(0) + { + /* Derived classes must call fetchNext() in their constructors themselves, + XMLNodeIterator has no access to their virtual functions during + construction */ + } + + + /** + * Constructor for a trivial iterator. + * A trivial iterator contains only one element. + * This is at least needed internally for the + * postinc (iter++) operator + * @param entry is the one and only element of this iterator. + */ + XMLNodeIterator(ENTRYTYPE &entry) + : XMLNodeIteratorBase() + { + setCurrent((void *)& entry); + } + + /** + * Constructor for an empty iterator. + * An empty iterator is already at its end. + * This is what end() returns ... + */ + XMLNodeIterator() + : XMLNodeIteratorBase(), _current(0) + { } + + /** + * Destructor + */ + virtual ~XMLNodeIterator() + { } + + /** + * Fetch a pointer to the current element + * @return pointer to the current element. + */ + ENTRYTYPE & + operator*() const + { + assert (! atEnd()); + return * (ENTRYTYPE *) getCurrent(); + } + + /** + * Fetch the current element + * @return the current element + */ + ENTRYTYPE * + operator()() const + { + if (_error) + return 0; + else + return getCurrent(); + } + + /** + * Go to the next element and return it + * @return the next element + */ + XMLNodeIterator & /* ++iter */ + operator++() { + fetchNext(); + return *this; + } + + /** + * remember the current element, go to next and return remembered one. + * avoid this, usually you need the preinc operator (++iter) + * This function may throw ParserError if something is fundamentally wrong + * with the input. + * @return the current element + */ + XMLNodeIterator operator++(int) /* iter++ */ + { + assert (!atEnd()); + XMLNodeIterator tmp(operator()()); + fetchNext(); + return tmp; + } + + /** + * similar to operator*, allows direct member access + * @return pointer to current element + */ + const ENTRYTYPE * + operator->() + { + assert(! atEnd()); + return getCurrent(); + } + + protected: + + /** + * filter for the xml nodes + * The derived class decides which xml nodes it is actually interested in. + * For each that is selected, process() will be called an the resulting ENTRYTYPE + * object used as the next value for the iterator. + * Documentation for the node structure can be found in the libxml2 documentation. + * Have a look at LibXMLHelper to access node attributes and contents. + * @param nodePtr points to the xml node in question. Only the node is available, not the subtree. + * See libxml2 documentation. + * @return true if interested + */ + virtual bool + isInterested(const xmlNodePtr nodePtr) = 0; + + /** + * process an xml node + * The derived class has to produce the ENTRYTYPE object here. + * Details about the xml reader is in the libxml2 documentation. + * You'll most probably want to use xmlTextReaderExpand(reader) to + * request the full subtree, and then use the links in the resulting + * node structure to traverse, and class LibXMLHelper to access the + * attributes and element contents. + * fetchNext() cannot throw an error since it will be called in the constructor. + * Instead, in case of a fundamental syntax error the error is saved + * and will be thrown with the next checkError(). + * @param readerPtr points to the xmlTextReader that reads the xml stream. + * @return + */ + virtual ENTRYTYPE + process(const xmlTextReaderPtr readerPtr) = 0; + + void + _process(const xmlTextReaderPtr readerPtr) + { + setCurrent(new ENTRYTYPE(process(readerPtr))); + } + + private: + + void setCurrent(const void *data) + { + if (data) + _current.reset(new ENTRYTYPE(* (ENTRYTYPE *) data)); + else + _current.reset(0); + } + + void *getCurrent() const + { + return _current.get(); + } + + /** + * contains the current element of the iterator. + * a pointer is used to be able to handle non-assigneable ENTRYTYPEs. + * The iterator owns the element until the next ++ operation. + * It can be 0 when the end has been reached. + **/ + std::auto_ptr _current; + }; /* end class XMLNodeIterator */ + + } +} + +#endif -- 2.7.4