Annotation of parser3/src/classes/xnode.C, revision 1.72

1.1       parser      1: /** @file
                      2:        Parser: @b dom parser class.
                      3: 
1.71      paf         4:        Copyright (c) 2001-2005 ArtLebedev Group (http://www.artlebedev.com)
1.38      paf         5:        Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru)
1.1       parser      6: */
                      7: #include "classes.h"
                      8: #ifdef XML
1.46      paf         9: 
1.72    ! paf        10: static const char * const IDENT_XNODE_C="$Date: 2005/08/09 08:14:48 $";
1.56      paf        11: 
                     12: #include "pa_vmethod_frame.h"
1.1       parser     13: 
1.21      paf        14: #include "pa_charset.h"
1.1       parser     15: #include "pa_request.h"
                     16: #include "pa_vxnode.h"
1.34      paf        17: #include "pa_vxdoc.h"
1.23      paf        18: #include "pa_vvoid.h"
1.60      paf        19: #include "pa_xml_exception.h"
1.1       parser     20: 
                     21: #include "xnode.h"
1.21      paf        22: #include "libxml/xpath.h"
1.66      paf        23: #include "libxml/xpathInternals.h"
1.21      paf        24: 
1.56      paf        25: // global variable
                     26: 
                     27: DECLARE_CLASS_VAR(xnode, new MXnode, 0);
                     28: 
1.21      paf        29: // classes
                     30: 
                     31: class xmlXPathObject_auto_ptr {
                     32: public:
1.29      paf        33:        explicit xmlXPathObject_auto_ptr(xmlXPathObject *_APtr = 0) 
                     34:                : _Owns(_APtr != 0), _Ptr(_APtr) {}
1.21      paf        35:        xmlXPathObject_auto_ptr(const xmlXPathObject_auto_ptr& _Y) 
                     36:                : _Owns(_Y._Owns), _Ptr(_Y.release()) {}
                     37:        xmlXPathObject_auto_ptr& operator=(const xmlXPathObject_auto_ptr& _Y) 
                     38:                {if (this != &_Y)
                     39:                        {if (_Ptr != _Y.get())
                     40:                                {if (_Owns && _Ptr)
                     41:                                        xmlXPathFreeObject(_Ptr);
                     42:                                _Owns = _Y._Owns; }
                     43:                        else if (_Y._Owns)
                     44:                                _Owns = true;
                     45:                        _Ptr = _Y.release(); }
                     46:                return (*this); }
                     47:        ~xmlXPathObject_auto_ptr()
                     48:                {if (_Owns && _Ptr)
                     49:                        xmlXPathFreeObject(_Ptr); }
                     50:        xmlXPathObject& operator*() const 
                     51:                {return (*get()); }
                     52:        xmlXPathObject *operator->() const 
                     53:                {return (get()); }
                     54:        xmlXPathObject *get() const 
                     55:                {return (_Ptr); }
                     56:        xmlXPathObject *release() const 
                     57:                {((xmlXPathObject_auto_ptr *)this)->_Owns = false;
                     58:                return (_Ptr); }
                     59: private:
                     60:        bool _Owns;
                     61:        xmlXPathObject *_Ptr;
                     62: };
                     63: 
                     64: class xmlXPathContext_auto_ptr {
                     65: public:
1.29      paf        66:        explicit xmlXPathContext_auto_ptr(xmlXPathContext *_APtr = 0) 
                     67:                : _Owns(_APtr != 0), _Ptr(_APtr) {}
1.21      paf        68:        xmlXPathContext_auto_ptr(const xmlXPathContext_auto_ptr& _Y) 
                     69:                : _Owns(_Y._Owns), _Ptr(_Y.release()) {}
                     70:        xmlXPathContext_auto_ptr& operator=(const xmlXPathContext_auto_ptr& _Y) 
                     71:                {if (this != &_Y)
                     72:                        {if (_Ptr != _Y.get())
                     73:                                {if (_Owns && _Ptr)
                     74:                                        xmlXPathFreeContext(_Ptr);
                     75:                                _Owns = _Y._Owns; }
                     76:                        else if (_Y._Owns)
                     77:                                _Owns = true;
                     78:                        _Ptr = _Y.release(); }
                     79:                return (*this); }
                     80:        ~xmlXPathContext_auto_ptr()
                     81:                {if (_Owns && _Ptr)
                     82:                        xmlXPathFreeContext(_Ptr); }
                     83:        xmlXPathContext& operator*() const 
                     84:                {return (*get()); }
                     85:        xmlXPathContext *operator->() const 
                     86:                {return (get()); }
                     87:        xmlXPathContext *get() const 
                     88:                {return (_Ptr); }
                     89:        xmlXPathContext *release() const 
                     90:                {((xmlXPathContext_auto_ptr *)this)->_Owns = false;
                     91:                return (_Ptr); }
                     92: private:
                     93:        bool _Owns;
                     94:        xmlXPathContext *_Ptr;
                     95: };
1.1       parser     96: 
1.6       parser     97: // helpers
                     98: 
1.72    ! paf        99: xmlNode& as_node(MethodParams& params, int index, const char* msg) {
1.56      paf       100:        Value& value=params.as_no_junction(index, msg);
                    101:        if(Value* vxnode=value.as(VXNODE_TYPE, false))
1.72    ! paf       102:                return static_cast<VXnode*>(vxnode)->get_xmlnode();
        !           103:        else
1.41      paf       104:                throw Exception("parser.runtime",
1.56      paf       105:                        0,
1.6       parser    106:                        msg);
1.72    ! paf       107: }
1.56      paf       108: 
1.72    ! paf       109: xmlChar* as_xmlchar(Request& r, MethodParams& params, int index, const char* msg) {
        !           110:        return r.transcode(params.as_string(index, msg));
1.6       parser    111: }
                    112: 
