--- parser3/src/classes/xnode.C 2004/03/10 10:42:11 1.67 +++ parser3/src/classes/xnode.C 2007/02/03 18:08:38 1.76 @@ -1,13 +1,13 @@ /** @file Parser: @b dom parser class. - Copyright (c) 2001-2004 ArtLebedev Group (http://www.artlebedev.com) + Copyright (c) 2001-2005 ArtLebedev Group (http://www.artlebedev.com) Author: Alexandr Petrosian (http://paf.design.ru) */ #include "classes.h" #ifdef XML -static const char * const IDENT_XNODE_C="$Date: 2004/03/10 10:42:11 $"; +static const char * const IDENT_XNODE_C="$Date: 2007/02/03 18:08:38 $"; #include "pa_vmethod_frame.h" @@ -17,14 +17,9 @@ static const char * const IDENT_XNODE_C= #include "pa_vxdoc.h" #include "pa_vvoid.h" #include "pa_xml_exception.h" +#include "pa_vbool.h" #include "xnode.h" - -extern "C" { -#include "gdomecore/gdome-xml-node.h" -#include "gdomecore/gdome-xml-document.h" -}; -#include "gdome.h" #include "libxml/xpath.h" #include "libxml/xpathInternals.h" @@ -102,35 +97,150 @@ private: // helpers -GdomeNode* as_node(MethodParams& params, - int index, const char* msg) { - GdomeNode* result; +xmlNode& as_node(MethodParams& params, int index, const char* msg) { Value& value=params.as_no_junction(index, msg); if(Value* vxnode=value.as(VXNODE_TYPE, false)) - result=static_cast(vxnode)->get_node(); - else { + return static_cast(vxnode)->get_xmlnode(); + else throw Exception("parser.runtime", 0, msg); - } - - return result; } -// helpers +xmlChar* as_xmlchar(Request& r, MethodParams& params, int index, const char* msg) { + return r.transcode(params.as_string(index, msg)); +} -GdomeAttr* as_attr(MethodParams& params, - int index, const char* msg) { - GdomeNode* node=as_node(params, index, msg); - GdomeException exc; - if(gdome_n_nodeType(node, &exc)!=GDOME_ATTRIBUTE_NODE) +xmlAttr& as_attr(MethodParams& params, int index, const char* msg) { + xmlNode& xmlnode=as_node(params, index, msg); + if(xmlnode.type!=XML_ATTRIBUTE_NODE) throw Exception("parser.runtime", 0, msg); - return GDOME_A(node); + return *(xmlAttr*)&xmlnode; +} + +static void writeNode(Request& r, VXdoc& xdoc, xmlNode* node) { + if(!node|| xmlHaveGenericErrors()) + throw XmlException(0); // OOM, bad name, things like that + + // write out result + r.write_no_lang(xdoc.wrap(*node)); +} + +static xmlNode* pa_getAttributeNodeNS(xmlNode& selfNode, + const xmlChar* localName, + const xmlChar* namespaceURI) +{ + for(xmlNode* currentNode=(xmlNode*)selfNode.properties; + currentNode; + currentNode=currentNode->next) + { + if(!namespaceURI || currentNode->ns && xmlStrEqual(currentNode->ns->href, namespaceURI)) + if(!localName || xmlStrEqual(currentNode->name, localName)) + return currentNode; + } + return 0; +} + +xmlNs& pa_xmlMapNs(xmlDoc& doc, const xmlChar *href, const xmlChar *prefix) { + assert(href); + // prefix can be null + + xmlNs *cur=doc.oldNs; + while (cur != NULL && + ((cur->prefix == NULL && prefix != NULL) || + (cur->prefix != NULL && prefix == NULL) || + !xmlStrEqual (cur->prefix, prefix)) && + !xmlStrEqual (cur->href, href)) + cur = cur->next; + + if (cur == NULL) { + cur = xmlNewNs (NULL, href, prefix); + if(!cur || xmlHaveGenericErrors()) + throw XmlException(0); + cur->next = doc.oldNs; + doc.oldNs = cur; + } + + return *cur; +} + +/// todo: проверить, обновляется ли parent! +static void pa_addAttributeNode(xmlNode& selfNode, xmlAttr& attrNode) +{ + if(attrNode.type!=XML_ATTRIBUTE_NODE) + throw Exception("parser.runtime", + 0, + "must be ATTRIBUTE_NODE"); + + /* + * Add it at the end to preserve parsing order ... + */ + if (selfNode.properties == NULL) { + selfNode.properties = &attrNode; + } else { + xmlAttrPtr prev = selfNode.properties; + + while (prev->next != NULL) + prev = prev->next; + prev->next = &attrNode; + attrNode.prev = prev; + } + + if (xmlIsID(selfNode.doc, &selfNode, &attrNode) == 1) + xmlAddID(NULL, selfNode.doc, xmlNodeGetContent((xmlNode*)&attrNode), &attrNode); +} + +static const xmlChar * +pa_xmlGetNsURI(xmlNode *node) { + if (node == NULL || node->ns == NULL) + return NULL; + + return node->ns->href; +} + +#ifndef DOXYGEN +struct AccumulateFoundInfo +{ + HashStringValue* hash; + VXdoc* vdoc; + int index; +}; +#endif +static void AccumulateFound(xmlNode& node, AccumulateFoundInfo* info) +{ + info->hash->put( + String::Body::Format(info->index++), + &info->vdoc->wrap(node)); +} +template static void +pa_xmlNamedPreorderTraversal ( + xmlNode *root, + xmlChar *tagURI, + xmlChar *tagName, + void callback(xmlNode& node, I info), + I info) +{ + for(xmlNode *iter=root->children; iter; iter = iter->next) { + if(iter->type == XML_ELEMENT_NODE && + (xmlStrEqual(iter->name, tagName) || + xmlStrEqual(tagName, (const xmlChar*)"*"))) { + if(tagURI != NULL && + (xmlStrEqual(pa_xmlGetNsURI(iter), tagURI) || + xmlStrEqual(tagURI, (const xmlChar*)"*"))) + callback(*iter, info); + else if(tagURI == NULL) + callback(*iter, info); + } + pa_xmlNamedPreorderTraversal(iter, tagURI, tagName, callback, info); + } + + return; } + // methods // DOM1 node @@ -138,200 +248,227 @@ GdomeAttr* as_attr(MethodParams& params, // Node insertBefore(in Node newChild,in Node refChild) raises(DOMException); static void _insertBefore(Request& r, MethodParams& params) { VXnode& vnode=GET_SELF(r, VXnode); - GdomeNode* selfNode=vnode.get_node(); - GdomeNode* newChild=as_node(params, 0, "newChild must be node"); - GdomeNode* refChild=as_node(params, 1, "refChild must be node"); + VXdoc& vxdoc=vnode.get_vxdoc(); + + //xmlNode& selfNode=vnode.get_xmlnode(); + xmlNode& newChild=as_node(params, 0, "newChild must be node"); + xmlNode& refChild=as_node(params, 1, "refChild must be node"); - GdomeException exc; - if(GdomeNode* retNode=gdome_n_insertBefore(selfNode, newChild, refChild, &exc)) { - // write out result - r.write_no_lang(*new VXnode(&r.charsets, vnode.get_xdoc(), retNode)); - } else - throw XmlException(0, exc); + xmlNode* retNode=xmlAddPrevSibling(&refChild, &newChild); + // write out result + writeNode(r, vxdoc, retNode); } // Node replaceChild(in Node newChild,in Node oldChild) raises(DOMException); static void _replaceChild(Request& r, MethodParams& params) { VXnode& vnode=GET_SELF(r, VXnode); - GdomeNode* selfNode=vnode.get_node(); - GdomeNode* newChild=as_node(params, 0, "newChild must be node"); - GdomeNode* oldChild=as_node(params, 1, "oldChild must be node"); - - GdomeException exc; - if(GdomeNode* retNode=gdome_n_replaceChild(selfNode, newChild, oldChild, &exc)) { - // write out result - r.write_no_lang(*new VXnode(&r.charsets, vnode.get_xdoc(), retNode)); - } else - throw XmlException(0, exc); + VXdoc& vxdoc=vnode.get_vxdoc(); + xmlDoc& xmldoc=vxdoc.get_xmldoc(); + xmlNode& selfNode=vnode.get_xmlnode(); + xmlNode& newChild=as_node(params, 0, "newChild must be node"); + xmlNode& oldChild=as_node(params, 1, "oldChild must be node"); + + if(newChild.doc!=&xmldoc) + throw Exception("xml.dom", + 0, + "WRONG_DOCUMENT_ERR"); + if(oldChild.doc!=&xmldoc) + throw Exception("xml.dom", + 0, + "WRONG_DOCUMENT_ERR"); + + if(oldChild.parent!=&selfNode) + throw Exception("xml.dom", + 0, + "NOT_FOUND_ERR"); + + xmlNode* refChild=oldChild.next; + xmlUnlinkNode(&oldChild); + xmlNode* retNode; + if(refChild) + retNode=xmlAddPrevSibling(refChild, &newChild); + else + retNode=xmlAddChild(&selfNode, &newChild); + + // write out result + writeNode(r, vxdoc, retNode); } // Node removeChild(in Node oldChild) raises(DOMException); static void _removeChild(Request& r, MethodParams& params) { VXnode& vnode=GET_SELF(r, VXnode); - GdomeNode* selfNode=vnode.get_node(); - GdomeNode* oldChild=as_node(params, 0, "oldChild must be node"); + VXdoc& vxdoc=vnode.get_vxdoc(); + xmlDoc& xmldoc=vxdoc.get_xmldoc(); +// xmlNode& selfNode=vnode.get_xmlnode(); + xmlNode& oldChild=as_node(params, 0, "refChild must be node"); - GdomeException exc; - if(GdomeNode* retNode=gdome_n_removeChild(selfNode, oldChild, &exc)) { - // write out result - r.write_no_lang(*new VXnode(&r.charsets, vnode.get_xdoc(), retNode)); - } else - throw XmlException(0, exc); + if(oldChild.doc!=&xmldoc) + throw Exception("xml.dom", + 0, + "WRONG_DOCUMENT_ERR"); + + xmlUnlinkNode(&oldChild); + // write out result + writeNode(r, vxdoc, &oldChild); } // Node appendChild(in Node newChild) raises(DOMException); static void _appendChild(Request& r, MethodParams& params) { VXnode& vnode=GET_SELF(r, VXnode); - GdomeNode* selfNode=vnode.get_node(); - GdomeNode* newChild=as_node(params, 0, "newChild must be node"); + VXdoc& vxdoc=vnode.get_vxdoc(); + xmlNode& selfNode=vnode.get_xmlnode(); + xmlNode& newChild=as_node(params, 0, "newChild must be node"); - GdomeException exc; - if(GdomeNode* retNode=gdome_n_appendChild(selfNode, newChild, &exc)) { - // write out result - r.write_no_lang(*new VXnode(&r.charsets, vnode.get_xdoc(), retNode)); - } else - throw XmlException(0, exc); + xmlNode* retNode=xmlAddChild(&selfNode, &newChild); + // write out result + writeNode(r, vxdoc, retNode); } // boolean hasChildNodes(); static void _hasChildNodes(Request& r, MethodParams&) { VXnode& vnode=GET_SELF(r, VXnode); - GdomeNode* node=vnode.get_node(); + xmlNode& node=vnode.get_xmlnode(); - GdomeException exc; // write out result - bool result=gdome_n_hasChildNodes(node, &exc)!=0; + bool result=node.children!=0; r.write_no_lang(*new VBool(result)); } // Node cloneNode(in boolean deep); static void _cloneNode(Request& r, MethodParams& params) { VXnode& vnode=GET_SELF(r, VXnode); - GdomeNode* node=vnode.get_node(); + xmlNode& selfNode=vnode.get_xmlnode(); + VXdoc& vxdoc=vnode.get_vxdoc(); + xmlDoc& xmldoc=vxdoc.get_xmldoc(); bool deep=params.as_bool(0, "deep must be bool", r); - GdomeException exc; + xmlNode* retNode=xmlDocCopyNode(&selfNode, &xmldoc, deep?1: 0); // write out result - r.write_no_lang(*new VXnode(&r.charsets, vnode.get_xdoc(), gdome_n_cloneNode(node, deep, &exc))); + writeNode(r, vxdoc, retNode); } // DOM1 element -GdomeElement* get_self_element(VXnode& vnode) { - GdomeNode* node=vnode.get_node(); +xmlNode& get_self_element(VXnode& vnode) { + xmlNode& node=vnode.get_xmlnode(); - GdomeException exc; - if(gdome_n_nodeType(node, &exc)!=GDOME_ELEMENT_NODE) + if(node.type!=XML_ELEMENT_NODE) throw Exception("parser.runtime", 0, "method can only be called on nodes of ELEMENT type"); - return GDOME_EL(node); + return node; } - -/// @bug attribute_value must be freed! [// DOMString getAttribute(in DOMString name); static void _getAttribute(Request& r, MethodParams& params) { VXnode& vnode=GET_SELF(r, VXnode); - GdomeElement* element=get_self_element(vnode); - const String& name=params.as_string(0, "name must be string"); + xmlNode& element=get_self_element(vnode); + const xmlChar* name=as_xmlchar(r, params, 0, "name must be string"); - GdomeException exc; - GdomeDOMString *attribute_value= - gdome_el_getAttribute(element, r.transcode(name).use(), &exc); + // todo: when name="xmlns" + xmlChar* attribute_value=xmlGetProp(&element, name); // write out result - r.write_no_lang(r.transcode(attribute_value)); + r.write_pass_lang(r.transcode(attribute_value)); } // void setAttribute(in DOMString name, in DOMString value) raises(DOMException); static void _setAttribute(Request& r, MethodParams& params) { VXnode& vnode=GET_SELF(r, VXnode); - GdomeElement* element=get_self_element(vnode); - const String& name=params.as_string(0, "name must be string"); - const String& attribute_value=params.as_string(1, "value must be string"); - - GdomeException exc; - gdome_el_setAttribute(element, - r.transcode(name).use(), - r.transcode(attribute_value).use(), - &exc); - if(exc) - throw XmlException(0, exc); + xmlNode& element=get_self_element(vnode); + const xmlChar* name=as_xmlchar(r, params, 0, "name must be string"); + const xmlChar* attribute_value=as_xmlchar(r, params, 1, "value must be string"); + + // todo: when name="xmlns" + if(!xmlSetProp(&element, name, attribute_value)) + throw XmlException(0); } // void removeAttribute(in DOMString name) raises(DOMException); static void _removeAttribute(Request& r, MethodParams& params) { VXnode& vnode=GET_SELF(r, VXnode); - GdomeElement* element=get_self_element(vnode); - const String& name=params.as_string(0, "name must be string"); + xmlNode& element=get_self_element(vnode); + const xmlChar* name=as_xmlchar(r, params, 0, "name must be string"); - GdomeException exc; - gdome_el_removeAttribute(element, r.transcode(name).use(), &exc); - if(exc) - throw XmlException(0, exc); + // todo: when name="xmlns" + xmlUnsetProp(&element, name); } // Attr getAttributeNode(in DOMString name); static void _getAttributeNode(Request& r, MethodParams& params) { VXnode& vnode=GET_SELF(r, VXnode); - GdomeElement* element=get_self_element(vnode); - const String& name=params.as_string(0, "name must be string"); + VXdoc& vxdoc=vnode.get_vxdoc(); + xmlNode& element=get_self_element(vnode); + const xmlChar* localName=as_xmlchar(r, params, 0, "name must be string"); - GdomeException exc; - if(GdomeAttr *attr=gdome_el_getAttributeNode(element, - r.transcode(name).use(), &exc)) { + if(xmlNode* retNode=pa_getAttributeNodeNS(element, localName, 0)){ // write out result - r.write_no_lang(*new VXnode(&r.charsets, vnode.get_xdoc(), (GdomeNode* )attr)); - } else if(exc) - throw XmlException(0, exc); + writeNode(r, vxdoc, retNode); + } } // Attr setAttributeNode(in Attr newAttr) raises(DOMException); +// Attr setAttributeNodeNS(in Attr newAttr) raises(DOMException); static void _setAttributeNode(Request& r, MethodParams& params) { VXnode& vnode=GET_SELF(r, VXnode); - GdomeElement* element=get_self_element(vnode); - GdomeAttr * newAttr=as_attr(params, 0, "newAttr must be ATTRIBUTE node"); + VXdoc& vxdoc=vnode.get_vxdoc(); + xmlNode& element=get_self_element(vnode); + xmlDoc& xmldoc=vxdoc.get_xmldoc(); + xmlAttr& newAttr=as_attr(params, 0, "newAttr must be ATTRIBUTE node"); + + if(newAttr.doc!=&xmldoc) + throw Exception("xml.dom", + 0, + "WRONG_DOCUMENT_ERR"); - GdomeException exc; - if(GdomeAttr *returnAttr=gdome_el_setAttributeNode(element, newAttr, &exc)) { + if(newAttr.parent) + throw Exception("xml.dom", + 0, + "INUSE_ATTRIBUTE_ERR"); + + if(xmlNode* retNode=pa_getAttributeNodeNS(element, newAttr.name, pa_xmlGetNsURI((xmlNode*)&newAttr))) { // write out result - r.write_no_lang(*new VXnode(&r.charsets, vnode.get_xdoc(), (GdomeNode* )returnAttr)); - } else - throw XmlException(0, exc); + writeNode(r, vxdoc, retNode); + xmlUnlinkNode(retNode); + } + + pa_addAttributeNode(element, newAttr); } // Attr removeAttributeNode(in Attr oldAttr) raises(DOMException); static void _removeAttributeNode(Request& r, MethodParams& params) { VXnode& vnode=GET_SELF(r, VXnode); - GdomeElement* element=get_self_element(vnode); - GdomeAttr * oldAttr=as_attr(params, 0, "oldAttr must be ATTRIBUTE node"); + VXdoc& vxdoc=vnode.get_vxdoc(); + xmlNode& element=get_self_element(vnode); + xmlAttr& oldAttr=as_attr(params, 0, "oldAttr must be ATTRIBUTE node"); - GdomeException exc; - gdome_el_removeAttributeNode(element, oldAttr, &exc); - if(exc) - throw XmlException(0, exc); + if(oldAttr.parent!=&element) + throw Exception("xml.dom", + 0, + "NOT_FOUND_ERR"); + + xmlUnlinkNode((xmlNode*)&oldAttr); + + // write out result + writeNode(r, vxdoc, (xmlNode*)&oldAttr); } // NodeList getElementsByTagName(in DOMString name); static void _getElementsByTagName(Request& r, MethodParams& params) { VXnode& vnode=GET_SELF(r, VXnode); - GdomeElement* element=get_self_element(vnode); + VXdoc& vxdoc=vnode.get_vxdoc(); + xmlNode& xmlnode=vnode.get_xmlnode(); - const String& name=params.as_string(0, "name must be string"); + xmlChar* tagName=as_xmlchar(r, params, 0, "name must be string"); VHash& result=*new VHash; - GdomeException exc; - if(GdomeNodeList *nodes= - gdome_el_getElementsByTagName(element, r.transcode(name).use(), &exc)) { - gulong length=gdome_nl_length(nodes, &exc); - for(gulong i=0; iget_string()) xmlXPathRegisterNs(info->ctxt, - BAD_CAST info->r->transcode(key)->str, - BAD_CAST info->r->transcode(*svalue)->str); + info->r->transcode(key), + info->r->transcode(*svalue)); else throw Exception("parser.runtime", new String(key, String::L_TAINTED), @@ -514,40 +634,36 @@ static void _selectX(Request& r, MethodP const String& expression, xmlXPathObject_auto_ptr res, VXdoc& xdoc, - Value*& result)) { + Value*& result)) +{ VXnode& vnode=GET_SELF(r, VXnode); + xmlNode& xmlnode=vnode.get_xmlnode(); + VXdoc& vdoc=vnode.get_vxdoc(); + xmlDoc& xmldoc=vdoc.get_xmldoc(); // expression const String& expression=params.as_string(0, "expression must be string"); - GdomeException exc; - GdomeNode* dome_node=vnode.get_node(); - GdomeDocument *dome_document=gdome_n_ownerDocument(dome_node, &exc); - if(!dome_document) // document does not own itself, so ownerDocument = 0 - dome_document=GDOME_DOC(dome_node); // and we need downcast - xmlDoc *xml_document=gdome_xml_doc_get_xmlDoc(dome_document); - xmlXPathContext_auto_ptr ctxt(xmlXPathNewContext(xml_document)); + xmlXPathContext_auto_ptr ctxt(xmlXPathNewContext(&xmldoc)); { Register_one_ns_info info={&r, ctxt.get()}; - vnode.get_xdoc().search_namespaces.hash().for_each(register_one_ns, &info); + vdoc.search_namespaces.hash().for_each(register_one_ns, &info); } - ctxt->node=gdome_xml_n_get_xmlNode(dome_node); + ctxt->node=&xmlnode; /*error to stderr for now*/ xmlXPathObject_auto_ptr res( - xmlXPathEvalExpression(BAD_CAST r.transcode(expression)->str, ctxt.get())); + xmlXPathEvalExpression(r.transcode(expression), ctxt.get())); - if(xmlHaveGenericErrors()) { - GdomeException exc=0; - throw XmlException(&expression, exc); - } + if(xmlHaveGenericErrors()) + throw XmlException(0); Value* result=0; if(res.get()) - handler(r, expression, res, vnode.get_xdoc(), result); + handler(r, expression, res, vdoc, result); if(result) r.write_no_lang(*result); } -static void selectNodesHandler(Request& r, +static void selectNodesHandler(Request&, const String& expression, xmlXPathObject_auto_ptr res, VXdoc& xdoc, @@ -563,14 +679,11 @@ static void selectNodesHandler(Request& for(int i=0; inodesetval->nodeTab[i]))); + &xdoc.wrap(*res->nodesetval->nodeTab[i])); } break; default: - throw Exception(0, + throw Exception("parser.runtime", &expression, "wrong xmlXPathEvalExpression result type (%d)", res->type); break; // never @@ -592,10 +705,7 @@ static void selectNodeHandler(Request& r &expression, "resulted not in a single node (%d)", res->nodesetval->nodeNr); - result=new VXnode( - &r.charsets, - xdoc, - gdome_xml_n_mkref(res->nodesetval->nodeTab[0])); + result=&xdoc.wrap(*res->nodesetval->nodeTab[0]); } break; case XPATH_BOOLEAN: @@ -611,8 +721,6 @@ static void selectNodeHandler(Request& r throw Exception("parser.runtime", &expression, "wrong xmlXPathEvalExpression result type (%d)", res->type); - // result=0; - break; // never } } @@ -759,7 +867,7 @@ MXnode::MXnode(const char* aname, VState // Attr getAttributeNodeNS(in DOMString namespaceURI, in DOMString localName); add_native_method("getAttributeNodeNS", Method::CT_DYNAMIC, _getAttributeNodeNS, 2, 2); // Attr setAttributeNodeNS(in Attr newAttr) raises(DOMException); - add_native_method("setAttributeNodeNS", Method::CT_DYNAMIC, _setAttributeNodeNS, 1, 1); + add_native_method("setAttributeNodeNS", Method::CT_DYNAMIC, _setAttributeNode, 1, 1); // boolean hasAttribute(in DOMString name) raises(DOMException); add_native_method("hasAttribute", Method::CT_DYNAMIC, _hasAttribute, 1, 1); // boolean hasAttributeNS(in DOMString namespaceURI, in DOMString localName) raises(DOMException); @@ -781,19 +889,21 @@ MXnode::MXnode(const char* aname, VState // consts #define CONST(name) \ - consts.put(String::Body(#name), new VInt(GDOME_##name)) + consts.put(String::Body(#name), new VInt(XML_##name)) +#define CONST2(name, value) \ + consts.put(String::Body(#name), new VInt(value)) CONST(ELEMENT_NODE); CONST(ATTRIBUTE_NODE); CONST(TEXT_NODE); CONST(CDATA_SECTION_NODE); - CONST(ENTITY_REFERENCE_NODE); + CONST2(ENTITY_REFERENCE_NODE, XML_ENTITY_REF_NODE); CONST(ENTITY_NODE); - CONST(PROCESSING_INSTRUCTION_NODE); + CONST2(PROCESSING_INSTRUCTION_NODE, XML_PI_NODE); CONST(COMMENT_NODE); CONST(DOCUMENT_NODE); CONST(DOCUMENT_TYPE_NODE); - CONST(DOCUMENT_FRAGMENT_NODE); + CONST2(DOCUMENT_FRAGMENT_NODE, XML_DOCUMENT_FRAG_NODE); CONST(NOTATION_NODE); }