// --------------------------------------------------------------------------- // - XmlInclude.cpp - // - afnix:xpe module - xml include feature 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 "Uri.hpp" #include "XmlTag.hpp" #include "Boolean.hpp" #include "Runnable.hpp" #include "QuarkZone.hpp" #include "Exception.hpp" #include "XmlInclude.hpp" namespace afnix { // ------------------------------------------------------------------------- // - private section - // ------------------------------------------------------------------------- // the xml include name and info static const String XML_FTRS_NAME = "xi"; static const String XML_FTRS_INFO = "xml include 1.0"; // the xml include processor level static const long XML_FTRS_PLVL = 10; // the xi:include tag to process static const String XML_XTAG_NAME = "xi:include"; // the xi:include attributes static const String XML_HREF_ATTR = "href"; static const String XML_TYPE_ATTR = "parse"; static const String XML_XPTR_ATTR = "xpointer"; static const String XML_EMOD_ATTR = "encoding"; static const String XML_ACCP_ATTR = "accept"; static const String XML_ACCL_ATTR = "accept-language"; // the valid parse attribute value static const String XML_TYPE_XVAL = "xml"; static const String XML_TYPE_TVAL = "text"; // forward content processing declaration static XmlContent* process_xcnt (const XmlContent* xcnt); // the xml include structure struct s_xi { // the include reference String d_href; // the parse type String d_type; // the xpointer reference String d_xptr; // the encoding mode String d_emod; // the hhtp accept String d_accp; // the http accept language String d_accl; // the reference node XmlTag* p_tag; // create a xi structure by node s_xi (XmlTag* tag) { // initialize content d_href = ""; d_type = XML_TYPE_XVAL; d_xptr = ""; d_emod = ""; d_emod = ""; d_accp = ""; d_accl = ""; p_tag = nilp; // set the node settag (tag); } // destroy this structure ~s_xi (void) { Object::dref (p_tag); } // set the xi tag void settag (XmlTag* tag) { // check for nil type if (tag == nilp) return; if (tag->isname (XML_XTAG_NAME) == false) { throw Exception ("internal-error", "invalid tag for include", tag->getname ()); } // save the node Object::iref (tag); Object::dref (p_tag); p_tag = tag; // update from attributes if (tag->isattr (XML_HREF_ATTR) == true) { d_href = tag->getpval (XML_HREF_ATTR); } if (tag->isattr (XML_TYPE_ATTR) == true) { d_type = tag->getpval (XML_TYPE_ATTR); if ((d_type != XML_TYPE_XVAL) && (d_type != XML_TYPE_TVAL)) { throw Exception ("xi-error", "invalid parse attribute value", d_type); } } if (tag->isattr (XML_XPTR_ATTR) == true) { d_xptr = tag->getpval (XML_XPTR_ATTR); if ((d_type != XML_TYPE_XVAL) && (d_xptr.isnil () == false)) { throw Exception ("xi-error", "invlid xpointer attribute with non xml type", d_type); } } if (tag->isattr (XML_EMOD_ATTR) == true) { d_emod = tag->getpval (XML_EMOD_ATTR); } if (tag->isattr (XML_ACCP_ATTR) == true) { d_accp = tag->getpval (XML_ACCP_ATTR); } if (tag->isattr (XML_ACCL_ATTR) == true) { d_accl = tag->getpval (XML_ACCL_ATTR); } } // process the tag with a root node and document name void process (const String& docn) { // check first for circular reference if ((d_href.isnil () == true) && (d_type == XML_TYPE_XVAL) && (d_xptr.isnil () == true)) { throw Exception ("xi-error", "self xml document inclusion", docn); } // create a new uri from the reference Uri ruri = docn; Uri huri = ruri.gethref (d_href); // initialize the content to load XmlContent* bcnt = nilp; // check for xml type if (d_type == XML_TYPE_XVAL) { bcnt = new XmlContent (d_href, huri.getname ()); } // check for text type if (d_type == XML_TYPE_TVAL) { bcnt = new XmlContent (d_href, huri.getname (), d_emod); } // process this content XmlContent* xcnt = process_xcnt (bcnt); delete bcnt; if (xcnt == nilp) return; // get the root node and clean the declaration XmlRoot* root = xcnt->getroot (); if (root != nilp) root->deldecl (); // get the parent node find the node index and merge XmlNode* pnod = p_tag->getparent (); if (pnod != nilp) { long nidx = pnod->getnidx (p_tag); pnod->merge (root, nidx); } } }; // process a node with a document name static void process_node (XmlNode* node, const String docn) { // check for nil node if (node == nilp) return; // process the include node long clen = node->lenchild (); for (long i = 0; i < clen; i++) { XmlNode* cnod = node->getchild (i); if (cnod == nilp) continue; if (cnod->isname (XML_XTAG_NAME) == true) { // get the tag to process XmlTag* tag = dynamic_cast (cnod); if (tag == nilp) continue; // create a xi structure s_xi xi (tag); // process the node by name xi.process (docn); // restart the count clen = node->lenchild (); i = 0; // done with this node continue; } } // process children if any clen = node->lenchild (); for (long i = 0; i < clen; i++) { XmlNode* cnod = node->getchild (i); if (cnod == nilp) continue; if (cnod->nilchild () == false) process_node (cnod, docn); } } // process the xi:include node in a tree static XmlContent* process_xcnt (const XmlContent* xcnt) { // check for nil if (xcnt == nilp) return nilp; // create a working content XmlContent* result = new XmlContent (*xcnt); // get the root node XmlRoot* root= result->getroot (); // do nothing if nil if (root == nilp) return result; // get the document name String docn = result->getdocn (); // process the node process_node (root, docn); // here is the result return result; } // ------------------------------------------------------------------------- // - class section - // ------------------------------------------------------------------------- // create a default xml include feature XmlInclude::XmlInclude (void) : XmlFeature (XML_FTRS_NAME, XML_FTRS_INFO) { d_plvl = XML_FTRS_PLVL; } // create a xml include feature with a processing level XmlInclude::XmlInclude (const long plvl) : XmlFeature (XML_FTRS_NAME, XML_FTRS_INFO) { d_plvl = plvl; } // return the class name String XmlInclude::repr (void) const { return "XmlInclude"; } // process a xml content and generate a new content XmlContent* XmlInclude::process (const XmlContent* xcnt) { try { // get a copy of the tree XmlContent* result = process_xcnt (xcnt); // here it is unlock (); return result; } catch (...) { unlock (); throw; } } // ------------------------------------------------------------------------- // - object section - // ------------------------------------------------------------------------- // create a new object in a generic way Object* XmlInclude::mknew (Vector* argv) { long argc = (argv == nilp) ? 0 : argv->length (); // check for 0 argument if (argc == 0) return new XmlInclude; // check for 1 argument if (argc == 1) { long plvl = argv->getint (0); return new XmlInclude (plvl); } // wrong arguments throw Exception ("argument-error", "too many arguments with xml include constructor"); } }