1.72    ! paf       113: xmlAttr& as_attr(MethodParams& params, int index, const char* msg) {
        !           114:        xmlNode& xmlnode=as_node(params, index, msg);
        !           115:        if(xmlnode.type!=XML_ATTRIBUTE_NODE)
1.41      paf       116:                throw Exception("parser.runtime",
1.56      paf       117:                        0,
1.6       parser    118:                        msg);
                    119: 
1.72    ! paf       120:        return *(xmlAttr*)&xmlnode;
        !           121: }
        !           122: 
        !           123: static void writeNode(Request& r, VXdoc& xdoc, xmlNode* node) {
        !           124:        if(!node)
        !           125:                throw XmlException(0); // OOM, bad name, things like that
        !           126: 
        !           127:        // write out result
        !           128:        r.write_no_lang(xdoc.wrap(*node));
        !           129: }
        !           130: 
        !           131: static xmlNode* pa_getAttributeNodeNS(xmlNode& selfNode, 
        !           132:                                                                          const xmlChar* localName,
        !           133:                                                                          const xmlChar* namespaceURI) 
        !           134: {
        !           135:        for(xmlNode* currentNode=(xmlNode*)selfNode.properties;
        !           136:                currentNode;
        !           137:                currentNode=currentNode->next)
        !           138:        {
        !           139:                if(!namespaceURI || currentNode->ns && xmlStrEqual(currentNode->ns->href, namespaceURI))
        !           140:                        if(!localName || xmlStrEqual(currentNode->name, localName))
        !           141:                                return currentNode;
        !           142:        }
        !           143:        return 0;
        !           144: }
        !           145: 
        !           146: xmlNs& pa_xmlMapNs(xmlDoc& doc, const xmlChar *href, const xmlChar *prefix) {
        !           147:        assert(href);
        !           148:        // prefix can be null
        !           149: 
        !           150:        xmlNs *cur=doc.oldNs;
        !           151:        while (cur != NULL &&
        !           152:                                 ((cur->prefix == NULL && prefix != NULL) ||
        !           153:                                        (cur->prefix != NULL && prefix == NULL) ||
        !           154:                                        !xmlStrEqual (cur->prefix, prefix)) &&
        !           155:                                 !xmlStrEqual (cur->href, href))
        !           156:                cur = cur->next;
        !           157: 
        !           158:        if (cur == NULL) {
        !           159:                cur = xmlNewNs (NULL, href, prefix);
        !           160:                if(!cur || xmlHaveGenericErrors())
        !           161:                        throw XmlException(0);
        !           162:                cur->next = doc.oldNs;
        !           163:                doc.oldNs = cur;
        !           164:        }
        !           165: 
        !           166:        return *cur;
        !           167: }
        !           168: 
        !           169: /// todo: проверить, обновляется ли parent!
        !           170: static void pa_addAttributeNode(xmlNode& selfNode, xmlAttr& attrNode) 
        !           171: {
        !           172:        if(attrNode.type!=XML_ATTRIBUTE_NODE)
        !           173:                throw Exception("xml",
        !           174:                        0,
        !           175:                        "must be ATTRIBUTE_NODE");
        !           176: 
        !           177:     /*
        !           178:      * Add it at the end to preserve parsing order ...
        !           179:      */
        !           180:     if (selfNode.properties == NULL) {
        !           181:         selfNode.properties = &attrNode;
        !           182:     } else {
        !           183:         xmlAttrPtr prev = selfNode.properties;
        !           184: 
        !           185:         while (prev->next != NULL)
        !           186:             prev = prev->next;
        !           187:         prev->next = &attrNode;
        !           188:         attrNode.prev = prev;
        !           189:     }
        !           190: 
        !           191:     if (xmlIsID(selfNode.doc, &selfNode, &attrNode) == 1)
        !           192:         xmlAddID(NULL, selfNode.doc, xmlNodeGetContent((xmlNode*)&attrNode), &attrNode);
        !           193: }
        !           194: 
        !           195: static const xmlChar *
        !           196: pa_xmlGetNsURI(xmlNode *node) {
        !           197:        if (node == NULL || node->ns == NULL)
        !           198:                return NULL;
        !           199: 
        !           200:        return node->ns->href;
        !           201: }
        !           202: 
        !           203: #ifndef DOXYGEN
        !           204: struct AccumulateFoundInfo
        !           205: {
        !           206:        HashStringValue* hash;
        !           207:        VXdoc* vdoc;
        !           208:        int index;
        !           209: };
        !           210: #endif
        !           211: static void AccumulateFound(xmlNode& node, AccumulateFoundInfo* info)
        !           212: {
        !           213:        info->hash->put(
        !           214:                String::Body::Format(info->index++), 
        !           215:                &info->vdoc->wrap(node));
        !           216: }
        !           217: template<typename I> static void
        !           218: pa_xmlNamedPreorderTraversal (
        !           219:                                                          xmlNode *root, 
        !           220:                                                          xmlChar *tagURI, 
        !           221:                                                          xmlChar *tagName, 
        !           222:                                                          void callback(xmlNode& node, I info),
        !           223:                                                          I info) 
        !           224: {
        !           225:        for(xmlNode *iter=root->children; iter; iter = iter->next) {
        !           226:                if(iter->type == XML_ELEMENT_NODE &&
        !           227:                        (xmlStrEqual(iter->name, tagName) ||
        !           228:                        xmlStrEqual(tagName, (const xmlChar*)"*"))) {
        !           229:                                if(tagURI != NULL &&
        !           230:                                        (xmlStrEqual(pa_xmlGetNsURI(iter), tagURI) ||
        !           231:                                        xmlStrEqual(tagURI, (const xmlChar*)"*")))
        !           232:                                        callback(*iter, info);
        !           233:                                else if(tagURI == NULL)
        !           234:                                        callback(*iter, info);
        !           235:                        }
        !           236:                        pa_xmlNamedPreorderTraversal(iter, tagURI, tagName, callback, info);
        !           237:        }
        !           238: 
        !           239:        return;
1.6       parser    240: }
1.1       parser    241: 
1.72    ! paf       242: 
1.1       parser    243: // methods
                    244: 
1.6       parser    245: // DOM1 node
                    246: 
                    247: // Node insertBefore(in Node newChild,in Node refChild) raises(DOMException);
1.56      paf       248: static void _insertBefore(Request& r, MethodParams& params) {
                    249:        VXnode& vnode=GET_SELF(r, VXnode);
1.72    ! paf       250:        VXdoc& vxdoc=vnode.get_vxdoc();
        !           251: 
        !           252:        //xmlNode& selfNode=vnode.get_xmlnode();
        !           253:        xmlNode& newChild=as_node(params, 0, "newChild must be node");
        !           254:        xmlNode& refChild=as_node(params, 1, "refChild must be node");
1.6       parser    255:        
1.72    ! paf       256:        xmlNode* retNode=xmlAddPrevSibling(&refChild, &newChild);
        !           257:        // write out result
        !           258:        writeNode(r, vxdoc, retNode);
1.6       parser    259: }
                    260: 
                    261: // Node replaceChild(in Node newChild,in Node oldChild) raises(DOMException);
