// --------------------------------------------------------------------------- // - XmlNode.cpp - // - afnix:xml module - xml base node class implementation - // --------------------------------------------------------------------------- // - This program is free software; you can redistribute it and/or modify - // - it provided that this copyright notice is kept intact. - // - - // - This program is distributed in the hope that it will be useful, but - // - without any warranty; without even the implied warranty of - // - merchantability or fitness for a particular purpose. In no event shall - // - the copyright holder be liable for any direct, indirect, incidental or - // - special damages arising in any way out of the use of this software. - // --------------------------------------------------------------------------- // - copyright (c) 1999-2007 amaury darsch - // --------------------------------------------------------------------------- #include "XmlNode.hpp" #include "Integer.hpp" #include "Boolean.hpp" #include "Runnable.hpp" #include "XmlSystem.hpp" #include "XmlReader.hpp" #include "QuarkZone.hpp" #include "Exception.hpp" namespace afnix { // ------------------------------------------------------------------------- // - private section - // ------------------------------------------------------------------------- // the default empty flag static const bool XML_EFLG_DEF = false; // the default eol flag static const bool XML_EOLF_DEF = true; // ------------------------------------------------------------------------- // - class section - // ------------------------------------------------------------------------- // create an empty node XmlNode::XmlNode (void) { d_eflg = XML_EFLG_DEF; d_eolf = XML_EOLF_DEF; p_pnod = nilp; } // create a node with an empty flag XmlNode::XmlNode (const bool eflg) { d_eflg = eflg; d_eolf = XML_EOLF_DEF; p_pnod = nilp; } // destroy this node XmlNode::~XmlNode (void) { Object::dref (p_pnod); } // return the node class name String XmlNode::repr (void) const { return "XmlNode"; } // make this node shared void XmlNode::mksho (void) { if (p_shared != nilp) return; Object::mksho (); d_chld.mksho (); } // release all xml node links void XmlNode::release (void) { wrlock (); // protect us Object::iref (this); try { // get the child length long len = lenchild (); for (long i = 0; i < len; i++) { // get the child node XmlNode* node = getchild (i); if (node == nilp) continue; // check if we disconnect the parent if (node->getparent () == this) { //reset the parent node->setparent (nilp); // call the node release node->release (); } } Object::tref (this); unlock (); } catch (...) { Object::tref (this); unlock (); throw; } } // get the empty flag bool XmlNode::geteflg (void) const { rdlock (); bool result = d_eflg; unlock (); return result; } // set the source line number void XmlNode::setlnum (const long lnum) { wrlock (); d_lnum = lnum; unlock (); } // get the source line number long XmlNode::getlnum (void) const { rdlock (); long result= d_lnum; unlock (); return result; } // set the node source name void XmlNode::setsnam (const String& snam) { wrlock (); d_snam = snam; unlock (); } // get the node source name String XmlNode::getsnam (void) const { rdlock (); String result= d_snam; unlock (); return result; } // check if a node name is valid bool XmlNode::isname (const String& name) const { rdlock (); bool result = false; unlock (); return result; } // check if a node index is valid bool XmlNode::isnidx (const long nidx) const { rdlock (); try { // assume not valid bool result = false; // check with parent node if (p_pnod != nilp) { long index = p_pnod->getnidx (this); result = (index == nidx); } unlock (); return result; } catch (...) { unlock (); throw; } } // check if a node attribute exists bool XmlNode::isattr (const String& name) const { rdlock (); bool result = false; unlock (); return result; } // check if a node attribute is valid bool XmlNode::isattr (const String& name, const String& pval) const { rdlock (); bool result = false; unlock (); return result; } // get the parent node XmlNode* XmlNode::getparent (void) const { rdlock (); XmlNode* result = p_pnod; unlock (); return result; } // set the parent node void XmlNode::setparent (XmlNode* node) { wrlock (); Object::iref (node); Object::dref (p_pnod); p_pnod = node; unlock (); } // get a copy of the node tree XmlNode* XmlNode::copy (void) const { rdlock (); XmlNode* result = nilp; try { // get a clone of this node result = dynamic_cast (clone ()); if (result == nilp) { throw Exception ("clone-error", "unable to clone node"); } // loop in the child nodes and copy long len = lenchild (); for (long i = 0; i < len; i++) { XmlNode* node = getchild (i); if (node == nilp) continue; result->addchild (node->copy ()); } // here it is unlock (); return result; } catch (...) { delete result; unlock (); throw; } } // return true if there is no children bool XmlNode::nilchild (void) const { rdlock (); try { bool result = d_chld.empty (); unlock (); return result; } catch (...) { unlock (); throw; } } // return true if a child node exists by name bool XmlNode::ischild (const String& name) const { rdlock (); try { long clen = lenchild (); for (long i = 0; i < clen; i++) { XmlNode* node = getchild (i); if (node == nilp) continue; if (node->isname (name) == true) { unlock (); return true; } } unlock (); return false; } catch (...) { unlock (); throw; } } // return true if a child node exists by name with an attribute bool XmlNode::ischild (const String& name, const String& anam) const { rdlock (); try { long clen = lenchild (); for (long i = 0; i < clen; i++) { XmlNode* node = getchild (i); if ((node != nilp) && (node->isname (name) == true)) { if (node->isattr (anam) == true) { unlock (); return true; } } } unlock (); return false; } catch (...) { unlock (); throw; } } // return true if a child node exists by name with an attribute name // and attribute value bool XmlNode::ischild (const String& name, const String& anam, const String& pval) const { rdlock (); try { long clen = lenchild (); for (long i = 0; i < clen; i++) { XmlNode* node = getchild (i); if ((node != nilp) && (node->isname (name) == true)) { if (node->isattr (anam, pval) == true) { unlock (); return true; } } } unlock (); return false; } catch (...) { unlock (); throw; } } // return the number of children nodes long XmlNode::lenchild (void) const { rdlock (); try { long result = d_chld.length (); unlock (); return result; } catch (...) { unlock (); throw; } } // append a child node to this node void XmlNode::addchild (XmlNode* node) { // check for nil first if (node == nilp) return; // lock, mark parent and append wrlock (); try { if (d_eflg == true) { throw Exception ("xml-error", "trying to add node in empty node"); } // set the parent node node->setparent (this); // append the node d_chld.append (node); unlock (); } catch (...) { unlock(); throw; } } // append a child node to this node at a certain position void XmlNode::addchild (XmlNode* node, const long nidx) { // check for nil first if (node == nilp) return; // lock, mark parent and append wrlock (); try { if (d_eflg == true) { throw Exception ("xml-error", "trying to add node in empty node"); } // set the parent node node->setparent (this); // append the node d_chld.append (node, nidx); unlock (); } catch (...) { unlock(); throw; } } // get a child node by index XmlNode* XmlNode::getchild (const long index) const { rdlock (); try { XmlNode* node = dynamic_cast (d_chld.get (index)); unlock (); return node; } catch (...) { unlock (); throw; } } // get a child node by name if possible XmlNode* XmlNode::getchild (const String& name) const { rdlock (); try { long clen = lenchild (); for (long i = 0; i < clen; i++) { XmlNode* node = getchild (i); if (node == nilp) continue; if (node->isname (name) == true) { unlock (); return node; } } unlock (); return nilp; } catch (...) { unlock (); throw; } } // find a node by name or throw an exception XmlNode* XmlNode::lookchild (const String& name) const { rdlock (); try { XmlNode* result = getchild (name); if (result == nilp) { throw Exception ("lookup-error", "cannot find child node", name); } unlock (); return result; } catch (...) { unlock (); throw; } } // remove a child node by index void XmlNode::delchild (const long nidx) { wrlock (); try { // protect us Object::iref (this); d_chld.remove (nidx); // update self reference Object::tref (this); } catch (...) { unlock (); throw; } } // clear the child node list void XmlNode::clrchild (void) { wrlock (); // protect us Object::iref (this); try { // clear child list d_chld.reset (); // update self Object::tref (this); unlock (); } catch (...) { Object::tref (this); unlock (); throw; } } // find a child node index by node long XmlNode::getnidx (const XmlNode* node) const { rdlock (); try { long clen = lenchild (); for (long i = 0; i < clen; i++) { if (getchild (i) == node) { unlock (); return i; } } throw Exception ("node-error", "cannot find node index"); } catch (...) { unlock (); throw; } } // merge a child node at a certain position void XmlNode::merge (const XmlNode* snod, const long nidx) { // do nothing if nil if (snod == nilp) return; // lock and merge wrlock (); try { // remove the child node at index delchild (nidx); // set the insert index long index = nidx; // get the source node length long slen = snod->lenchild (); for (long i = 0; i < slen; i++) { XmlNode* node = snod->getchild (i); if (node == nilp) continue; addchild (node->copy (), index++); } unlock (); } catch (...) { unlock (); throw; } } // parse a string and add the result as child nodes void XmlNode::parse (const String& s) { wrlock (); try { // create a default reader XmlReader xmlr; // parse the string xmlr.parse (s); // get the result root node XmlRoot* root = xmlr.getroot (); // get the child length and copy the nodes long nlen = (root == nilp) ? 0 : root->lenchild (); for (long i = 0; i < nlen; i++) addchild (root->getchild (i)); // done unlock (); } catch (...) { unlock (); throw Exception ("xml-error", "cannot parse string", s); } } // return a text content of the node tree String XmlNode::totext (void) const { rdlock (); try { String result; long len = lenchild (); for (long i = 0; i < len; i++) { XmlNode* node = getchild (i); if (node == nilp) continue; result += node->totext (); } unlock (); return result; } catch (...) { unlock (); throw; } } // normalize this node and its children void XmlNode::normalize (void) { wrlock (); try { long len = lenchild (); for (long i = 0; i < len; i++) { XmlNode* node = getchild (i); if (node != nilp) node->normalize (); } unlock (); } catch (...) { unlock (); throw; } } // ------------------------------------------------------------------------- // - object section - // ------------------------------------------------------------------------- // the quark zone static const long QUARK_ZONE_LENGTH = 23; static QuarkZone zone (QUARK_ZONE_LENGTH); // the object supported quarks static const long QUARK_COPY = zone.intern ("copy"); static const long QUARK_MERGE = zone.intern ("merge"); static const long QUARK_WRITE = zone.intern ("write"); static const long QUARK_NAMEP = zone.intern ("name-p"); static const long QUARK_ATTRP = zone.intern ("attribute-p"); static const long QUARK_PARSE = zone.intern ("parse"); static const long QUARK_TOTEXT = zone.intern ("to-text"); static const long QUARK_CHILDP = zone.intern ("child-p"); static const long QUARK_GETLNUM = zone.intern ("get-source-line"); static const long QUARK_SETLNUM = zone.intern ("set-source-line"); static const long QUARK_GETSNAM = zone.intern ("get-source-name"); static const long QUARK_SETSNAM = zone.intern ("set-source-name"); static const long QUARK_ADDCHILD = zone.intern ("add-child"); static const long QUARK_GETCHILD = zone.intern ("get-child"); static const long QUARK_DELCHILD = zone.intern ("del-child"); static const long QUARK_GETINDEX = zone.intern ("get-index"); static const long QUARK_CHILDLEN = zone.intern ("child-length"); static const long QUARK_CLRCHILD = zone.intern ("clear-child"); static const long QUARK_GETPARENT = zone.intern ("get-parent"); static const long QUARK_SETPARENT = zone.intern ("set-parent"); static const long QUARK_NILCHILDP = zone.intern ("nil-child-p"); static const long QUARK_LOOKCHILD = zone.intern ("lookup-child"); static const long QUARK_NORMALIZE = zone.intern ("normalize"); // return true if the given quark is defined bool XmlNode::isquark (const long quark, const bool hflg) const { rdlock (); if (zone.exists (quark) == true) { unlock (); return true; } bool result = hflg ? Freeable::isquark (quark, hflg) : false; unlock (); return result; } // apply this object with a set of arguments and a quark Object* XmlNode::apply (Runnable* robj, Nameset* nset, const long quark, Vector* argv) { // get the number of arguments long argc = (argv == nilp) ? 0 : argv->length (); // check for 0 argument if (argc == 0) { if (quark == QUARK_TOTEXT) return new String (totext ()); if (quark == QUARK_CHILDLEN) return new Integer (lenchild ()); if (quark == QUARK_GETLNUM) return new Integer (getlnum ()); if (quark == QUARK_GETSNAM) return new String (getsnam ()); if (quark == QUARK_NILCHILDP) return new Boolean (nilchild ()); if (quark == QUARK_GETPARENT) { rdlock (); Object* result = getparent (); robj->post (result); unlock (); return result; } if (quark == QUARK_WRITE) { Output* os = (robj == nilp) ? nilp : robj->getos (); if (os == nilp) return nilp; write (*os); return nilp; } if (quark == QUARK_COPY) { Object* result = copy (); robj->post (result); return result; } if (quark == QUARK_CLRCHILD) { clrchild (); return nilp; } if (quark == QUARK_NORMALIZE) { normalize (); return nilp; } } // check for 1 argument if (argc == 1) { if (quark == QUARK_NAMEP) { String name= argv->getstring (0); return new Boolean (isname (name)); } if (quark == QUARK_ATTRP) { String name= argv->getstring (0); return new Boolean (isattr (name)); } if (quark == QUARK_CHILDP) { String name= argv->getstring (0); return new Boolean (ischild (name)); } if (quark == QUARK_PARSE) { String s = argv->getstring (0); parse (s); return nilp; } if (quark == QUARK_SETPARENT) { // get the node object Object* obj = argv->get (0); XmlNode* node = dynamic_cast (obj); if ((obj != nilp) && (node == nilp)) { throw Exception ("type-error", "invalid object with set-parent", Object::repr (obj)); } // set the node setparent (node); return nilp; } if (quark == QUARK_ADDCHILD) { // get the node object Object* obj = argv->get (0); XmlNode* node = dynamic_cast (obj); if ((obj != nilp) && (node == nilp)) { throw Exception ("type-error", "invalid object with add-child", Object::repr (obj)); } // set the node addchild (node); return nilp; } if (quark == QUARK_WRITE) { Object* obj = argv->get (0); // check for an output stream Output* os = dynamic_cast (obj); if (os != nilp) { write (*os); return nilp; } // check for a buffer Buffer* buf = dynamic_cast (obj); if (buf != nilp) { write (*buf); return nilp; } throw Exception ("type-error", "invalid object with write", Object::repr (obj)); } if (quark == QUARK_GETCHILD) { Object* obj = argv->get (0); // check for an integer Integer* iobj = dynamic_cast (obj); if (iobj != nilp) { long index = iobj->tointeger (); rdlock (); try { XmlNode* result = getchild (index); robj->post (result); unlock (); return result; } catch (...) { unlock (); throw; } } // check for a string String* sobj = dynamic_cast (obj); if (sobj != nilp) { rdlock (); try { XmlNode* result = getchild (*sobj); robj->post (result); unlock (); return result; } catch (...) { unlock (); throw; } } throw Exception ("type-error", "invalid object with get-child", Object::repr (obj)); } if (quark == QUARK_LOOKCHILD) { String name = argv->getstring (0); rdlock (); try { XmlNode* result = lookchild (name); robj->post (result); unlock (); return result; } catch (...) { unlock (); throw; } } if (quark == QUARK_GETINDEX) { // get the node object Object* obj = argv->get (0); XmlNode* node = dynamic_cast (obj); if ((obj != nilp) && (node == nilp)) { throw Exception ("type-error", "invalid object with get-index", Object::repr (obj)); } // get the node index return new Integer (getnidx (node)); } if (quark == QUARK_DELCHILD) { long nidx = argv->getint (0); delchild (nidx); return nilp; } if (quark == QUARK_SETLNUM) { long lnum = argv->getint (0); setlnum (lnum); return nilp; } if (quark == QUARK_SETSNAM) { String snam = argv->getstring (0); setsnam (snam); return nilp; } } // check for 2 arguments if (argc == 2) { if (quark == QUARK_CHILDP) { String name = argv->getstring (0); String anam = argv->getstring (1); return new Boolean (ischild (name, anam)); } if (quark == QUARK_ATTRP) { String name = argv->getstring (0); String pval = argv->getstring (1); return new Boolean (isattr (name, pval)); } if (quark == QUARK_ADDCHILD) { // get the node object Object* obj = argv->get (0); XmlNode* node = dynamic_cast (obj); if ((obj != nilp) && (node == nilp)) { throw Exception ("type-error", "invalid object with add-child", Object::repr (obj)); } // get the index and add long nidx = argv->getint (1); addchild (node, nidx); return nilp; } if (quark == QUARK_MERGE) { // get the node object Object* obj = argv->get (0); XmlNode* node = dynamic_cast (obj); if ((obj != nilp) && (node == nilp)) { throw Exception ("type-error", "invalid object with merge", Object::repr (obj)); } // get the index and add long nidx = argv->getint (1); merge (node, nidx); return nilp; } } // check for 3 arguments if (argc == 3) { if (quark == QUARK_CHILDP) { String name = argv->getstring (0); String anam = argv->getstring (1); String pval = argv->getstring (2); return new Boolean (ischild (name, anam, pval)); } } // call the freeable method return Freeable::apply (robj, nset, quark, argv); } }