// --------------------------------------------------------------------------- // - XsmDocument.cpp - // - afnix:xml module - xsm document 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 "Vector.hpp" #include "Integer.hpp" #include "Runnable.hpp" #include "InputFile.hpp" #include "QuarkZone.hpp" #include "Exception.hpp" #include "XsmBuffer.hpp" #include "XsmReader.hpp" #include "XsmDocument.hpp" namespace afnix { // ------------------------------------------------------------------------- // - private section - // ------------------------------------------------------------------------- // this procedure reads an input stream and return a vector static Vector* get_xdoc_tree (Input* is) { // create a result vector Vector* result = new Vector; // check the stream if (is == nilp) return result; // create a new reader and parse the input stream XsmReader* xsmr = new XsmReader (is); try { while (true) { XsmNode* node = xsmr->getnode (); if (node == nilp) break; result->append (node); } delete xsmr; return result; } catch (...) { delete xsmr; delete result; throw; } } // this procedure reads an input file and returns a root node // the reference counter for the node is already increased static Vector* get_xdoc_tree (const String& name) { // open the file InputFile is (name); // by default we operate in UTF-8 mode is.setemod (System::UTF8); // get document vector return get_xdoc_tree (&is); } // ------------------------------------------------------------------------- // - class section - // ------------------------------------------------------------------------- // create a default document XsmDocument::XsmDocument (void) { p_tree = nilp; } // create a document by name XsmDocument::XsmDocument (const String& name) { // create a reader d_name = name; Object::iref (p_tree = get_xdoc_tree (name)); } // create a document by name and input stream XsmDocument::XsmDocument (const String& name, Input* is) { d_name = name; Object::iref (p_tree = get_xdoc_tree (is)); } // destroy this document XsmDocument::~XsmDocument (void) { Object::dref (p_tree); } // return the document class name String XsmDocument::repr (void) const { return "XsmDocument"; } // make this document shared void XsmDocument::mksho (void) { if (p_shared != nilp) return; Object::mksho (); if (p_tree != nilp) p_tree->mksho (); } // return the document name String XsmDocument::getname (void) const { rdlock (); String result = d_name; unlock (); return result; } // set the document name void XsmDocument::setname (const String& name) { wrlock (); d_name = name; unlock (); } // return the document length long XsmDocument::length (void) const { rdlock (); long result = (p_tree == nilp) ? 0 : p_tree->length (); unlock (); return result; } // get a node by index XsmNode* XsmDocument::getnode (const long index) const { rdlock (); try { if (p_tree == nilp) { throw Exception ("index-error", "out of bound node index"); } XsmNode* result = dynamic_cast (p_tree->get (index)); unlock (); return result; } catch (...) { unlock (); throw; } } // get an info node by index XsoInfo* XsmDocument::getinfo (const long index) const { return getinfo (index, false); } // get an info node by index and case flag XsoInfo* XsmDocument::getinfo (const long index, const bool lwcf) const { rdlock (); try { // check we have a tag node XsmNode* tag = getnode (index); if ((tag == nilp) || (tag->isntag () == false)) { throw Exception ("type-error", "invalid node index for get-info"); } // get the tag name String name = tag->getname (lwcf); // get the tag attribute list Plist alst = tag->getattr (lwcf); // get the tag text info XsmBuffer xbuf; bool xvok = false; // loop until the end tag and accumulate text long len = length (); for (long i = index + 1; i < len; i++) { XsmNode* node = getnode (i); if (node == nilp) continue; // check if we have a end node if (node->isend () == true) { if (name == node->getname (lwcf)) { xvok = true; break; } continue; } // check if we have a normal tag match and avoid recursion if (node->isntag () == true) { if (name == node->getname (lwcf)) break; } // check if we have a xval node if (node->isxval () == true) xbuf.add (node->tostring ()); } // check if the text is ok if (xvok == true) { xbuf.stripm (); xbuf.strip (); } else { xbuf.reset (); } String xval = xbuf.tostring (); // create here the info node XsoInfo* result = new XsoInfo (name, alst, xval); unlock (); return result; } catch (...) { unlock (); throw; } } // get an info vector by name Vector* XsmDocument::getivec (const String& name) const { return getivec (name, false); } // get an info vector by name and case flag Vector* XsmDocument::getivec (const String& name, const bool lwcf) const { rdlock (); // make sure the argument follow the flag String tnam = lwcf ? name.tolower () : name; // create the result vector Vector* result = new Vector; // loop in the document try { long dlen = length (); for (long i = 0; i < dlen; i++) { XsmNode* node = getnode (i); if (node == nilp) continue; if (node->isntag () == false) continue; if (node->getname (lwcf) == tnam) result->append (getinfo (i)); } unlock (); return result; } catch (...) { delete result; unlock (); throw; } } // get a vector of words Vector* XsmDocument::getwords (void) const { rdlock (); Vector* result = new Vector; try { // loop in the document nodes long dlen = length (); for (long i = 0; i < dlen; i++) { // get the node XsmNode* node = getnode (i); if (node == nilp) continue; // get the word vector Strvec words = node->getwords (); // copy the vector into the results long vlen = words.length (); for (long j = 0; j < vlen; j++) { result->append (new String (words.get (j))); } } // here it is unlock (); return result; } catch (...) { delete result; unlock (); throw; } } // ------------------------------------------------------------------------- // - object section - // ------------------------------------------------------------------------- // the quark zone static const long QUARK_ZONE_LENGTH = 6; static QuarkZone zone (QUARK_ZONE_LENGTH); // the object supported quarks static const long QUARK_LENGTH = zone.intern ("length"); static const long QUARK_GETNODE = zone.intern ("get-node"); static const long QUARK_SETNAME = zone.intern ("set-name"); static const long QUARK_GETINFO = zone.intern ("get-info"); static const long QUARK_GETIVEC = zone.intern ("get-info-vector"); static const long QUARK_GETWORDS = zone.intern ("get-words"); // create a new object in a generic way Object* XsmDocument::mknew (Vector* argv) { long argc = (argv == nilp) ? 0 : argv->length (); // create a default document object if (argc == 0) return new XsmDocument; // check for 1 argument if (argc == 1) { String name = argv->getstring (0); return new XsmDocument (name); } // check for 2 arguments if (argc == 2) { // get the document name String name = argv->getstring (0); // get the object and check Object* obj = argv->get (1); // check for an input stream Input* is = dynamic_cast (obj); if (is != nilp) return new XsmDocument (name, is); throw Exception ("type-error", "invalid object with xsm document constructor", Object::repr (obj)); } throw Exception ("argument-error", "too many argument with xsm document constructor"); } // return true if the given quark is defined bool XsmDocument::isquark (const long quark, const bool hflg) const { rdlock (); if (zone.exists (quark) == true) { unlock (); return true; } bool result = hflg ? Nameable::isquark (quark, hflg) : false; unlock (); return result; } // apply this object with a set of arguments and a quark Object* XsmDocument::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_LENGTH) return new Integer (length ()); if (quark == QUARK_GETWORDS) return getwords (); } // check for 1 argument if (argc == 1) { if (quark == QUARK_GETNODE) { long index = argv->getint (0); rdlock (); Object* result = getnode (index); robj->post (result); unlock (); return result; } if (quark == QUARK_SETNAME) { String name = argv->getstring (0); setname (name); return nilp; } if (quark == QUARK_GETINFO) { long index = argv->getint (0); return getinfo (index); } if (quark == QUARK_GETIVEC) { String name = argv->getstring (0); return getivec (name); } } // check for 2 arguments if (argc == 2) { if (quark == QUARK_GETINFO) { long index = argv->getint (0); bool lwcf = argv->getbool (1); return getinfo (index, lwcf); } if (quark == QUARK_GETIVEC) { String name = argv->getstring (0); bool lwcf = argv->getbool (1); return getivec (name, lwcf); } } // call the nameable method return Nameable::apply (robj, nset, quark, argv); } }