1.56      paf       262: static void _replaceChild(Request& r, MethodParams& params) {
                    263:        VXnode& vnode=GET_SELF(r, VXnode);
1.72    ! paf       264:        VXdoc& vxdoc=vnode.get_vxdoc();
        !           265:        xmlDoc& xmldoc=vxdoc.get_xmldoc();
        !           266:        xmlNode& selfNode=vnode.get_xmlnode();
        !           267:        xmlNode& newChild=as_node(params, 0, "newChild must be node");
        !           268:        xmlNode& oldChild=as_node(params, 1, "refChild must be node");
        !           269: 
        !           270:        if(newChild.doc!=&xmldoc)
        !           271:                throw Exception("xml",
        !           272:                        0,
        !           273:                        "WRONG_DOCUMENT_ERR");
        !           274:        if(oldChild.doc!=&xmldoc)
        !           275:                throw Exception("xml",
        !           276:                        0,
        !           277:                        "WRONG_DOCUMENT_ERR");
        !           278: 
        !           279:        xmlNode* refChild=oldChild.next;
        !           280:        xmlUnlinkNode(&oldChild);
        !           281:        xmlNode* retNode;
        !           282:        if(refChild)
        !           283:                retNode=xmlAddPrevSibling(refChild, &newChild);
        !           284:        else
        !           285:                retNode=xmlAddChild(&selfNode, &newChild);
        !           286: 
        !           287:        // write out result
        !           288:        writeNode(r, vxdoc, retNode);
1.6       parser    289: }
                    290: 
                    291: // Node removeChild(in Node oldChild) raises(DOMException);
1.56      paf       292: static void _removeChild(Request& r, MethodParams& params) {
                    293:        VXnode& vnode=GET_SELF(r, VXnode);
1.72    ! paf       294:        VXdoc& vxdoc=vnode.get_vxdoc();
        !           295:        xmlDoc& xmldoc=vxdoc.get_xmldoc();
        !           296: //     xmlNode& selfNode=vnode.get_xmlnode();
        !           297:        xmlNode& oldChild=as_node(params, 0, "refChild must be node");
1.6       parser    298:        
1.72    ! paf       299:        if(oldChild.doc!=&xmldoc)
        !           300:                throw Exception("xml",
        !           301:                        0,
        !           302:                        "WRONG_DOCUMENT_ERR");
        !           303: 
        !           304:        xmlUnlinkNode(&oldChild);
        !           305:        // write out result
        !           306:        writeNode(r, vxdoc, &oldChild);
1.6       parser    307: }
                    308: 
                    309: // Node appendChild(in Node newChild) raises(DOMException);
1.56      paf       310: static void _appendChild(Request& r, MethodParams& params) {
                    311:        VXnode& vnode=GET_SELF(r, VXnode);
1.72    ! paf       312:        VXdoc& vxdoc=vnode.get_vxdoc();
        !           313:        xmlNode& selfNode=vnode.get_xmlnode();
        !           314:        xmlNode& newChild=as_node(params, 0, "newChild must be node");
1.6       parser    315:        
1.72    ! paf       316:        xmlNode* retNode=xmlAddChild(&selfNode, &newChild);
        !           317:        // write out result
        !           318:        writeNode(r, vxdoc, retNode);
1.6       parser    319: }
                    320: 
                    321: // boolean hasChildNodes();
1.56      paf       322: static void _hasChildNodes(Request& r, MethodParams&) {
                    323:        VXnode& vnode=GET_SELF(r, VXnode);
1.72    ! paf       324:        xmlNode& node=vnode.get_xmlnode();
1.6       parser    325: 
                    326:        // write out result
1.72    ! paf       327:        bool result=node.children!=0;
1.56      paf       328:        r.write_no_lang(*new VBool(result));
1.6       parser    329: }
                    330: 
                    331: // Node cloneNode(in boolean deep);
1.56      paf       332: static void _cloneNode(Request& r, MethodParams& params) {
                    333:        VXnode& vnode=GET_SELF(r, VXnode);
1.72    ! paf       334:        xmlNode& selfNode=vnode.get_xmlnode();
        !           335:        VXdoc& vxdoc=vnode.get_vxdoc();
        !           336:        xmlDoc& xmldoc=vxdoc.get_xmldoc();
1.6       parser    337: 
1.56      paf       338:        bool deep=params.as_bool(0, "deep must be bool", r);
1.6       parser    339: 
1.72    ! paf       340:        xmlNode* retNode=xmlDocCopyNode(&selfNode, &xmldoc, deep?1: 0);
1.6       parser    341:        // write out result
1.72    ! paf       342:        writeNode(r, vxdoc, retNode);
1.6       parser    343: }
                    344: 
                    345: // DOM1 element
                    346: 
1.72    ! paf       347: xmlNode& get_self_element(VXnode& vnode) {
        !           348:        xmlNode& node=vnode.get_xmlnode();
1.6       parser    349: 
1.72    ! paf       350:        if(node.type!=XML_ELEMENT_NODE)
1.41      paf       351:                throw Exception("parser.runtime",
1.56      paf       352:                        0,
1.51      paf       353:                        "method can only be called on nodes of ELEMENT type");
1.6       parser    354: 
1.72    ! paf       355:        return node;
1.6       parser    356: }
                    357: 
1.56      paf       358: 
                    359: static void _getAttribute(Request& r, MethodParams& params) {
1.67      paf       360:        VXnode& vnode=GET_SELF(r, VXnode);
1.72    ! paf       361:        xmlNode& element=get_self_element(vnode);
        !           362:        const xmlChar* name=as_xmlchar(r, params, 0, "name must be string");
1.6       parser    363: 
1.72    ! paf       364:        // todo: when name="xmlns"
        !           365:        xmlChar* attribute_value=xmlGetProp(&element, name);
1.6       parser    366:        // write out result
1.68      paf       367:        r.write_pass_lang(r.transcode(attribute_value));
1.6       parser    368: }
                    369: 
                    370: // void setAttribute(in DOMString name, in DOMString value) raises(DOMException);
