// --------------------------------------------------------------------------- // - XsmNode.cpp - // - afnix:xml module - xsm 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 "Item.hpp" #include "Vector.hpp" #include "Integer.hpp" #include "Boolean.hpp" #include "XsmNode.hpp" #include "Runnable.hpp" #include "XsmBuffer.hpp" #include "QuarkZone.hpp" #include "Exception.hpp" namespace afnix { // ------------------------------------------------------------------------- // - private section - // ------------------------------------------------------------------------- // the xsm special characters static const t_quad XSM_CHAR_EP = 0x00000021; // ! static const t_quad XSM_CHAR_LB = 0x0000005B; // [ // this procedure convert a reference to a string static String ref_to_string (const String& xref) { // check for known reference if (xref == "lt") return "<"; if (xref == "gt") return ">"; // build default reference String result = "&"; result += xref; result += ";"; return result; } // this procedure check that a string corresponds to a reserved node static bool is_resv (const String& xval) { // check the size if (xval.isnil () == true) return false; // check for a special starter if (xval[0] == XSM_CHAR_EP) return true; if (xval[0] == XSM_CHAR_LB) return true; return false; } // ------------------------------------------------------------------------- // - class section - // ------------------------------------------------------------------------- // create a default xsm node XsmNode::XsmNode (void) { d_type = TXT_NODE; d_tsub = TAG_TEXT; d_lnum = 0; } // create a text node by value XsmNode::XsmNode (const String& xval) { d_type = TXT_NODE; d_tsub = TAG_TEXT; d_xval = xval; d_lnum = 0; } // create a node by type and value XsmNode::XsmNode (const t_xsmt type, const String& xval) { d_type = type; d_tsub = ((type == TAG_NODE) && is_resv (xval)) ? TAG_RESV : TAG_TEXT; d_xval = xval; d_lnum = 0; } // copy construct this node XsmNode::XsmNode (const XsmNode& that) { that.rdlock (); d_type = that.d_type; d_tsub = that.d_tsub; d_xval = that.d_xval; d_lnum = that.d_lnum; d_snam = that.d_snam; that.unlock (); } // assign a node to this one XsmNode& XsmNode::operator = (const XsmNode& that) { wrlock (); that.rdlock (); d_type = that.d_type; d_tsub = that.d_tsub; d_xval = that.d_xval; d_lnum = that.d_lnum; d_snam = that.d_snam; that.unlock (); unlock (); return *this; } // return the class name String XsmNode::repr (void) const { return "XsmNode"; } // get a clone of this node Object* XsmNode::clone (void) const { return new XsmNode (*this); } // get the node string value String XsmNode::tostring (void) const { rdlock (); String result; switch (d_type) { case TXT_NODE: case TAG_NODE: case END_NODE: result = d_xval; break; case REF_NODE: result = ref_to_string (d_xval); break; } unlock (); return result; } // get the node literal value String XsmNode::toliteral (void) const { rdlock (); String result; switch (d_type) { case TXT_NODE: result = d_xval; break; case TAG_NODE: result = "<"; result += d_xval; result += ">"; break; case END_NODE: result = ""; break; case REF_NODE: result = "&"; result += d_xval; result += ";"; break; } unlock (); return result; } // get the node type XsmNode::t_xsmt XsmNode::gettype (void) const { rdlock (); XsmNode::t_xsmt result = d_type; unlock (); return result; } // get the node subtype XsmNode::t_xsub XsmNode::gettsub (void) const { rdlock (); XsmNode::t_xsub result = d_tsub; unlock (); return result; } // set the source line number void XsmNode::setlnum (const long lnum) { wrlock (); d_lnum = lnum; unlock (); } // get the source line number long XsmNode::getlnum (void) const { rdlock (); long result= d_lnum; unlock (); return result; } // set the node source name void XsmNode::setsnam (const String& snam) { wrlock (); d_snam = snam; unlock (); } // get the node source name String XsmNode::getsnam (void) const { rdlock (); String result= d_snam; unlock (); return result; } // return true if the node is a text node bool XsmNode::istext (void) const { rdlock (); bool result = (d_type == TXT_NODE); unlock (); return result; } // return true if the node is a tag node bool XsmNode::istag (void) const { rdlock (); bool result = (d_type == TAG_NODE); unlock (); return result; } // return true if the node is a normal tag bool XsmNode::isntag (void) const { rdlock (); bool result = (d_type == TAG_NODE) && (d_tsub == TAG_TEXT); unlock (); return result; } // return true if the node is reserved bool XsmNode::isresv (void) const { rdlock (); bool result = (d_type == TAG_NODE) && (d_tsub == TAG_RESV); unlock (); return result; } // return true if the node is a text value node bool XsmNode::isxval (void) const { rdlock (); bool result = ((d_type == TXT_NODE) || (d_type == REF_NODE)); unlock (); return result; } // return true if the node is a reference node bool XsmNode::isref (void) const { rdlock (); bool result = (d_type == REF_NODE); unlock (); return result; } // return true if the node is an end node bool XsmNode::isend (void) const { rdlock (); bool result = (d_type == END_NODE); unlock (); return result; } // get the node name String XsmNode::getname (void) const { return getname (false); } // get the node name by case flag String XsmNode::getname (const bool lwcf) const { rdlock (); try { // create a buffer XsmBuffer xbuf = d_xval; // get the next string String name = xbuf.getnstr (); String result = lwcf ? name.tolower () : name; unlock (); return result; } catch (...) { unlock (); throw; } } // get the tag attribute list Plist XsmNode::getattr (void) const { return getattr (false); } // get the tag attribute list by case flag Plist XsmNode::getattr (const bool lwcf) const { rdlock (); try { // create a buffer XsmBuffer xbuf = d_xval; // remove the name xbuf.getnstr (); // create a plist and add attribute until end Plist result; while (xbuf.isnext () == true) { Property prop = xbuf.getattr (); String name = prop.getname (); String pval = prop.getpval (); if (lwcf == true) { result.add (name.tolower (), pval); } else { result.add (name, pval); } } unlock (); return result; } catch (...) { unlock (); throw; } } // get a vector of words from a text node Strvec XsmNode::getwords (void) const { rdlock (); Strvec result; try { // check for the node if (d_type != TXT_NODE) { unlock (); return result; } // create a working buffer XsmBuffer xbuf = d_xval; // lookp for words while (xbuf.empty () == false) { String word = xbuf.getword (); if (word.isnil () == false) result.add (word); } // here is the vector unlock (); return result; } catch (...) { unlock (); throw; } } // ------------------------------------------------------------------------- // - object section - // ------------------------------------------------------------------------- // the object eval quarks static const long QUARK_XSMNODE = String::intern ("XsmNode"); static const long QUARK_TXTNODE = String::intern ("TXT"); static const long QUARK_TAGNODE = String::intern ("TAG"); static const long QUARK_REFNODE = String::intern ("REF"); static const long QUARK_ENDNODE = String::intern ("END"); // the quark zone static const long QUARK_ZONE_LENGTH = 14; static QuarkZone zone (QUARK_ZONE_LENGTH); // the object supported quarks static const long QUARK_TAGP = zone.intern ("tag-p"); static const long QUARK_REFP = zone.intern ("reference-p"); static const long QUARK_ENDP = zone.intern ("end-p"); static const long QUARK_TEXTP = zone.intern ("text-p"); static const long QUARK_NTAGP = zone.intern ("normal-p"); static const long QUARK_RESVP = zone.intern ("reserved-p"); static const long QUARK_XVALP = zone.intern ("textable-p"); static const long QUARK_GETNAME = zone.intern ("get-name"); static const long QUARK_GETALST = zone.intern ("get-attribute-list"); 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_GETWORDS = zone.intern ("get-words"); // map an item to a node type static inline XsmNode::t_xsmt item_to_type (const Item& item) { // check for a key item if (item.gettid () != QUARK_XSMNODE) throw Exception ("item-error", "item is not a xsm node item"); // map the item to the enumeration long quark = item.getquark (); if (quark == QUARK_TXTNODE) return XsmNode::TXT_NODE; if (quark == QUARK_TAGNODE) return XsmNode::TAG_NODE; if (quark == QUARK_REFNODE) return XsmNode::REF_NODE; if (quark == QUARK_ENDNODE) return XsmNode::END_NODE; throw Exception ("item-error", "cannot map item to xsm node type"); } // map a xsm node type to an item static inline Item* type_to_item (const XsmNode::t_xsmt type) { switch (type) { case XsmNode::TXT_NODE: return new Item (QUARK_XSMNODE, QUARK_TXTNODE); break; case XsmNode::TAG_NODE: return new Item (QUARK_XSMNODE, QUARK_TAGNODE); break; case XsmNode::REF_NODE: return new Item (QUARK_XSMNODE, QUARK_REFNODE); break; case XsmNode::END_NODE: return new Item (QUARK_XSMNODE, QUARK_ENDNODE); break; } return nilp; } // evaluate an object data member Object* XsmNode::meval (Runnable* robj, Nameset* nset, const long quark) { if (quark == QUARK_TXTNODE) return new Item (QUARK_XSMNODE, QUARK_TXTNODE); if (quark == QUARK_TAGNODE) return new Item (QUARK_XSMNODE, QUARK_TAGNODE); if (quark == QUARK_REFNODE) return new Item (QUARK_XSMNODE, QUARK_REFNODE); if (quark == QUARK_ENDNODE) return new Item (QUARK_XSMNODE, QUARK_ENDNODE); throw Exception ("eval-error", "cannot evaluate member", String::qmap (quark)); } // create a new object in a generic way Object* XsmNode::mknew (Vector* argv) { long argc = (argv == nilp) ? 0 : argv->length (); // check for 0 argument if (argc == 0) return new XsmNode; // check for 1 argument if (argc == 1) { String xval = argv->getstring (0); return new XsmNode (xval); } // check for 2 arguments if (argc == 2) { Object* obj = argv->get (0); String xval = argv->getstring (1); // check for an item type Item* iobj = dynamic_cast (obj); if (iobj != nilp) { t_xsmt type = item_to_type (*iobj); return new XsmNode (type, xval); } throw Exception ("type-error", "invalid object with xsm node", Object::repr (obj)); } throw Exception ("argument-error", "too many argument with xsm node constructor"); } // return true if the given quark is defined bool XsmNode::isquark (const long quark, const bool hflg) const { rdlock (); if (zone.exists (quark) == true) { unlock (); return true; } bool result = hflg ? Literal::isquark (quark, hflg) : false; unlock (); return result; } // apply this object with a set of arguments and a quark Object* XsmNode::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_TAGP) return new Boolean (istag ()); if (quark == QUARK_REFP) return new Boolean (isref ()); if (quark == QUARK_ENDP) return new Boolean (isend ()); if (quark == QUARK_TEXTP) return new Boolean (istext ()); if (quark == QUARK_NTAGP) return new Boolean (isntag ()); if (quark == QUARK_RESVP) return new Boolean (isresv ()); if (quark == QUARK_XVALP) return new Boolean (isxval ()); if (quark == QUARK_GETNAME) return new String (getname ()); if (quark == QUARK_GETLNUM) return new Integer (getlnum ()); if (quark == QUARK_GETSNAM) return new String (getsnam ()); if (quark == QUARK_GETALST) return new Plist (getattr ()); if (quark == QUARK_GETWORDS) { Strvec words = getwords (); return words.tovector (); } } // check for 1 argument if (argc == 1) { 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; } if (quark == QUARK_GETNAME) { bool lwcf = argv->getbool (0); return new String (getname (lwcf)); } if (quark == QUARK_GETALST) { bool lwcf = argv->getbool (0); return new Plist (getattr (lwcf)); } } // call the literal method return Literal::apply (robj, nset, quark, argv); } }