1.56      paf       371: static void _setAttribute(Request& r, MethodParams& params) {
1.67      paf       372:        VXnode& vnode=GET_SELF(r, VXnode);
1.72    ! paf       373:        xmlNode& element=get_self_element(vnode);
        !           374:        const xmlChar* name=as_xmlchar(r, params, 0, "name must be string");
        !           375:        const xmlChar* attribute_value=as_xmlchar(r, params, 1, "value must be string");
        !           376: 
        !           377:        // todo: when name="xmlns"
        !           378:        if(!xmlSetProp(&element, name,  attribute_value))
        !           379:                throw XmlException(0);
1.6       parser    380: }
                    381: 
                    382: // void removeAttribute(in DOMString name) raises(DOMException);
1.56      paf       383: static void _removeAttribute(Request& r, MethodParams& params) {
1.67      paf       384:        VXnode& vnode=GET_SELF(r, VXnode);
1.72    ! paf       385:        xmlNode& element=get_self_element(vnode);
        !           386:        const xmlChar* name=as_xmlchar(r, params, 0, "name must be string");
1.6       parser    387: 
1.72    ! paf       388:        // todo: when name="xmlns"
        !           389:        xmlUnsetProp(&element, name);
1.6       parser    390: }
                    391: 
                    392: // Attr getAttributeNode(in DOMString name);
1.56      paf       393: static void _getAttributeNode(Request& r, MethodParams& params) {
1.67      paf       394:        VXnode& vnode=GET_SELF(r, VXnode);
1.72    ! paf       395:        VXdoc& vxdoc=vnode.get_vxdoc();
        !           396:        xmlNode& element=get_self_element(vnode);
        !           397:        const xmlChar* localName=as_xmlchar(r, params, 0, "name must be string");
1.6       parser    398: 
1.72    ! paf       399:        if(xmlNode* retNode=pa_getAttributeNodeNS(element, localName, 0)){
1.6       parser    400:                // write out result
1.72    ! paf       401:                writeNode(r, vxdoc, retNode);
        !           402:        }
1.6       parser    403: }      
                    404: 
                    405: // Attr setAttributeNode(in Attr newAttr) raises(DOMException);
1.72    ! paf       406: // Attr setAttributeNodeNS(in Attr newAttr) raises(DOMException);
1.56      paf       407: static void _setAttributeNode(Request& r, MethodParams& params) {
1.67      paf       408:        VXnode& vnode=GET_SELF(r, VXnode);
1.72    ! paf       409:        VXdoc& vxdoc=vnode.get_vxdoc();
        !           410:        xmlNode& element=get_self_element(vnode);
        !           411:        xmlDoc& xmldoc=vxdoc.get_xmldoc();
        !           412:        xmlAttr& newAttr=as_attr(params, 0, "newAttr must be ATTRIBUTE node");
        !           413: 
        !           414:        if(newAttr.doc!=&xmldoc)
        !           415:                throw Exception("xml",
        !           416:                        0,
        !           417:                        "WRONG_DOCUMENT_ERR");
1.6       parser    418: 
1.72    ! paf       419:        if(newAttr.parent)
        !           420:                throw Exception("xml",
        !           421:                        0,
        !           422:                        "INUSE_ATTRIBUTE_ERR");
        !           423:        
        !           424:        if(xmlNode* retNode=pa_getAttributeNodeNS(element, newAttr.name, pa_xmlGetNsURI((xmlNode*)&newAttr))) {
1.19      paf       425:                // write out result
1.72    ! paf       426:                writeNode(r, vxdoc, retNode);
        !           427:                xmlUnlinkNode(retNode);
        !           428:        }
        !           429: 
        !           430:        pa_addAttributeNode(element, newAttr);
1.6       parser    431: }      
                    432: 
                    433: // Attr removeAttributeNode(in Attr oldAttr) raises(DOMException);
1.56      paf       434: static void _removeAttributeNode(Request& r, MethodParams& params) {
1.67      paf       435:        VXnode& vnode=GET_SELF(r, VXnode);
1.72    ! paf       436:        VXdoc& vxdoc=vnode.get_vxdoc();
        !           437:        xmlNode& element=get_self_element(vnode);
        !           438:        xmlAttr& oldAttr=as_attr(params, 0, "oldAttr must be ATTRIBUTE node");
        !           439: 
        !           440:        if(oldAttr.parent!=&element)
        !           441:                throw Exception("xml",
        !           442:                        0,
        !           443:                        "NOT_FOUND_ERR");
1.6       parser    444: 
1.72    ! paf       445:        xmlUnlinkNode((xmlNode*)&oldAttr);
        !           446: 
        !           447:        // write out result
        !           448:        writeNode(r, vxdoc, (xmlNode*)&oldAttr);
1.6       parser    449: }      
                    450: 
                    451: // NodeList getElementsByTagName(in DOMString name);
1.56      paf       452: static void _getElementsByTagName(Request& r, MethodParams& params) {
1.67      paf       453:        VXnode& vnode=GET_SELF(r, VXnode);
1.72    ! paf       454:        VXdoc& vxdoc=vnode.get_vxdoc();
        !           455:        xmlNode& xmlnode=vnode.get_xmlnode();
1.6       parser    456: 
1.72    ! paf       457:        xmlChar* tagName=as_xmlchar(r, params, 0, "name must be string");
1.6       parser    458: 
1.56      paf       459:        VHash& result=*new VHash;
1.72    ! paf       460:        AccumulateFoundInfo info={&result.hash(), &vxdoc, 0};
        !           461:        pa_xmlNamedPreorderTraversal(&xmlnode, 
        !           462:                                                          0, 
        !           463:                                                          tagName, 
        !           464:                                                          AccumulateFound,
        !           465:                                                          &info);
1.6       parser    466: 
                    467:        // write out result
                    468:        r.write_no_lang(result);
                    469: }
                    470: 
1.57      paf       471: // DOM 2
                    472: 
1.58      paf       473: // DOMString getAttributeNS(in DOMString namespaceURI, in DOMString localName);
1.57      paf       474: static void _getAttributeNS(Request& r, MethodParams& params) {
1.67      paf       475:        VXnode& vnode=GET_SELF(r, VXnode);
1.72    ! paf       476:        xmlNode& element=get_self_element(vnode);
1.57      paf       477:        
1.72    ! paf       478:        xmlChar* namespaceURI=as_xmlchar(r, params, 0, "namespaceURI must be string");
        !           479:        xmlChar* localName=as_xmlchar(r, params, 1, "localName must be string");
1.57      paf       480: 
1.72    ! paf       481:        // todo: when name="xmlns"
        !           482:        xmlChar* attribute_value=xmlGetNsProp(&element, localName, namespaceURI);
1.57      paf       483:        // write out result
1.68      paf       484:        r.write_pass_lang(r.transcode(attribute_value));
1.57      paf       485: }
                    486: 
1.72    ! paf       487: 
1.58      paf       488: // void setAttributeNS(in DOMString namespaceURI, in DOMString qualifiedName, in DOMString value) raises(DOMException);
1.57      paf       489: static void _setAttributeNS(Request& r, MethodParams& params) {
1.67      paf       490:        VXnode& vnode=GET_SELF(r, VXnode);
1.72    ! paf       491:        xmlNode& element=get_self_element(vnode);
        !           492:        VXdoc& vxdoc=vnode.get_vxdoc();
        !           493:        xmlDoc& xmldoc=vxdoc.get_xmldoc();
        !           494:        const xmlChar* namespaceURI=as_xmlchar(r, params, 0, "namespaceURI must be string");
        !           495:        const xmlChar* qualifiedName=as_xmlchar(r, params, 1, "qualifiedName must be string");
        !           496:        const xmlChar* attribute_value=as_xmlchar(r, params, 2, "value must be string");
        !           497: 
        !           498:        xmlChar* prefix=0;
        !           499:        xmlChar* localName=xmlSplitQName2(qualifiedName, &prefix);
        !           500: 
        !           501:        // todo: name=xmlns
        !           502:        xmlAttr* attrNode;
        !           503:        if(localName) {
        !           504:                xmlNs& ns=pa_xmlMapNs(xmldoc, namespaceURI, prefix);
        !           505:                
        !           506:                attrNode=xmlSetNsProp(&element, &ns,
        !           507:                        localName,
        !           508:                        attribute_value);
        !           509:        } else {
        !           510:                attrNode=xmlSetProp(&element, 
        !           511:                        qualifiedName/*unqualified, actually*/,
        !           512:                        attribute_value);
        !           513:        }
        !           514: 
        !           515:        if(!attrNode)
        !           516:                throw XmlException(0);
1.57      paf       517: }
                    518: 
1.58      paf       519: // void removeAttributeNS(in DOMString namespaceURI, in DOMString localName) raises(DOMException);
1.57      paf       520: static void _removeAttributeNS(Request& r, MethodParams& params) {
1.67      paf       521:        VXnode& vnode=GET_SELF(r, VXnode);
1.72    ! paf       522:        xmlNode& element=get_self_element(vnode);
        !           523:        VXdoc& vxdoc=vnode.get_vxdoc();
        !           524:        xmlDoc& xmldoc=vxdoc.get_xmldoc();
        !           525:        const xmlChar* namespaceURI=as_xmlchar(r, params, 0, "namespaceURI must be string");
        !           526:        const xmlChar* localName=as_xmlchar(r, params, 1, "localName must be string");
        !           527: 
        !           528:        // todo: when name="xmlns"
        !           529:        xmlNs& ns=pa_xmlMapNs(xmldoc, namespaceURI, 0);
        !           530:        xmlUnsetNsProp(&element, &ns, localName);
1.57      paf       531: }
                    532: 
1.58      paf       533: // Attr getAttributeNodeNS(in DOMString namespaceURI, in DOMString localName);
1.57      paf       534: static void _getAttributeNodeNS(Request& r, MethodParams& params) {
1.67      paf       535:        VXnode& vnode=GET_SELF(r, VXnode);
1.72    ! paf       536:        VXdoc& vxdoc=vnode.get_vxdoc();
        !           537:        xmlNode& element=get_self_element(vnode);
        !           538:        const xmlChar* namespaceURI=as_xmlchar(r, params, 0, "namespaceURI must be string");
        !           539:        const xmlChar* localName=as_xmlchar(r, params, 1, "localName must be string");
1.57      paf       540: 
1.72    ! paf       541:        if(xmlNode* retNode=pa_getAttributeNodeNS(element, localName, namespaceURI)){
1.57      paf       542:                // write out result
1.72    ! paf       543:                writeNode(r, vxdoc, retNode);
        !           544:        }
        !           545: }      
1.57      paf       546: 
1.58      paf       547: // boolean hasAttribute(in DOMString name) raises(DOMException);
1.57      paf       548: static void _hasAttribute(Request& r, MethodParams& params) {
1.67      paf       549:        VXnode& vnode=GET_SELF(r, VXnode);
1.72    ! paf       550:        xmlNode& element=get_self_element(vnode);
1.57      paf       551: 
1.72    ! paf       552:        const xmlChar* name=as_xmlchar(r, params, 0, "name must be string");
1.57      paf       553: 
1.72    ! paf       554:        xmlChar* prop=xmlGetProp(&element, name);
        !           555:        // todo: when name="xmlns"
1.57      paf       556:        // write out result
1.72    ! paf       557:        r.write_no_lang(*new VBool(prop!=0));
1.57      paf       558: }
                    559: 
1.58      paf       560: // boolean hasAttributeNS(n DOMString namespaceURI, in DOMString localName) raises(DOMException);
1.57      paf       561: static void _hasAttributeNS(Request& r, MethodParams& params) {
1.67      paf       562:        VXnode& vnode=GET_SELF(r, VXnode);
1.72    ! paf       563:        xmlNode& element=get_self_element(vnode);
1.57      paf       564: 
1.72    ! paf       565:        const xmlChar* namespaceURI=as_xmlchar(r, params, 0, "namespaceURI must be string");
        !           566:        const xmlChar* localName=as_xmlchar(r, params, 1, "localName must be string");
1.57      paf       567: 
1.72    ! paf       568:        xmlChar* prop=xmlGetNsProp(&element, localName, namespaceURI);
1.57      paf       569:        // write out result
1.72    ! paf       570:        r.write_no_lang(*new VBool(prop!=0));
1.57      paf       571: }
                    572: 
1.56      paf       573: static void _getElementsByTagNameNS(Request& r, MethodParams& params) {
1.67      paf       574:        VXnode& vnode=GET_SELF(r, VXnode);
1.72    ! paf       575:        VXdoc& vdoc=vnode.get_vxdoc();
        !           576:        xmlDoc& xmldoc=vdoc.get_xmldoc();
1.39      paf       577: 
                    578:        // namespaceURI;localName
1.72    ! paf       579:        xmlChar* namespaceURI=as_xmlchar(r, params, 0, "namespaceURI must be string");
        !           580:        xmlChar* localName=as_xmlchar(r, params, 1, "name must be string");
1.39      paf       581: 
1.56      paf       582:        VHash& result=*new VHash;
1.72    ! paf       583:        AccumulateFoundInfo info={&result.hash(), &vdoc, 0};
        !           584:        pa_xmlNamedPreorderTraversal((xmlNode*)&xmldoc, 
        !           585:                                                          namespaceURI, 
        !           586:                                                          localName, 
        !           587:                                                          AccumulateFound,
        !           588:                                                          &info);
1.39      paf       589: 
                    590:        // write out result
                    591:        r.write_no_lang(result);
                    592: }
                    593: 
1.72    ! paf       594: 
1.6       parser    595: // void normalize();
1.72    ! paf       596: static void _normalize(Request&, MethodParams&) {
        !           597: /*maybe someday
1.56      paf       598:        VXnode& vnode=GET_SELF(r, VXnode);
1.72    ! paf       599:        xmlNode& selfNode=vnode.get_xmlnode();
1.6       parser    600: 
1.19      paf       601:        gdome_n_normalize(selfNode, &exc);
                    602:        if(exc)
1.60      paf       603:                throw XmlException(0, exc);
1.72    ! paf       604: */
1.6       parser    605: }
1.25      paf       606: 
1.66      paf       607: #ifndef DOXYGEN
                    608: struct Register_one_ns_info {
                    609:        Request* r;
                    610:        xmlXPathContextPtr ctxt;
                    611: };
                    612: #endif
                    613: static void register_one_ns(
                    614:                                                                  HashStringValue::key_type key, 
                    615:                                                                  HashStringValue::value_type value, 
                    616:                                                                  Register_one_ns_info* info) {
                    617:        if(const String* svalue=value->get_string())
                    618:                xmlXPathRegisterNs(info->ctxt, 
1.72    ! paf       619:                        info->r->transcode(key), 
        !           620:                        info->r->transcode(*svalue));
1.66      paf       621:        else
                    622:                throw Exception("parser.runtime",
                    623:                        new String(key, String::L_TAINTED),
                    624:                        "value is %s, must be string or number", value->type());
                    625: }
1.56      paf       626: static void _selectX(Request& r, MethodParams& params,
                    627:                                         void (*handler)(Request& r,
1.26      paf       628:                                                          const String& expression, 
                    629:                                                          xmlXPathObject_auto_ptr res,
1.67      paf       630:                                                          VXdoc& xdoc,
1.72    ! paf       631:                                                          Value*& result)) 
        !           632: {
1.56      paf       633:        VXnode& vnode=GET_SELF(r, VXnode);
1.72    ! paf       634:        xmlNode& element=get_self_element(vnode);
        !           635:        VXdoc& vdoc=vnode.get_vxdoc();
        !           636:        xmlDoc& xmldoc=vdoc.get_xmldoc();
1.1       parser    637: 
                    638:        // expression
1.56      paf       639:        const String& expression=params.as_string(0, "expression must be string");
1.72    ! paf       640:        xmlXPathContext_auto_ptr ctxt(xmlXPathNewContext(&xmldoc));
1.67      paf       641:        {
1.66      paf       642:                Register_one_ns_info info={&r, ctxt.get()};
1.72    ! paf       643:                vdoc.search_namespaces.hash().for_each(register_one_ns, &info);
1.66      paf       644:        }
1.72    ! paf       645:        ctxt->node=&element;
1.25      paf       646:        /*error to stderr for now*/
                    647:        xmlXPathObject_auto_ptr res(
1.72    ! paf       648:                xmlXPathEvalExpression(r.transcode(expression), ctxt.get()));
1.28      paf       649: 
1.72    ! paf       650:        if(xmlHaveGenericErrors())
        !           651:                throw XmlException(0);
1.25      paf       652: 
1.56      paf       653:        Value* result=0;
1.25      paf       654:        if(res.get())
1.72    ! paf       655:                handler(r, expression, res, vdoc, result);
1.44      paf       656:        if(result)
1.26      paf       657:                r.write_no_lang(*result);
                    658: }
1.25      paf       659: 
1.72    ! paf       660: static void selectNodesHandler(Request&,
1.56      paf       661:                               const String& expression,
                    662:                               xmlXPathObject_auto_ptr res,
1.67      paf       663:                                   VXdoc& xdoc,
1.56      paf       664:                               Value*& result) {
                    665:        VHash& vhash=*new VHash;  result=&vhash;
1.26      paf       666:        switch(res->type) {
                    667:        case XPATH_UNDEFINED: 
                    668:                break;
                    669:        case XPATH_NODESET:
1.40      paf       670:                if(res->nodesetval)
                    671:                        if(int size=res->nodesetval->nodeNr) {
1.56      paf       672:                                HashStringValue& hash=vhash.hash();
                    673:                                for(int i=0; i<size; i++)
                    674:                                        hash.put(
1.59      paf       675:                                                String::Body::Format(i), 
1.72    ! paf       676:                                                &xdoc.wrap(*res->nodesetval->nodeTab[i]));
1.1       parser    677:                        }
1.26      paf       678:                break;
                    679:        default: 
1.41      paf       680:                throw Exception(0,
1.26      paf       681:                        &expression,
                    682:                        "wrong xmlXPathEvalExpression result type (%d)", res->type);
                    683:                break; // never
                    684:        }
                    685: }
                    686: 
1.56      paf       687: static void selectNodeHandler(Request& r,
                    688:                              const String& expression,
1.67      paf       689:                                  xmlXPathObject_auto_ptr res,
                    690:                                  VXdoc& xdoc,
                    691:                                  Value*& result) {
1.26      paf       692:        switch(res->type) {
                    693:        case XPATH_UNDEFINED: 
                    694:                break;
                    695:        case XPATH_NODESET: 
1.40      paf       696:                if(res->nodesetval && res->nodesetval->nodeNr) { // empty result strangly has NODESET  res->type
1.26      paf       697:                        if(res->nodesetval->nodeNr>1)
1.41      paf       698:                                throw Exception("parser.runtime",
1.56      paf       699:                                        &expression,
                    700:                                        "resulted not in a single node (%d)", res->nodesetval->nodeNr);
1.26      paf       701:                        
1.72    ! paf       702:                        result=&xdoc.wrap(*res->nodesetval->nodeTab[0]);
1.25      paf       703:                }
1.26      paf       704:                break;
                    705:        case XPATH_BOOLEAN: 
1.56      paf       706:                result=new VBool(res->boolval!=0);
1.26      paf       707:                break;
                    708:        case XPATH_NUMBER: 
1.56      paf       709:                result=new VDouble(res->floatval);
1.26      paf       710:                break;
                    711:        case XPATH_STRING:
1.56      paf       712:                result=new VString(r.transcode((xmlChar*)res->stringval));
1.26      paf       713:                break;
                    714:        default: 
1.41      paf       715:                throw Exception("parser.runtime",
1.26      paf       716:                        &expression,
                    717:                        "wrong xmlXPathEvalExpression result type (%d)", res->type);
                    718:        }
                    719: }
                    720: 
1.62      paf       721: static void selectBoolHandler(Request&,
1.56      paf       722:                              const String& expression,
                    723:                              xmlXPathObject_auto_ptr res,
1.67      paf       724:                                  VXdoc& /*xdoc*/,
1.56      paf       725:                              Value*& result) {
1.26      paf       726:        switch(res->type) {
                    727:        case XPATH_BOOLEAN: 
1.56      paf       728:                result=new VBool(res->boolval!=0);
1.26      paf       729:                break;
                    730:        case XPATH_NODESET: 
1.40      paf       731:                if(!(res->nodesetval && res->nodesetval->nodeNr))
1.26      paf       732:                        break;
                    733:                // else[nodeset] fall down to default
                    734:        default: 
1.41      paf       735:                throw Exception("parser.runtime",
1.26      paf       736:                        &expression,
                    737:                        "wrong xmlXPathEvalExpression result type (%d)", res->type);
                    738:                break; // never
                    739:        }
                    740: }
1.1       parser    741: 
1.62      paf       742: static void selectNumberHandler(Request&,
1.56      paf       743:                                const String& expression,
                    744:                                xmlXPathObject_auto_ptr res,
1.67      paf       745:                                VXdoc& /*xdoc*/,
1.56      paf       746:                                Value*& result) {
1.26      paf       747:        switch(res->type) {
                    748:        case XPATH_NUMBER: 
1.56      paf       749:                result=new VDouble(res->floatval);
1.26      paf       750:                break;
                    751:        case XPATH_NODESET:
1.40      paf       752:                if(!(res->nodesetval && res->nodesetval->nodeNr))
1.26      paf       753:                        break;
                    754:                // else[nodeset] fall down to default
                    755:        default: 
1.41      paf       756:                throw Exception("parser.runtime",
1.26      paf       757:                        &expression,
                    758:                        "wrong xmlXPathEvalExpression result type (%d)", res->type);
                    759:                break; // never
                    760:        }
                    761: }
                    762: 
1.56      paf       763: static void selectStringHandler(Request& r,
1.26      paf       764:                                                          const String& expression,
                    765:                                                          xmlXPathObject_auto_ptr res,
1.67      paf       766:                                                          VXdoc& /*xdoc*/,
1.56      paf       767:                                                          Value*& result) {
1.26      paf       768:        switch(res->type) {
                    769:        case XPATH_UNDEFINED: 
                    770:                break;
                    771:        case XPATH_STRING:
1.56      paf       772:                result=new VString(r.transcode((xmlChar*)res->stringval));
1.26      paf       773:                break;
                    774:        case XPATH_NODESET: 
1.40      paf       775:                if(!(res->nodesetval && res->nodesetval->nodeNr))
1.26      paf       776:                        break;
                    777:                // else[nodeset] fall down to default
                    778:        default: 
1.41      paf       779:                throw Exception("parser.runtime",
1.26      paf       780:                        &expression,
                    781:                        "wrong xmlXPathEvalExpression result type (%d)", res->type);
                    782:                break; // never
1.1       parser    783:        }
                    784: }
1.25      paf       785: 
1.56      paf       786: static void _select(Request& r, MethodParams& params) {
                    787:        _selectX(r, params,
1.26      paf       788:                selectNodesHandler);
                    789: }
                    790: 
1.56      paf       791: static void _selectSingle(Request& r, MethodParams& params) {
                    792:        _selectX(r, params,
1.26      paf       793:                selectNodeHandler);
                    794: }
1.1       parser    795: 
1.56      paf       796: static void _selectBool(Request& r, MethodParams& params) {
                    797:        _selectX(r, params,
1.26      paf       798:                selectBoolHandler);
                    799: }
1.20      paf       800: 
1.56      paf       801: static void _selectNumber(Request& r, MethodParams& params) {
                    802:        _selectX(r, params,
1.26      paf       803:                selectNumberHandler);
                    804: }
1.24      paf       805: 
1.56      paf       806: static void _selectString(Request& r, MethodParams& params) {
                    807:        _selectX(r, params,
1.26      paf       808:                selectStringHandler);
1.1       parser    809: }
1.20      paf       810: 
1.1       parser    811: // constructor
                    812: 
1.56      paf       813: /// @bug one can change const and ruin other's work, we need unchangable VIntConst class
                    814: MXnode::MXnode(const char* aname, VStateless_class *abase):
                    815:        Methoded(aname?aname:"xnode", abase)
1.44      paf       816: {
1.6       parser    817:        /// DOM1 node
                    818: 
                    819:        // Node insertBefore(in Node newChild,in Node refChild) raises(DOMException);
                    820:        add_native_method("insertBefore", Method::CT_DYNAMIC, _insertBefore, 2, 2);
                    821:        // Node replaceChild(in Node newChild,in Node oldChild) raises(DOMException);
                    822:        add_native_method("replaceChild", Method::CT_DYNAMIC, _replaceChild, 2, 2);
                    823:        // Node removeChild(in Node oldChild) raises(DOMException);
                    824:        add_native_method("removeChild", Method::CT_DYNAMIC, _removeChild, 1, 1);
                    825:        // Node appendChild(in Node newChild) raises(DOMException);
                    826:        add_native_method("appendChild", Method::CT_DYNAMIC, _appendChild, 1, 1);
                    827:        // boolean hasChildNodes();
                    828:        add_native_method("hasChildNodes", Method::CT_DYNAMIC, _hasChildNodes, 0, 0);
                    829:        // Node cloneNode(in boolean deep);
                    830:        add_native_method("cloneNode", Method::CT_DYNAMIC, _cloneNode, 1, 1);
                    831: 
                    832:        /// DOM1 element
                    833: 
                    834:        // DOMString getAttribute(in DOMString name);
                    835:        add_native_method("getAttribute", Method::CT_DYNAMIC, _getAttribute, 1, 1);
                    836:        // void setAttribute(in DOMString name, in DOMString value) raises(DOMException);
                    837:        add_native_method("setAttribute", Method::CT_DYNAMIC, _setAttribute, 2, 2);
                    838:        // void removeAttribute(in DOMString name) raises(DOMException);
                    839:        add_native_method("removeAttribute", Method::CT_DYNAMIC, _removeAttribute, 1, 1);
                    840:        // Attr getAttributeNode(in DOMString name);
                    841:        add_native_method("getAttributeNode", Method::CT_DYNAMIC, _getAttributeNode, 1, 1);
                    842:        // Attr setAttributeNode(in Attr newAttr) raises(DOMException);
                    843:        add_native_method("setAttributeNode", Method::CT_DYNAMIC, _setAttributeNode, 1, 1);
                    844:        // Attr removeAttributeNode(in Attr oldAttr) raises(DOMException);
                    845:        add_native_method("removeAttributeNode", Method::CT_DYNAMIC, _removeAttributeNode, 1, 1);
                    846:        // NodeList getElementsByTagName(in DOMString name);
                    847:        add_native_method("getElementsByTagName", Method::CT_DYNAMIC, _getElementsByTagName, 1, 1);
1.39      paf       848:        // NodeList getElementsByTagNameNS(in DOMString namespaceURI, in DOMString localName);
                    849:        add_native_method("getElementsByTagNameNS", Method::CT_DYNAMIC, _getElementsByTagNameNS, 2, 2);
1.6       parser    850:        // void normalize();
                    851:        add_native_method("normalize", Method::CT_DYNAMIC, _normalize, 0, 0);
1.57      paf       852: 
                    853:        /// DOM2 element
                    854: 
1.58      paf       855:        // DOMString getAttributeNS(in DOMString namespaceURI, in DOMString localName);
1.57      paf       856:        add_native_method("getAttributeNS", Method::CT_DYNAMIC, _getAttributeNS, 2, 2);
1.58      paf       857:        // void setAttributeNS(in DOMString namespaceURI, in DOMString qualifiedName, in DOMString value) raises(DOMException);
1.57      paf       858:        add_native_method("setAttributeNS", Method::CT_DYNAMIC, _setAttributeNS, 3, 3);
1.58      paf       859:        // void removeAttributeNS(in DOMString namespaceURI, in DOMString localName) raises(DOMException);
1.57      paf       860:        add_native_method("removeAttributeNS", Method::CT_DYNAMIC, _removeAttributeNS, 2, 2);
1.58      paf       861:        // Attr getAttributeNodeNS(in DOMString namespaceURI, in DOMString localName);
1.57      paf       862:        add_native_method("getAttributeNodeNS", Method::CT_DYNAMIC, _getAttributeNodeNS, 2, 2);
                    863:        // Attr setAttributeNodeNS(in Attr newAttr) raises(DOMException);
1.72    ! paf       864:        add_native_method("setAttributeNodeNS", Method::CT_DYNAMIC, _setAttributeNode, 1, 1);
1.58      paf       865:        // boolean hasAttribute(in DOMString name) raises(DOMException);
1.57      paf       866:        add_native_method("hasAttribute", Method::CT_DYNAMIC, _hasAttribute, 1, 1);
1.58      paf       867:        // boolean hasAttributeNS(in DOMString namespaceURI, in DOMString localName) raises(DOMException);
1.57      paf       868:        add_native_method("hasAttributeNS", Method::CT_DYNAMIC, _hasAttributeNS, 2, 2);
1.6       parser    869: 
                    870:        /// parser
1.1       parser    871:        // ^node.select[/some/xpath/query] = hash $.#[dnode]
1.67      paf       872:        add_native_method("select", Method::CT_DYNAMIC, _select, 1, 1);
1.1       parser    873: 
1.26      paf       874:        // ^node.selectSingle[/some/xpath/query] = first node [if any]
1.67      paf       875:        add_native_method("selectSingle", Method::CT_DYNAMIC, _selectSingle, 1, 1);
1.26      paf       876:        // ^node.selectBool[/some/xpath/query] = bool value [if any]
1.67      paf       877:        add_native_method("selectBool", Method::CT_DYNAMIC, _selectBool, 1, 1);
1.26      paf       878:        // ^node.selectNumber[/some/xpath/query] = double value [if any]
1.67      paf       879:        add_native_method("selectNumber", Method::CT_DYNAMIC, _selectNumber, 1, 1);
1.26      paf       880:        // ^node.selectString[/some/xpath/query] = strinv value [if any]
1.67      paf       881:        add_native_method("selectString", Method::CT_DYNAMIC, _selectString, 1, 1);
1.20      paf       882: 
1.2       parser    883:        // consts
                    884: 
1.19      paf       885: #define CONST(name) \
1.72    ! paf       886:        consts.put(String::Body(#name), new VInt(XML_##name))
        !           887: #define CONST2(name, value) \
        !           888:        consts.put(String::Body(#name), new VInt(value))
1.2       parser    889: 
1.19      paf       890:        CONST(ELEMENT_NODE);
1.56      paf       891:        CONST(ATTRIBUTE_NODE);
                    892:        CONST(TEXT_NODE);
                    893:        CONST(CDATA_SECTION_NODE);
1.72    ! paf       894:        CONST2(ENTITY_REFERENCE_NODE, XML_ENTITY_REF_NODE);
1.56      paf       895:        CONST(ENTITY_NODE);
1.72    ! paf       896:        CONST2(PROCESSING_INSTRUCTION_NODE, XML_PI_NODE);
1.56      paf       897:        CONST(COMMENT_NODE);
                    898:        CONST(DOCUMENT_NODE);
                    899:        CONST(DOCUMENT_TYPE_NODE);
1.72    ! paf       900:        CONST2(DOCUMENT_FRAGMENT_NODE, XML_DOCUMENT_FRAG_NODE);
1.56      paf       901:        CONST(NOTATION_NODE);
                    902: }
1.1       parser    903: 
1.56      paf       904: #else
1.3       parser    905: 
1.1       parser    906: // global variable
                    907: 
1.56      paf       908: DECLARE_CLASS_VAR(xnode, 0, 0); // fictive
1.1       parser    909: 
                    910: #endif

E-mail: