1. Shlomi Fish
  2. perl-XML-LibXML

Commits

pa...@9ae0c189-cd1f-4510-a509-f4891f5cf20d  committed a298949

merged in XML::LibXML::XPathContext

  • Participants
  • Parent commits 5094f71
  • Branches default

Comments (0)

Files changed (13)

File Changes

View file
  • Ignore whitespace
 1.61pre 
    - get{Elements,Children}By{TagName,TagNameNS,LocalName} now
      obey wildcards '*', getChildrenByLocalName was added.
+   - XML::LibXML::XPathContext merged in
 
 1.60
    - getElementsById corrected to getElementById and the old name kept

File LibXML.pm

View file
  • Ignore whitespace
 use IO::Handle; # for FH reads called as methods
 
 
-$VERSION = "1.60";
+$VERSION = "1.61";
 require Exporter;
 require DynaLoader;
 

File LibXML.xs

View file
  • Ignore whitespace
 #include <libxml/c14n.h>
 #include <libxml/tree.h>
 #include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
 #include <libxml/xmlIO.h>
 /* #include <libxml/debugXML.h> */
 #include <libxml/xmlerror.h>
 
 #include "dom.h"
 #include "xpath.h"
+#include "xpathcontext.h"
 
 #ifdef __cplusplus
 }
         dTHX;
         dSP;
 
-        int cnt;
         SV * tbuff = newSVpv(buffer,len);
         SV * tsize = newSViv(len);
 
     /* we fetch all switches and callbacks from the hash */
     HV* real_obj = NULL;
     SV** item    = NULL;
-    SV*  item2   = NULL;
 
     /* A NOTE ABOUT xmlInitParser();                     */
     /* xmlInitParser() should be used only at startup and*/
     return(1);
 }
 
+/* ****************************************************************
+ * XPathContext helper functions
+ * **************************************************************** */
+
+/* Temporary node pool:                                              *
+ * Stores pnode in context node-pool hash table in order to preserve *
+ * at least one reference.                                           *
+ * If pnode is NULL, only return current value for hashkey           */
+static SV*
+LibXML_XPathContext_pool ( xmlXPathContextPtr ctxt, int hashkey, SV * pnode ) {
+    SV ** value;
+    SV * key;
+    STRLEN len;
+    char * strkey;
+    dTHX;
+
+    if (XPathContextDATA(ctxt)->pool == NULL) {
+        if (pnode == NULL) {
+            return &PL_sv_undef;
+        } else {
+            xs_warn("initializing node pool");
+            XPathContextDATA(ctxt)->pool = newHV();
+        }
+    }
+
+    key = newSViv(hashkey);
+    strkey = SvPV(key, len);
+    if (pnode != NULL && !hv_exists(XPathContextDATA(ctxt)->pool,strkey,len)) {        
+        value = hv_store(XPathContextDATA(ctxt)->pool,strkey,len, SvREFCNT_inc(pnode),0);
+    } else {
+        value = hv_fetch(XPathContextDATA(ctxt)->pool,strkey,len, 0);
+    }
+    SvREFCNT_dec(key);
+    
+    if (value == NULL) {
+        return &PL_sv_undef;
+    } else {
+        return *value;
+    }
+}
+
+/* convert perl result structures to LibXML structures */
+static xmlXPathObjectPtr
+LibXML_perldata_to_LibXMLdata(xmlXPathParserContextPtr ctxt,
+                              SV* perl_result) {
+    dTHX;
+    if (!SvOK(perl_result)) {
+        return (xmlXPathObjectPtr)xmlXPathNewCString("");        
+    }
+    if (SvROK(perl_result) &&
+        SvTYPE(SvRV(perl_result)) == SVt_PVAV) {
+        /* consider any array ref to be a nodelist */
+        int i;
+        int length;
+        SV ** pnode;
+        AV * array_result;
+        xmlXPathObjectPtr ret;
+
+        ret = (xmlXPathObjectPtr) xmlXPathNewNodeSet((xmlNodePtr) NULL);
+        array_result = (AV*)SvRV(perl_result);
+        length = av_len(array_result);
+        for( i = 0; i <= length ; i++ ) {
+            pnode = av_fetch(array_result,i,0);
+            if (pnode != NULL && sv_isobject(*pnode) &&
+                sv_derived_from(*pnode,"XML::LibXML::Node")) {
+                xmlXPathNodeSetAdd(ret->nodesetval, 
+                                   (xmlNodePtr)PmmSvNode(*pnode));
+                if(ctxt) {
+                    LibXML_XPathContext_pool(ctxt->context,
+                                             (int) PmmSvNode(*pnode), *pnode);
+                }
+            } else {
+                warn("XPathContext: ignoring non-node member of a nodelist");
+            }
+        }
+        return ret;
+    } else if (sv_isobject(perl_result) && 
+               (SvTYPE(SvRV(perl_result)) == SVt_PVMG)) 
+        {
+            if (sv_derived_from(perl_result, "XML::LibXML::Node")) {
+                xmlNodePtr tmp_node;
+                xmlXPathObjectPtr ret;
+
+                ret =  (xmlXPathObjectPtr)xmlXPathNewNodeSet(NULL);
+                tmp_node = (xmlNodePtr)PmmSvNode(perl_result);
+                xmlXPathNodeSetAdd(ret->nodesetval,tmp_node);
+                if(ctxt) {
+                    LibXML_XPathContext_pool(ctxt->context, (int) PmmSvNode(perl_result), 
+                                             perl_result);
+                }
+
+                return ret;
+            }
+            else if (sv_isa(perl_result, "XML::LibXML::Boolean")) {
+                return (xmlXPathObjectPtr)
+                    xmlXPathNewBoolean(SvIV(SvRV(perl_result)));
+            }
+            else if (sv_isa(perl_result, "XML::LibXML::Literal")) {
+                return (xmlXPathObjectPtr)
+                    xmlXPathNewCString(SvPV_nolen(SvRV(perl_result)));
+            }
+            else if (sv_isa(perl_result, "XML::LibXML::Number")) {
+                return (xmlXPathObjectPtr)
+                    xmlXPathNewFloat(SvNV(SvRV(perl_result)));
+            }
+        } else if (SvNOK(perl_result) || SvIOK(perl_result)) {
+            return (xmlXPathObjectPtr)xmlXPathNewFloat(SvNV(perl_result));
+        } else {
+            return (xmlXPathObjectPtr)
+                xmlXPathNewCString(SvPV_nolen(perl_result));
+    }
+    return NULL;
+}
+
+
+/* save XPath context and XPathContextDATA for recursion */
+static xmlXPathContextPtr
+LibXML_save_context(xmlXPathContextPtr ctxt)
+{
+    xmlXPathContextPtr copy;
+    copy = xmlMalloc(sizeof(xmlXPathContext));
+    if (copy) {
+	/* backup ctxt */
+	memcpy(copy, ctxt, sizeof(xmlXPathContext));
+	/* clear namespaces so that they are not freed and overwritten
+	   by configure_namespaces */
+	ctxt->namespaces = NULL;
+	/* backup data */
+	copy->user = xmlMalloc(sizeof(XPathContextData));
+	if (XPathContextDATA(copy)) {
+	    memcpy(XPathContextDATA(copy), XPathContextDATA(ctxt),sizeof(XPathContextData));
+	    /* clear ctxt->pool, so that it is not used freed during re-entrance */
+	    XPathContextDATA(ctxt)->pool = NULL; 
+	}
+    }
+    return copy;
+}
+
+/* restore XPath context and XPathContextDATA from a saved copy */
+static void
+LibXML_restore_context(xmlXPathContextPtr ctxt, xmlXPathContextPtr copy)
+{
+    dTHX;
+    /* cleanup */
+    if (XPathContextDATA(ctxt)) {
+	/* cleanup newly created pool */
+	if (XPathContextDATA(ctxt)->pool != NULL &&
+	    SvOK(XPathContextDATA(ctxt)->pool)) {
+	    SvREFCNT_dec((SV *)XPathContextDATA(ctxt)->pool);
+	}
+    }
+    if (ctxt->namespaces) {
+	/* free namespaces allocated during recursion */
+        xmlFree( ctxt->namespaces );
+    }
+
+    /* restore context */
+    if (copy) {
+	/* 1st restore our data */
+	if (XPathContextDATA(copy)) {
+	    memcpy(XPathContextDATA(ctxt),XPathContextDATA(copy),sizeof(XPathContextData));
+	    xmlFree(XPathContextDATA(copy));
+	    copy->user = XPathContextDATA(ctxt);
+	}
+	/* now copy the rest */
+	memcpy(ctxt, copy, sizeof(xmlXPathContext));
+	xmlFree(copy);
+    }
+}
+
+
+/* ****************************************************************
+ * Variable Lookup
+ * **************************************************************** */
+/* Much of the code is borrowed from Matt Sergeant's XML::LibXSLT   */
+static xmlXPathObjectPtr
+LibXML_generic_variable_lookup(void* varLookupData,
+                               const xmlChar *name,
+                               const xmlChar *ns_uri)
+{
+    xmlXPathObjectPtr ret;
+    xmlXPathContextPtr ctxt;
+    xmlXPathContextPtr copy;
+    XPathContextDataPtr data;
+    I32 count;
+    dTHX;
+    dSP;
+
+    ctxt = (xmlXPathContextPtr) varLookupData;
+    if ( ctxt == NULL )
+	croak("XPathContext: missing xpath context");
+    data = XPathContextDATA(ctxt);
+    if ( data == NULL )
+	croak("XPathContext: missing xpath context private data");
+    if ( data->varLookup == NULL || !SvROK(data->varLookup) || 
+	 SvTYPE(SvRV(data->varLookup)) != SVt_PVCV )
+        croak("XPathContext: lost variable lookup function!");
+
+    ENTER;
+    SAVETMPS;
+    PUSHMARK(SP);
+
+    XPUSHs( (data->varData != NULL) ? data->varData : &PL_sv_undef ); 
+    XPUSHs(sv_2mortal(C2Sv(name,NULL))); 
+    XPUSHs(sv_2mortal(C2Sv(ns_uri,NULL)));
+
+    /* save context to allow recursive usage of XPathContext */
+    copy = LibXML_save_context(ctxt);
+
+    PUTBACK ;    
+    count = perl_call_sv(data->varLookup, G_SCALAR|G_EVAL);
+    SPAGAIN;
+
+    /* restore the xpath context */
+    LibXML_restore_context(ctxt, copy);
+
+    if (SvTRUE(ERRSV)) {
+        POPs;
+        croak("XPathContext: error coming back from variable lookup function. %s", SvPV_nolen(ERRSV));
+    } 
+    if (count != 1) croak("XPathContext: variable lookup function returned more than one argument!");
+
+    ret = LibXML_perldata_to_LibXMLdata(NULL, POPs);
+
+    PUTBACK;
+    FREETMPS;
+    LEAVE;    
+    return ret;
+}
+
+/* ****************************************************************
+ * Generic Extension Function
+ * **************************************************************** */
+/* Much of the code is borrowed from Matt Sergeant's XML::LibXSLT   */
+static void
+LibXML_generic_extension_function(xmlXPathParserContextPtr ctxt, int nargs) 
+{
+    xmlXPathObjectPtr obj,ret;
+    xmlNodeSetPtr nodelist = NULL;
+    int count;
+    SV * perl_dispatch;
+    int i;
+    STRLEN len;
+    ProxyNodePtr owner = NULL;
+    SV *key;
+    char *strkey;
+    const char *function, *uri;
+    SV **perl_function;
+    dTHX;
+    dSP;
+    SV * data;
+    xmlXPathContextPtr copy;
+
+    /* warn("entered LibXML_generic_extension_function for %s\n",ctxt->context->function); */
+    data = (SV *) ctxt->context->funcLookupData;
+    if (ctxt->context->funcLookupData == NULL || !SvROK(data) ||
+        SvTYPE(SvRV(data)) != SVt_PVHV) {
+        croak("XPathContext: lost function lookup data structure!");
+    }
+    
+    function = (char*) ctxt->context->function;
+    uri = (char*) ctxt->context->functionURI;
+    
+    key = newSVpvn("",0);
+    if (uri && *uri) {
+        sv_catpv(key, "{");
+        sv_catpv(key, (const char*)uri);
+        sv_catpv(key, "}");
+    }
+    sv_catpv(key, (const char*)function);
+    strkey = SvPV(key, len);
+    perl_function =
+        hv_fetch((HV*)SvRV(data), strkey, len, 0);
+    if ( perl_function == NULL || !SvOK(*perl_function) || 
+         !(SvPOK(*perl_function) ||
+           (SvROK(*perl_function) &&
+            SvTYPE(SvRV(*perl_function)) == SVt_PVCV))) {
+        croak("XPathContext: lost perl extension function!");
+    }
+    SvREFCNT_dec(key);
+
+    ENTER;
+    SAVETMPS;
+    PUSHMARK(SP);
+    
+    XPUSHs(*perl_function);
+
+    /* set up call to perl dispatcher function */
+    for (i = 0; i < nargs; i++) {
+        obj = (xmlXPathObjectPtr)valuePop(ctxt);
+        switch (obj->type) {
+        case XPATH_XSLT_TREE:
+        case XPATH_NODESET:
+            nodelist = obj->nodesetval;
+            if ( nodelist ) {
+                XPUSHs(sv_2mortal(newSVpv("XML::LibXML::NodeList", 0)));                
+                XPUSHs(sv_2mortal(newSViv(nodelist->nodeNr)));
+                if ( nodelist->nodeNr > 0 ) {
+                    int j;
+                    const char * cls = "XML::LibXML::Node";
+                    xmlNodePtr tnode;
+                    SV * element;
+
+                    len = nodelist->nodeNr;
+                    for( j = 0 ; j < len; j++){
+                        tnode = nodelist->nodeTab[j];
+                        if( tnode != NULL && tnode->doc != NULL) {
+                            owner = PmmOWNERPO(PmmNewNode((xmlNodePtr) tnode->doc));
+                        } else {
+                            owner = NULL;
+                        }
+                        if (tnode->type == XML_NAMESPACE_DECL) {
+                            element = sv_newmortal();
+                            cls = PmmNodeTypeName( tnode );
+                            element = sv_setref_pv( element,
+                                                    (const char *)cls,
+                                                    (void *)xmlCopyNamespace((xmlNsPtr)tnode)
+                                );
+                        }
+                        else {
+                            element = PmmNodeToSv(tnode, owner);
+                        }
+                        XPUSHs( sv_2mortal(element) );
+                    }
+                }
+            } else {
+                /* PP: We can't simply leave out an empty nodelist as Matt does! */
+                /* PP: The number of arguments must match! */
+                XPUSHs(sv_2mortal(newSVpv("XML::LibXML::NodeList", 0)));                
+                XPUSHs(sv_2mortal(newSViv(0)));
+            }
+            /* prevent libxml2 from freeing the actual nodes */
+            if (obj->boolval) obj->boolval=0;
+            break;
+        case XPATH_BOOLEAN:
+            XPUSHs(sv_2mortal(newSVpv("XML::LibXML::Boolean", 0)));
+            XPUSHs(sv_2mortal(newSViv(obj->boolval)));
+            break;
+        case XPATH_NUMBER:
+            XPUSHs(sv_2mortal(newSVpv("XML::LibXML::Number", 0)));
+            XPUSHs(sv_2mortal(newSVnv(obj->floatval)));
+            break;
+        case XPATH_STRING:
+            XPUSHs(sv_2mortal(newSVpv("XML::LibXML::Literal", 0)));
+            XPUSHs(sv_2mortal(C2Sv(obj->stringval, 0)));
+            break;
+        default:
+            warn("Unknown XPath return type (%d) in call to {%s}%s - assuming string", obj->type, uri, function);
+            XPUSHs(sv_2mortal(newSVpv("XML::LibXML::Literal", 0)));
+            XPUSHs(sv_2mortal(C2Sv(xmlXPathCastToString(obj), 0)));
+        }
+        xmlXPathFreeObject(obj);
+    }
+
+    /* save context to allow recursive usage of XPathContext */
+    copy = LibXML_save_context(ctxt->context);
+
+    /* call perl dispatcher */
+    PUTBACK;
+    perl_dispatch = sv_2mortal(newSVpv("XML::LibXML::XPathContext::_perl_dispatcher",0));
+    count = perl_call_sv(perl_dispatch, G_SCALAR|G_EVAL);    
+    SPAGAIN;
+
+    /* restore the xpath context */
+    LibXML_restore_context(ctxt->context, copy);
+    
+    if (SvTRUE(ERRSV)) {
+        POPs;
+        croak("XPathContext: error coming back from perl-dispatcher in pm file. %s", SvPV_nolen(ERRSV));
+    } 
+
+    if (count != 1) croak("XPathContext: perl-dispatcher in pm file returned more than one argument!");
+    
+    ret = LibXML_perldata_to_LibXMLdata(ctxt, POPs);
+
+    valuePush(ctxt, ret);
+    PUTBACK;
+    FREETMPS;
+    LEAVE;    
+}
+
+static void
+LibXML_configure_namespaces( xmlXPathContextPtr ctxt ) {
+    xmlNodePtr node = ctxt->node;
+
+    if (ctxt->namespaces != NULL) {
+        xmlFree( ctxt->namespaces );
+        ctxt->namespaces = NULL;
+    }
+    if (node != NULL) {
+        if (node->type == XML_DOCUMENT_NODE) {
+            ctxt->namespaces = xmlGetNsList( node->doc,
+                                             xmlDocGetRootElement( node->doc ) );
+        } else {
+            ctxt->namespaces = xmlGetNsList(node->doc, node);
+        }
+        ctxt->nsNr = 0;
+        if (ctxt->namespaces != NULL) {
+            while (ctxt->namespaces[ctxt->nsNr] != NULL)
+                ctxt->nsNr++;
+        }
+    }
+}
+
+static void
+LibXML_configure_xpathcontext( xmlXPathContextPtr ctxt ) {
+    xmlNodePtr node = PmmSvNode(XPathContextDATA(ctxt)->node);
+
+    if (node != NULL) {    
+        ctxt->doc = node->doc;
+    } else {
+        ctxt->doc = NULL;
+    }
+    ctxt->node = node;
+    LibXML_configure_namespaces(ctxt);
+}
+
 MODULE = XML::LibXML         PACKAGE = XML::LibXML
 
 PROTOTYPES: DISABLE
             RETVAL = xmlValidateDocument(&cvp, self);
         }
 
-        LibXML_report_error_ctx(saved_error, RETVAL);
+        LibXML_report_error_ctx(saved_error, RETVAL ? 1 : 0);
     OUTPUT:
         RETVAL
 
             cld = self->children;
             xs_warn("childnodes start");
             while ( cld ) {
-	      if ((name_wildcard && (cld->type == XML_ELEMENT_NODE) || 
+	      if (((name_wildcard && (cld->type == XML_ELEMENT_NODE)) || 
 		   xmlStrcmp( name, cld->name ) == 0)
 		   && (ns_wildcard ||
 		       (cld->ns != NULL && 
 
 #endif /* HAVE_SCHEMAS */
 
+MODULE = XML::LibXML::XPathContext     PACKAGE = XML::LibXML::XPathContext
+
+# PROTOTYPES: DISABLE
+
+SV*
+new( CLASS, ... )
+        const char * CLASS
+    PREINIT:
+        SV * pnode = &PL_sv_undef;
+    INIT:
+        xmlXPathContextPtr ctxt;
+    CODE:
+        if( items > 1 )
+            pnode = ST(1);
+
+        ctxt = xmlXPathNewContext( NULL );
+        ctxt->namespaces = NULL;
+
+        New(0, ctxt->user, sizeof(XPathContextData), XPathContextData);
+        if (ctxt->user == NULL) {
+            croak("XPathContext: failed to allocate proxy object");
+        } 
+
+        if (SvOK(pnode)) {
+          XPathContextDATA(ctxt)->node = newSVsv(pnode); 
+        } else {
+          XPathContextDATA(ctxt)->node = &PL_sv_undef;
+        }
+
+        XPathContextDATA(ctxt)->pool = NULL;
+        XPathContextDATA(ctxt)->varLookup = NULL;
+        XPathContextDATA(ctxt)->varData = NULL;
+
+        xmlXPathRegisterFunc(ctxt,
+                             (const xmlChar *) "document",
+                             perlDocumentFunction);
+
+        RETVAL = NEWSV(0,0),
+        RETVAL = sv_setref_pv( RETVAL,
+                               CLASS,
+                               (void*)ctxt );
+    OUTPUT:
+        RETVAL
+
+void
+DESTROY( self )
+        SV * self
+    INIT:
+        xmlXPathContextPtr ctxt = (xmlXPathContextPtr)SvIV(SvRV(self)); 
+    CODE:
+        xs_warn( "DESTROY XPATH CONTEXT" );
+        if (ctxt) {
+            if (XPathContextDATA(ctxt) != NULL) {
+                if (XPathContextDATA(ctxt)->node != NULL &&
+                    SvOK(XPathContextDATA(ctxt)->node)) {
+                    SvREFCNT_dec(XPathContextDATA(ctxt)->node);
+                }
+                if (XPathContextDATA(ctxt)->varLookup != NULL &&
+                    SvOK(XPathContextDATA(ctxt)->varLookup)) {
+                    SvREFCNT_dec(XPathContextDATA(ctxt)->varLookup);
+                }
+                if (XPathContextDATA(ctxt)->varData != NULL &&
+                    SvOK(XPathContextDATA(ctxt)->varData)) {
+                    SvREFCNT_dec(XPathContextDATA(ctxt)->varData);
+                }
+                if (XPathContextDATA(ctxt)->pool != NULL &&
+                    SvOK(XPathContextDATA(ctxt)->pool)) {
+                    SvREFCNT_dec((SV *)XPathContextDATA(ctxt)->pool);
+                }
+                Safefree(XPathContextDATA(ctxt));
+            }
+
+            if (ctxt->namespaces != NULL) {
+                xmlFree( ctxt->namespaces );
+            }
+            if (ctxt->funcLookupData != NULL && SvROK((SV*)ctxt->funcLookupData)
+                && SvTYPE(SvRV((SV *)ctxt->funcLookupData)) == SVt_PVHV) {
+                SvREFCNT_dec((SV *)ctxt->funcLookupData);
+            }
+            
+            xmlXPathFreeContext(ctxt);
+        }
+
+SV*
+getContextNode( self )
+        SV * self
+    INIT:
+        xmlXPathContextPtr ctxt = (xmlXPathContextPtr)SvIV(SvRV(self)); 
+        if ( ctxt == NULL ) {
+            croak("XPathContext: missing xpath context");
+        }
+    CODE:
+        if(XPathContextDATA(ctxt)->node != NULL) {
+            RETVAL = newSVsv(XPathContextDATA(ctxt)->node);
+        } else {
+            RETVAL = &PL_sv_undef;
+        }
+    OUTPUT:
+        RETVAL
+
+int
+getContextPosition( self )
+        SV * self
+    INIT:
+        xmlXPathContextPtr ctxt = (xmlXPathContextPtr)SvIV(SvRV(self)); 
+        if ( ctxt == NULL ) {
+            croak("XPathContext: missing xpath context");
+        }
+    CODE:
+        RETVAL = ctxt->proximityPosition;
+    OUTPUT:
+	RETVAL
+
+int
+getContextSize( self )
+        SV * self
+    INIT:
+        xmlXPathContextPtr ctxt = (xmlXPathContextPtr)SvIV(SvRV(self)); 
+        if ( ctxt == NULL ) {
+            croak("XPathContext: missing xpath context");
+        }
+    CODE:
+        RETVAL = ctxt->contextSize;
+    OUTPUT:
+	RETVAL
+
+void 
+setContextNode( self , pnode )
+        SV * self
+        SV * pnode
+    INIT:
+        xmlXPathContextPtr ctxt = (xmlXPathContextPtr)SvIV(SvRV(self)); 
+        if ( ctxt == NULL ) {
+            croak("XPathContext: missing xpath context");
+        }
+    PPCODE:
+        if (XPathContextDATA(ctxt)->node != NULL) {
+            SvREFCNT_dec(XPathContextDATA(ctxt)->node);
+        }
+        if (SvOK(pnode)) {
+            XPathContextDATA(ctxt)->node = newSVsv(pnode);
+        } else {
+            XPathContextDATA(ctxt)->node = NULL;
+        }
+
+void 
+setContextPosition( self , position )
+        SV * self
+        int position
+    INIT:
+        xmlXPathContextPtr ctxt = (xmlXPathContextPtr)SvIV(SvRV(self)); 
+        if ( ctxt == NULL )
+            croak("XPathContext: missing xpath context");
+        if ( position < -1 || position > ctxt->contextSize )
+	    croak("XPathContext: invalid position");
+    PPCODE:
+        ctxt->proximityPosition = position;
+
+void 
+setContextSize( self , size )
+        SV * self
+        int size
+    INIT:
+        xmlXPathContextPtr ctxt = (xmlXPathContextPtr)SvIV(SvRV(self)); 
+        if ( ctxt == NULL )
+            croak("XPathContext: missing xpath context");
+        if ( size < -1 )
+	    croak("XPathContext: invalid size");
+    PPCODE:
+        ctxt->contextSize = size;
+        if ( size == 0 )
+	    ctxt->proximityPosition = 0;
+	else if ( size > 0 )
+	    ctxt->proximityPosition = 1;
+        else 
+	    ctxt->proximityPosition = -1;
+
+void
+registerNs( pxpath_context, prefix, ns_uri )
+        SV * pxpath_context
+        SV * prefix
+        SV * ns_uri
+    PREINIT:
+        xmlXPathContextPtr ctxt = NULL;
+    INIT:
+        ctxt = (xmlXPathContextPtr)SvIV(SvRV(pxpath_context));
+        if ( ctxt == NULL ) {
+            croak("XPathContext: missing xpath context");
+        }
+        LibXML_configure_xpathcontext(ctxt);
+    PPCODE:
+        if(SvOK(ns_uri)) {
+	    if(xmlXPathRegisterNs(ctxt, (xmlChar *) SvPV_nolen(prefix),
+                                  (xmlChar *) SvPV_nolen(ns_uri)) == -1) {
+                croak("XPathContext: cannot register namespace");
+            }
+        } else {
+	    if(xmlXPathRegisterNs(ctxt, (xmlChar *) SvPV_nolen(prefix), NULL) == -1) {
+                croak("XPathContext: cannot unregister namespace");
+            }
+        }
+
+SV*
+lookupNs( pxpath_context, prefix )
+        SV * pxpath_context
+        SV * prefix
+    PREINIT:
+        xmlXPathContextPtr ctxt = NULL;
+    INIT:
+        ctxt = (xmlXPathContextPtr)SvIV(SvRV(pxpath_context));
+        if ( ctxt == NULL ) {
+            croak("XPathContext: missing xpath context");
+        }
+        LibXML_configure_xpathcontext(ctxt);
+    CODE:
+        RETVAL = C2Sv(xmlXPathNsLookup(ctxt, (xmlChar *) SvPV_nolen(prefix)), NULL);
+    OUTPUT:
+        RETVAL
+
+SV*
+getVarLookupData( self )
+        SV * self
+    INIT:
+        xmlXPathContextPtr ctxt = (xmlXPathContextPtr)SvIV(SvRV(self)); 
+        if ( ctxt == NULL ) {
+            croak("XPathContext: missing xpath context");
+        }
+    CODE:
+        if(XPathContextDATA(ctxt)->varData != NULL) {
+            RETVAL = newSVsv(XPathContextDATA(ctxt)->varData);
+        } else {
+            RETVAL = &PL_sv_undef;
+        }
+    OUTPUT:
+        RETVAL
+
+SV*
+getVarLookupFunc( self )
+        SV * self
+    INIT:
+        xmlXPathContextPtr ctxt = (xmlXPathContextPtr)SvIV(SvRV(self)); 
+        if ( ctxt == NULL ) {
+            croak("XPathContext: missing xpath context");
+        }
+    CODE:
+        if(XPathContextDATA(ctxt)->varData != NULL) {
+            RETVAL = newSVsv(XPathContextDATA(ctxt)->varLookup);
+        } else {
+            RETVAL = &PL_sv_undef;
+        }
+    OUTPUT:
+        RETVAL
+
+void
+registerVarLookupFunc( pxpath_context, lookup_func, lookup_data )
+        SV * pxpath_context
+        SV * lookup_func
+        SV * lookup_data
+    PREINIT:
+        xmlXPathContextPtr ctxt = NULL;
+        XPathContextDataPtr data = NULL;
+    INIT:
+        ctxt = (xmlXPathContextPtr)SvIV(SvRV(pxpath_context));
+        if ( ctxt == NULL )
+            croak("XPathContext: missing xpath context");
+        data = XPathContextDATA(ctxt);
+        if ( data == NULL )
+            croak("XPathContext: missing xpath context private data");
+        LibXML_configure_xpathcontext(ctxt);
+        /* free previous lookup function and data */
+        if (data->varLookup && SvOK(data->varLookup))
+            SvREFCNT_dec(data->varLookup);
+        if (data->varData && SvOK(data->varData))
+            SvREFCNT_dec(data->varData);
+        data->varLookup=NULL;
+        data->varData=NULL;
+    PPCODE:
+        if (SvOK(lookup_func)) {
+            if ( SvROK(lookup_func) && SvTYPE(SvRV(lookup_func)) == SVt_PVCV ) {
+		data->varLookup = newSVsv(lookup_func);
+		if (SvOK(lookup_data)) 
+		    data->varData = newSVsv(lookup_data);
+		xmlXPathRegisterVariableLookup(ctxt, 
+					       LibXML_generic_variable_lookup, ctxt);
+		if (ctxt->varLookupData==NULL || ctxt->varLookupData != ctxt) {
+		    croak( "XPathContext: registration failure" );
+		}    
+            } else {
+                croak("XPathContext: 1st argument is not a CODE reference");
+            }
+        } else {
+            /* unregister */
+            xmlXPathRegisterVariableLookup(ctxt, NULL, NULL);
+        }
+
+void
+registerFunctionNS( pxpath_context, name, uri, func)
+        SV * pxpath_context
+        char * name
+        SV * uri
+        SV * func
+    PREINIT:
+        xmlXPathContextPtr ctxt = NULL;
+        SV * pfdr;
+        SV * key;
+        STRLEN len;
+        char *strkey;
+
+    INIT:
+        ctxt = (xmlXPathContextPtr)SvIV(SvRV(pxpath_context));
+        if ( ctxt == NULL ) {
+            croak("XPathContext: missing xpath context");
+        }
+        LibXML_configure_xpathcontext(ctxt);
+        if ( !SvOK(func) || 
+             (SvOK(func) && ((SvROK(func) && SvTYPE(SvRV(func)) == SVt_PVCV )
+                || SvPOK(func)))) {
+            if (ctxt->funcLookupData == NULL) {
+                if (SvOK(func)) {
+                    pfdr = newRV_inc((SV*) newHV());
+                    ctxt->funcLookupData = pfdr;
+                } else {
+                    /* looks like no perl function was never registered, */
+                    /* nothing to unregister */
+                    warn("XPathContext: nothing to unregister");
+                    return;
+                }
+            } else {
+                if (SvTYPE(SvRV((SV *)ctxt->funcLookupData)) == SVt_PVHV) {
+                    /* good, it's a HV */
+                    pfdr = (SV *)ctxt->funcLookupData;
+                } else {
+                    croak ("XPathContext: cannot register: funcLookupData structure occupied");
+                }
+            }
+            key = newSVpvn("",0);
+            if (SvOK(uri)) {
+                sv_catpv(key, "{");
+                sv_catsv(key, uri);
+                sv_catpv(key, "}");
+            }
+            sv_catpv(key, (const char*)name);
+            strkey = SvPV(key, len);
+            /* warn("Trying to store function '%s' in %d\n", strkey, pfdr); */
+            if (SvOK(func)) {
+                hv_store((HV *)SvRV(pfdr),strkey, len, newSVsv(func), 0);
+            } else {
+                /* unregister */
+                hv_delete((HV *)SvRV(pfdr),strkey, len, G_DISCARD);
+            }
+            SvREFCNT_dec(key);
+        } else {
+            croak("XPathContext: 3rd argument is not a CODE reference or function name");
+        }
+    PPCODE:
+        if (SvOK(uri)) {
+	    xmlXPathRegisterFuncNS(ctxt, (xmlChar *) name,
+                                   (xmlChar *) SvPV(uri, len), 
+                                    (SvOK(func) ? 
+                                    LibXML_generic_extension_function : NULL));
+        } else {    
+            xmlXPathRegisterFunc(ctxt, (xmlChar *) name, 
+                                 (SvOK(func) ? 
+                                 LibXML_generic_extension_function : NULL));
+        }
+
+void
+_free_node_pool( pxpath_context )
+        SV * pxpath_context
+    PREINIT:
+        xmlXPathContextPtr ctxt = NULL;
+    INIT:
+        ctxt = (xmlXPathContextPtr)SvIV(SvRV(pxpath_context));
+        if ( ctxt == NULL ) {
+            croak("XPathContext: missing xpath context");
+        }
+    PPCODE:
+        if (XPathContextDATA(ctxt)->pool != NULL) {
+            SvREFCNT_dec((SV *)XPathContextDATA(ctxt)->pool);
+            XPathContextDATA(ctxt)->pool = NULL;
+        }
+
+void
+_findnodes( pxpath_context, perl_xpath )
+        SV * pxpath_context
+        SV * perl_xpath 
+        SV * saved_error = sv_2mortal(newSVpv("",0));
+    PREINIT:
+        xmlXPathContextPtr ctxt = NULL;
+        ProxyNodePtr owner = NULL;
+        xmlXPathObjectPtr found = NULL;
+        xmlNodeSetPtr nodelist = NULL;
+        SV * element = NULL ;
+        STRLEN len = 0 ;
+        xmlChar * xpath = NULL;
+    INIT:
+        ctxt = (xmlXPathContextPtr)SvIV(SvRV(pxpath_context));
+        if ( ctxt == NULL ) {
+            croak("XPathContext: missing xpath context");
+        }
+        LibXML_configure_xpathcontext(ctxt);
+        if ( ctxt->node == NULL ) {
+            croak("XPathContext: lost current node");
+        }
+        xpath = nodeSv2C(perl_xpath, ctxt->node);
+        if ( !(xpath && xmlStrlen(xpath)) ) {
+            if ( xpath ) 
+                xmlFree(xpath);
+            croak("XPathContext: empty XPath found");
+            XSRETURN_UNDEF;
+        }
+    PPCODE:
+        if ( ctxt->node->doc ) {
+            domNodeNormalize( xmlDocGetRootElement(ctxt->node->doc) );
+        }
+        else {
+            domNodeNormalize( PmmOWNER(PmmNewNode(ctxt->node)) );
+        }
+
+        LibXML_init_error_ctx(saved_error);
+
+        PUTBACK ;
+        found = domXPathFindCtxt( ctxt, xpath );
+        SPAGAIN ;
+
+        if (found != NULL) {
+          nodelist = found->nodesetval;  
+        } else {
+          nodelist = NULL;
+        }
+        xmlFree(xpath);
+
+        if ( nodelist ) {
+            if ( nodelist->nodeNr > 0 ) {
+                int i;
+                const char * cls = "XML::LibXML::Node";
+                xmlNodePtr tnode;
+                len = nodelist->nodeNr;
+                for( i = 0  ; i < len; i++){
+                    /* we have to create a new instance of an objectptr. 
+                     * and then place the current node into the new object. 
+                     * afterwards we can push the object to the array!
+                     */ 
+                    element = NULL;
+                    tnode = nodelist->nodeTab[i];
+                    if (tnode->type == XML_NAMESPACE_DECL) {
+                        xmlNsPtr newns = xmlCopyNamespace((xmlNsPtr)tnode);
+                        if ( newns != NULL ) {
+                            element = NEWSV(0,0);
+                            cls = PmmNodeTypeName( tnode );
+                            element = sv_setref_pv( element,
+                                                    (const char *)cls,
+                                                    newns
+                                                  );
+                        }
+                        else {
+                            continue;
+                        }
+                    }
+                    else {
+                        if (tnode->doc) {
+                            owner = PmmOWNERPO(PmmNewNode((xmlNodePtr) tnode->doc));
+                        } else {
+                            owner = NULL; /* self contained node */
+                        }
+                        element = PmmNodeToSv(tnode, owner);
+                    }
+                    XPUSHs( sv_2mortal(element) );
+                }
+            }
+            /* prevent libxml2 from freeing the actual nodes */
+            if (found->boolval) found->boolval=0;
+            xmlXPathFreeObject(found);
+	    LibXML_report_error_ctx(saved_error, 1);
+        }
+        else {
+            xmlXPathFreeObject(found);
+	    LibXML_report_error_ctx(saved_error, 0);
+        }
+
+void
+_find( pxpath_context, pxpath )
+        SV * pxpath_context
+        SV * pxpath
+    PREINIT:
+        xmlXPathContextPtr ctxt = NULL;
+        ProxyNodePtr owner = NULL;
+        xmlXPathObjectPtr found = NULL;
+        xmlNodeSetPtr nodelist = NULL;
+        SV* element = NULL ;
+        STRLEN len = 0 ;
+        xmlChar * xpath = NULL;
+        SV * saved_error = sv_2mortal(newSVpv("",0));
+    INIT:
+        ctxt = (xmlXPathContextPtr)SvIV(SvRV(pxpath_context));
+        if ( ctxt == NULL ) {
+            croak("XPathContext: missing xpath context");
+        }
+        LibXML_configure_xpathcontext(ctxt);
+        if ( ctxt->node == NULL ) {
+            croak("XPathContext: lost current node");
+        }
+        xpath = nodeSv2C(pxpath, ctxt->node);
+        if ( !(xpath && xmlStrlen(xpath)) ) {
+            if ( xpath ) 
+                xmlFree(xpath);
+            croak("XPathContext: empty XPath found");
+            XSRETURN_UNDEF;
+        }
+
+    PPCODE:
+        if ( ctxt->node->doc ) {
+            domNodeNormalize( xmlDocGetRootElement( ctxt->node->doc ) );
+        }
+        else {
+            domNodeNormalize( PmmOWNER(PmmNewNode(ctxt->node)) );
+        }
+
+        LibXML_init_error_ctx(saved_error);
+
+        PUTBACK ;
+        found = domXPathFindCtxt( ctxt, xpath );
+        SPAGAIN ;
+
+        xmlFree( xpath );
+
+        if (found) {
+            switch (found->type) {
+                case XPATH_NODESET:
+                    /* return as a NodeList */
+                    /* access ->nodesetval */
+                    XPUSHs(sv_2mortal(newSVpv("XML::LibXML::NodeList", 0)));
+                    nodelist = found->nodesetval;
+                    if ( nodelist ) {
+                        if ( nodelist->nodeNr > 0 ) {
+                            int i;
+                            const char * cls = "XML::LibXML::Node";
+                            xmlNodePtr tnode;
+                            SV * element;
+                        
+                            len = nodelist->nodeNr;
+                            for( i = 0 ; i < len; i++){
+                                /* we have to create a new instance of an
+                                 * objectptr. and then
+                                 * place the current node into the new
+                                 * object. afterwards we can
+                                 * push the object to the array!
+                                 */
+                                tnode = nodelist->nodeTab[i];
+
+                                /* let's be paranoid */
+                                if (tnode->type == XML_NAMESPACE_DECL) {
+                                     xmlNsPtr newns = xmlCopyNamespace((xmlNsPtr)tnode);
+                                    if ( newns != NULL ) {
+                                        element = NEWSV(0,0);
+                                        cls = PmmNodeTypeName( tnode );
+                                        element = sv_setref_pv( element,
+                                                                (const char *)cls,
+                                                                (void*)newns
+                                                          );
+                                    }
+                                    else {
+                                        continue;
+                                    }
+                                }
+                                else {
+                                    if (tnode->doc) {
+                                        owner = PmmOWNERPO(PmmNewNode((xmlNodePtr) tnode->doc));
+                                    } else {
+                                        owner = NULL; /* self contained node */
+                                    }
+                                    element = PmmNodeToSv(tnode, owner);
+                                }
+                                XPUSHs( sv_2mortal(element) );
+                            }
+                        }
+                    }
+                    /* prevent libxml2 from freeing the actual nodes */
+                    if (found->boolval) found->boolval=0;    
+                    break;
+                case XPATH_BOOLEAN:
+                    /* return as a Boolean */
+                    /* access ->boolval */
+                    XPUSHs(sv_2mortal(newSVpv("XML::LibXML::Boolean", 0)));
+                    XPUSHs(sv_2mortal(newSViv(found->boolval)));
+                    break;
+                case XPATH_NUMBER:
+                    /* return as a Number */
+                    /* access ->floatval */
+                    XPUSHs(sv_2mortal(newSVpv("XML::LibXML::Number", 0)));
+                    XPUSHs(sv_2mortal(newSVnv(found->floatval)));
+                    break;
+                case XPATH_STRING:
+                    /* access ->stringval */
+                    /* return as a Literal */
+                    XPUSHs(sv_2mortal(newSVpv("XML::LibXML::Literal", 0)));
+                    XPUSHs(sv_2mortal(C2Sv(found->stringval, NULL)));
+                    break;
+                default:
+                    croak("Unknown XPath return type");
+            }
+            xmlXPathFreeObject(found);
+	    LibXML_report_error_ctx(saved_error, 1);
+        }
+        else {
+	    LibXML_report_error_ctx(saved_error, 0);
+        }
 
 MODULE = XML::LibXML         PACKAGE = XML::LibXML::InputCallback
 
                                   (xmlInputCloseCallback) LibXML_input_close);
         
 
-

File MANIFEST

View file
  • Ignore whitespace
 dom.h
 xpath.c
 xpath.h
+xpathcontext.h
 perl-libxml-mm.c
 perl-libxml-mm.h
 perl-libxml-sax.c
 t/27new_callbacks_simple.t
 t/28new_callbacks_multiple.t
 t/29id.t
+t/30xpathcontext.t
+t/31xpc_functions.t
+t/32xpc_variables.t
+t/90threads.t
 lib/XML/LibXML/NodeList.pm
 lib/XML/LibXML/Literal.pm
 lib/XML/LibXML/Boolean.pm
 lib/XML/LibXML/Number.pm
 lib/XML/LibXML/SAX.pm
+lib/XML/LibXML/XPathContext.pm
 lib/XML/LibXML/SAX/Generator.pm
 lib/XML/LibXML/SAX/Builder.pm
 lib/XML/LibXML/SAX/Parser.pm

File docs/libxml.dbk

View file
  • Ignore whitespace
             </author>
         </authorgroup>
 
-        <edition>1.60</edition>
+        <edition>1.61</edition>
 
         <copyright>
             <year>2001-2006</year>
                     </listitem>
                 </varlistentry>
 
-                <varlistentry>
-                    <term>XML::LibXML::XPathContext</term>
-
-                    <listitem>
-                        <para>Advanced XPath processing using libxml2 and XML::LibXML</para>
-                    </listitem>
-                </varlistentry>
             </variablelist>
         </sect1>
 
                 <term>toStringC14N</term>
 
                 <listitem>
-                    <para><funcsynopsis><funcsynopsisinfo>$c14nstr = $doc-&#62;toStringC14N($comment_flag,$xpath); </funcsynopsisinfo></funcsynopsis>A variation
+                    <para><funcsynopsis><funcsynopsisinfo>$c14nstr = $doc-&#62;toStringC14N($comment_flag,$xpath); </funcsynopsisinfo></funcsynopsis> A variation
                     to toString, that returns the canonized form of the given document.</para>
                 </listitem>
             </varlistentry>
           <para>There are several possible ways to deal with namespaces in XPath:
 	  </para>
 	  <itemizedlist>
-	    <listitem><para>If the document declares a prefix for the
+	    <listitem>
+	      <para>
+		The recommended way is to use the
+		<function>XML::LibXML::XPathContext</function> module
+		to define an explicit context
+		for XPath evaluation, in which a document independent
+		prefix-to-namespace mapping can be defined. For
+		example:
+	      </para>
+	      <programlisting>my $xpc = XML::LibXML::XPathContext->new;
+$xpc->registerNs('x', 'http://www.w3.org/1999/xhtml');
+$xpc->find('/x:html',$node);</programlisting>
+	    </listitem>
+	    <listitem><para>
+		Another possibility is to use prefixes declared
+		in the queried document (if known). 
+		If the document declares a prefix for the
 		namespace in question (and the context node is in the
 		scope of the declaration),
 		<function>XML::LibXML</function> allows you to use the
 		prefix in the XPath expression, e.g.:
 	      </para>
 	      <programlisting>$node->find('/x:html');</programlisting>
-	      <para>
-		Some people use this feature and add a namespace
-		declaration to the DOM manually with e.g.:
-	      </para>
-	      <programlisting>$doc->getDocumentElement->setNamespace('http://www.w3.org/1999/xhtml','x',0);</programlisting>
-	      <para>
-		While this works just fine in most cases, it should be
-		noted that namespace declarations added in this way
-		will appear on the root element in an XML
-		serialization of the document, which may in some cases
-		be inconvenient. More importantly, one has to be
-		careful not to choose a prefix that is already used
-		for a different namespace in the document.
-	      </para>
-	    </listitem>
-	    <listitem>
-	      <para>
-		A cleaner way, which is independent of the queried
-		document, is to use the
-		<function>XML::LibXML::XPathContext</function> package
-		(distributed separately) to create an explicit context
-		for XPath evaluation, in which a document independent
-		prefix-to-namespace mapping can be defined. For
-		example:
-	      </para>
-	      <programlisting>use XML::LibXML::XPathContext;
-my $xpc = XML::LibXML::XPathContext->new;
-$xpc->registerNs('x', 'http://www.w3.org/1999/xhtml');
-$xpc->find('/x:html',$node);</programlisting>
-	      <para>
-		Apart from namespace registration,
-		<function>XML::LibXML::XPathContext</function>
-		provides several other nice features, such as use of
-		variables within XPath and the possibility to define custom XPath
-		functions written in Perl.
-	      </para>
-	    </listitem>
-	    <listitem>
-	      <para>A fallback solution wich is also independent of
-		the queried document is to rewrite all node tests in the
-		XPath expression to an explicit form:
-	      </para>
-	      <programlisting>$node->find('/*[local-name()="html" and namespace-uri="http://www.w3.org/1999/xhtml"]');</programlisting>
 	    </listitem>
 	  </itemizedlist>
+	  <para>See also XML::LibXML::XPathContext->findnodes.</para>
                 </listitem>
             </varlistentry>
         </variablelist>
                     <function>XML::LibXML::Number</function> object being returned. Other expressions might return a <function>XML::LibXML::Boolean</function>
                     object, or a <function>XML::LibXML::Literal</function> object (a string). Each of those objects uses Perl&#39;s overload feature to &#34;do
                     the right thing&#34; in different contexts.</para>
+	  <para>See also XML::LibXML::XPathContext->find.</para>
                 </listitem>
             </varlistentry>
         </variablelist>
 
                     <para>That is, it returns the literal value of the results. This enables you to ensure that you get a string back from your search, allowing
                     certain shortcuts. This could be used as the equivalent of XSLT&#39;s &#60;xsl:value-of select=&#34;some_xpath&#34;/&#62;.</para>
+	  <para>See also XML::LibXML::XPathContext->findvalue.</para>
                 </listitem>
             </varlistentry>
         </variablelist>
             </varlistentry>
         </variablelist>
     </chapter>
+  <chapter>
+    <title>XPath Evaluation</title>
+    <titleabbrev>XML::LibXML::XPathContext</titleabbrev>
+    <para>
+      The XML::LibXML::XPathContext
+      class provides an almost complete
+      interface to libxml2's XPath implementation.
+      With XML::LibXML::XPathContext is is possible to
+      evaluate XPath expressions in the context
+      of arbitrary node, context size, and context position,
+      with a user-defined namespace-prefix mapping,
+      custom XPath functions written in Perl, and
+      even a custom XPath variable resolver.
+    </para>
+    <sect1>
+      <title>Examples</title>
+      <sect2>
+        <title>Namespaces</title>
+<para>This example demonstrates <function>registerNs()</function> method.
+It finds all paragraph nodes in an XHTML document.</para>
+          <programlisting>my $xc = XML::LibXML::XPathContext-&gt;new($xhtml_doc);
+$xc-&gt;registerNs('xhtml', 'http://www.w3.org/1999/xhtml');
+my @nodes = $xc-&gt;findnodes('//xhtml:p');</programlisting>
+      </sect2>
+      <sect2>
+        <title>Custom XPath functions</title>
+<para>This example demonstrates <function>registerFunction()</function> method
+by defining a function filtering nodes based on a Perl regular expression:</para>
+	<programlisting>sub grep_nodes { 
+  my ($nodelist,$regexp) =  @_;
+  my $result = XML::LibXML::NodeList-&gt;new;
+  for my $node ($nodelist->get_nodelist()) {
+    $result-&gt;push($node) if $node-&gt;textContent =~ $regexp;
+  }
+  return $result;
+};
+
+my $xc = XML::LibXML::XPathContext-&gt;new($node);
+$xc-&gt;registerFunction('grep_nodes', \&amp;grep_nodes);
+my @nodes = $xc-&gt;findnodes('//section[grep_nodes(para,"\bsearch(ing|es)?\b")]');</programlisting>
+      </sect2>
+      <sect2>
+        <title>Variables</title>
+<para>This example demonstrates <function>registerVarLookup()</function>
+method. We use XPath variables to recycle results of previous evaluations:</para>
+	<programlisting>sub var_lookup {
+  my ($varname,$ns,$data)=@_;
+  return $data-&gt;{$varname};
+}
+        
+my $areas = XML::LibXML-&gt;new-&gt;parse_file('areas.xml');
+my $empl = XML::LibXML-&gt;new-&gt;parse_file('employees.xml');
+        
+my $xc = XML::LibXML::XPathContext-&gt;new($empl);
+        
+my %variables = ( 
+  A =&gt; $xc-&gt;find('/employees/employee[@salary&gt;10000]'),
+  B =&gt; $areas-&gt;find('/areas/area[district='Brooklyn']/street'), 
+);
+        
+# get names of employees from $A woring in an area listed in $B
+$xc-&gt;registerVarLookupFunc(\&amp;var_lookup, \%variables);
+my @nodes = $xc-&gt;findnodes('$A[work_area/street = $B]/name');
+</programlisting>
+      </sect2>
+    </sect1>
+    <sect1>
+      <title>Methods</title>
+      <variablelist>
+        <varlistentry>
+          <term>new</term>
+          <listitem>
+	    <funcsynopsis><funcsynopsisinfo>my $xpc = XML::LibXML::XPathContext-&gt;new();</funcsynopsisinfo></funcsynopsis>
+            <para>Creates a new XML::LibXML::XPathContext object
+            without a context node.</para>
+	    <funcsynopsis><funcsynopsisinfo>my $xpc = XML::LibXML::XPathContext-&gt;new($node);</funcsynopsisinfo></funcsynopsis>
+            <para>Creates a new XML::LibXML::XPathContext object with
+            the context node set to <literal>$node</literal>.</para>
+          </listitem>
+        </varlistentry>
+        <varlistentry>
+          <term>registerNs</term>
+          <listitem><funcsynopsis><funcsynopsisinfo>$xpc-&gt;registerNs($prefix, $namespace_uri)</funcsynopsisinfo></funcsynopsis>
+            <para>Registers namespace <literal>$prefix</literal> to
+            <literal>$namespace_uri</literal>.</para>
+          </listitem>
+        </varlistentry>
+        <varlistentry>
+          <term>unregisterNs</term>
+          <listitem><funcsynopsis><funcsynopsisinfo>$xpc-&gt;unregisterNs($prefix)</funcsynopsisinfo></funcsynopsis>
+            <para>Unregisters namespace <literal>$prefix</literal>.</para>
+          </listitem>
+        </varlistentry>
+        <varlistentry>
+          <term>lookupNs</term>
+          <listitem><funcsynopsis><funcsynopsisinfo>$uri = $xpc-&gt;lookupNs($prefix)</funcsynopsisinfo></funcsynopsis>
+            <para>Returns namespace URI registered with
+            <literal>$prefix</literal>. If <literal>$prefix</literal>
+            is not registered to any namespace URI returns
+            <literal>undef</literal>.</para>
+          </listitem>
+        </varlistentry>
+        <varlistentry>
+          <term>registerVarLookupFunc</term>
+          <listitem><funcsynopsis><funcsynopsisinfo>$xpc-&gt;registerVarLookupFunc($callback, $data)</funcsynopsisinfo></funcsynopsis>
+            <para>Registers variable lookup function
+	      <literal>$prefix</literal>. The registered function is
+	      executed by the XPath engine each time an XPath variable
+	      is evaluated. It takes three arguments:
+	      <literal>$data</literal>, variable name, and variable
+	      ns-URI and must return one value: a number or string or
+	      any <literal>XML::LibXML::</literal> object that can be a result
+	      of findnodes: Boolean, Literal, Number, Node
+	      (e.g. Document, Element, etc.), or NodeList. For
+	      convenience, simple (non-blessed) array references
+	      containing only <literal>XML::LibXML::Node</literal> objects can be
+	      used instead of a <literal>XML::LibXML::NodeList</literal>.</para>
+          </listitem>
+        </varlistentry>
+        <varlistentry>
+          <term>getVarLookupData</term>
+          <listitem>
+	    <funcsynopsis><funcsynopsisinfo>$data = $xpc-&gt;getVarLookupData();</funcsynopsisinfo></funcsynopsis>
+            <para>
+	      Returns the data that have been associated with a
+	      variable lookup function during a previous call to
+	      <literal>registerVarLookupFunc</literal>.</para>
+          </listitem>
+        </varlistentry>
+        <varlistentry>
+          <term>getVarLookupFunc</term>
+          <listitem>
+	    <funcsynopsis><funcsynopsisinfo>$callback = $xpc-&gt;getVarLookupFunc();</funcsynopsisinfo></funcsynopsis>
+            <para>
+	      Returns the variable lookup function previously registered with
+	      <literal>registerVarLookupFunc</literal>.</para>
+          </listitem>
+        </varlistentry>
+        <varlistentry>
+          <term>unregisterVarLookupFunc</term>
+          <listitem>
+	    <funcsynopsis><funcsynopsisinfo>$xpc-&gt;unregisterVarLookupFunc($name);</funcsynopsisinfo></funcsynopsis>
+            <para>Unregisters variable lookup function and the associated lookup data.</para>
+          </listitem>
+        </varlistentry>
+        <varlistentry>
+          <term>registerFunctionNS</term>
+          <listitem><funcsynopsis><funcsynopsisinfo>$xpc-&gt;registerFunctionNS($name, $uri, $callback)</funcsynopsisinfo></funcsynopsis>
+            <para>Registers an extension function
+	      <literal>$name</literal> in <literal>$uri</literal>
+	      namespace. <literal>$callback</literal> must be a CODE
+	      reference. The arguments of the callback function are
+	      either simple scalars or <literal>XML::LibXML::*</literal> objects
+	      depending on the XPath argument types. The function is
+	      responsible for checking the argument number and
+	      types. Result of the callback code must be a single
+	      value of the following types: a simple scalar
+	      (number, string) or an arbitrary <literal>XML::LibXML::*</literal>
+	      object that can be a result of findnodes: Boolean,
+	      Literal, Number, Node (e.g. Document, Element, etc.), or
+	      NodeList. For convenience, simple (non-blessed) array
+	      references containing only <literal>XML::LibXML::Node</literal>
+	      objects can be used instead of a
+	      <literal>XML::LibXML::NodeList</literal>.</para>
+          </listitem>
+        </varlistentry>
+        <varlistentry>
+          <term>unregisterFunctionNS</term>
+          <listitem><funcsynopsis><funcsynopsisinfo>$xpc-&gt;unregisterFunctionNS($name, $uri)</funcsynopsisinfo></funcsynopsis>
+            <para>
+	      Unregisters extension function <literal>$name</literal>
+	      in <literal>$uri</literal> namespace. Has the same
+	      effect as passing <literal>undef</literal> as
+	      <literal>$callback</literal> to
+	      registerFunctionNS.</para>
+          </listitem>
+        </varlistentry>
+        <varlistentry>
+          <term>registerFunction</term>
+          <listitem><funcsynopsis><funcsynopsisinfo>$xpc-&gt;registerFunction($name, $callback)</funcsynopsisinfo></funcsynopsis>
+            <para>Same as <literal>registerFunctionNS</literal> but
+            without a namespace.</para>
+          </listitem>
+        </varlistentry>
+        <varlistentry>
+          <term>unregisterFunction</term>
+          <listitem><funcsynopsis><funcsynopsisinfo>$xpc-&gt;unregisterFunction($name)</funcsynopsisinfo></funcsynopsis>
+            <para>Same as <literal>unregisterFunctionNS</literal> but
+	      without a namespace.</para>
+          </listitem>
+        </varlistentry>
+        <varlistentry>
+          <term>findnodes</term>
+          <listitem><funcsynopsis><funcsynopsisinfo>@nodes = $xpc-&gt;findnodes($xpath)</funcsynopsisinfo></funcsynopsis>
+          <funcsynopsis><funcsynopsisinfo>@nodes = $xpc-&gt;findnodes($xpath, $context_node )</funcsynopsisinfo></funcsynopsis>
+          <funcsynopsis><funcsynopsisinfo>$nodelist = $xpc-&gt;findnodes($xpath, $context_node )</funcsynopsisinfo></funcsynopsis>
+            <para>Performs the xpath statement on the current node and
+	      returns the result as an array. In scalar context
+	      returns a <literal>XML::LibXML::NodeList</literal> object. Optionally, a
+	      node may be passed as a second argument to set the
+	      context node for the query.</para>
+          </listitem>
+        </varlistentry>
+        <varlistentry>
+          <term>find</term>
+          <listitem><funcsynopsis><funcsynopsisinfo>$object = $xpc-&gt;find($xpath )</funcsynopsisinfo></funcsynopsis>
+          <funcsynopsis><funcsynopsisinfo>$object = $xpc-&gt;find($xpath, $context_node )</funcsynopsisinfo></funcsynopsis>
+            <para>Performs the xpath expression using the current node
+	      as the context of the expression, and returns the result
+	      depending on what type of result the XPath expression
+	      had. For example, the XPath <literal>1 * 3 +
+	      52</literal> results in a <literal>XML::LibXML::Number</literal> object
+	      being returned. Other expressions might return a
+	      <literal>XML::LibXML::Boolean</literal> object, or a
+	      <literal>XML::LibXML::Literal</literal> object (a string). Each of those
+	      objects uses Perl's overload feature to ``do the right
+	      thing'' in different contexts. Optionally, a node may be
+	      passed as a second argument to set the context node for
+	      the query.</para>
+          </listitem>
+        </varlistentry>
+        <varlistentry>
+          <term>findvalue</term>
+          <listitem><funcsynopsis><funcsynopsisinfo>$value = $xpc-&gt;findvalue($xpath )</funcsynopsisinfo></funcsynopsis>
+          <funcsynopsis><funcsynopsisinfo>$value = $xpc-&gt;findvalue($xpath, $context_node )</funcsynopsisinfo></funcsynopsis>
+            <para>Is exactly equivalent to:</para>
+	    <programlisting>$node-&gt;find( $xpath )-&gt;to_literal;</programlisting>
+            <para>That is, it returns the literal value of the
+            results. This enables you to ensure that you get a string
+            back from your search, allowing certain shortcuts. This
+            could be used as the equivalent of &lt;xsl:value-of
+            select=``some_xpath''/&gt;. Optionally, a node may be
+            passed in the second argument to set the context node for
+            the query.</para>
+          </listitem>
+        </varlistentry>
+        <varlistentry>
+          <term>setContextNode</term>
+          <listitem><funcsynopsis><funcsynopsisinfo>$xpc-&gt;setContextNode($node)</funcsynopsisinfo></funcsynopsis>
+            <para>Set the current context node.</para>
+          </listitem>
+        </varlistentry>
+        <varlistentry>
+          <term>getContextNode</term>
+          <listitem>
+	    <funcsynopsis><funcsynopsisinfo>my $node = $xpc-&gt;getContextNode;</funcsynopsisinfo></funcsynopsis>
+            <para>Get the current context node.</para>
+          </listitem>
+        </varlistentry>
+        <varlistentry>
+          <term>setContextPosition</term>
+          <listitem><funcsynopsis><funcsynopsisinfo>$xpc-&gt;setContextPosition($position)</funcsynopsisinfo></funcsynopsis>
+            <para>
+	      Set the current context position. By default, this
+	      value is -1 (and evaluating XPath function
+	      <literal>position()</literal> in the initial context
+	      raises an XPath error), but can be set to any value up
+	      to context size. This usually only serves to cheat the
+	      XPath engine to return given position when
+	      <literal>position()</literal> XPath function is
+	      called. Setting this value to -1 restores the default
+	      behavior.</para>
+          </listitem>
+        </varlistentry>
+        <varlistentry>
+          <term>getContextPosition</term>
+          <listitem>
+	    <funcsynopsis><funcsynopsisinfo>my $position = $xpc-&gt;getContextPosition;</funcsynopsisinfo></funcsynopsis>
+            <para>Get the current context position.</para>
+          </listitem>
+        </varlistentry>
+        <varlistentry>
+          <term>setContextSize</term>
+          <listitem><funcsynopsis><funcsynopsisinfo>$xpc-&gt;setContextSize($size)</funcsynopsisinfo></funcsynopsis>
+            <para>
+	      Set the current context size. By default, this value is -1 (and
+	      evaluating XPath function <literal>last()</literal> in
+	      the initial context raises an XPath error), but can be
+	      set to any non-negative value. This usually only serves
+	      to cheat the XPath engine to return the given value when
+	      <literal>last()</literal> XPath function is called. If
+	      context size is set to 0, position is automatically also
+	      set to 0. If context size is positive, position is
+	      automatically set to 1. Setting context size to -1
+	      restores the default behavior.</para>
+          </listitem>
+        </varlistentry>
+        <varlistentry>
+          <term>getContextSize</term>
+          <listitem>
+	    <funcsynopsis><funcsynopsisinfo>my $size = $xpc-&gt;getContextSize;</funcsynopsisinfo></funcsynopsis>
+            <para>Get the current context size.</para>
+          </listitem>
+        </varlistentry>
+        <varlistentry>
+          <term>setContextNode</term>
+          <listitem><funcsynopsis><funcsynopsisinfo>$xpc-&gt;setContextNode($node)</funcsynopsisinfo></funcsynopsis>
+            <para>Set the current context node.</para>
+          </listitem>
+        </varlistentry>
+      </variablelist>
+    </sect1>
+    <sect1>
+      <title>Bugs And Caveats</title>
+      <para>
+	XML::LibXML::XPathContext objects
+	<emphasis>are</emphasis> reentrant, meaning that you can call
+	methods of an XML::LibXML::XPathContext even from XPath
+	extension functions registered with the same object or from a
+	variable lookup function. On the other hand, you should rather
+	avoid registering new extension functions, namespaces and a
+	variable lookup function from within extension functions and a
+	variable lookup function, unless you want to experience
+	untested behavior.
+      </para>
+    </sect1>
+    <sect1>
+      <title>Authors</title>
+      <para>Ilya Martynov and Petr Pajas, based on 
+	XML::LibXML and XML::LibXSLT code by Matt Sergeant and 
+	Christian Glahn.</para> 
+    </sect1>
+    <sect1>
+      <title>Historical remark</title>
+      <para>Prior to XML::LibXML 1.61 this module was distributed separately
+	for maintanance reasons.
+      </para>
+    </sect1>
+  </chapter>
+
+
 </book>

File t/30threads.t

  • Ignore whitespace
-use Test;
-use Config;
-use constant MAX_THREADS => 10;
-BEGIN {
-	if( $Config{useithreads} ) {
-		plan tests => 14;
-		require threads;
-	} else {
-		plan tests => 0;
-		exit;
-	}
-}
-use XML::LibXML;
-ok(1);
-
-my $p = XML::LibXML->new();
-ok($p);
-
-# Simple spawn threads with $p in scope
-{
-for(1..MAX_THREADS)
-{
-	threads->new(sub {});
-}
-$_->join for(threads->list);
-ok(1);
-}
-
-my $xml = <<EOF;
-<?xml version="1.0" encoding="utf-8"?>
-<root><node><leaf/></node></root>
-EOF
-
-# Parse a correct XML document
-{
-for(1..MAX_THREADS)
-{
-	threads->new(sub { $p->parse_string($xml) for 1..100; 1; });
-}
-$_->join for(threads->list);
-ok(1);
-}
-
-my $xml_bad = <<EOF;
-<?xml version="1.0" encoding="utf-8"?>
-<root><node><leaf/></root>
-EOF
-
-# Parse a bad XML document
-{
-for(1..MAX_THREADS)
-{
-	threads->new(sub { eval { my $x = $p->parse_string($xml_bad)} for(1..100); 1; });
-}
-$_->join for(threads->list);
-ok(1);
-}
-
-
-my $xml_invalid = <<EOF;
-<?xml version="1.0" encoding="utf-8"?>
-<!DOCTYPE root [
-<!ELEMENT root EMPTY>
-]>
-<root><something/></root>
-EOF
-
-# Parse an invalid XML document
-{
-for(1..MAX_THREADS)
-{
-  threads->new(sub {
-		 for (1..100) {
-		   my $x = $p->parse_string($xml_invalid); 
-		   die if $x->is_valid;
-		   eval { $x->validate };
-		   die unless $@;
-		 }
-               1;
-	       });
-}
-$_->join for(threads->list);
-ok(1);
-}
-
-my $rngschema = <<EOF;
-<?xml version="1.0"?>
-<r:grammar xmlns:r="http://relaxng.org/ns/structure/1.0">
-  <r:start>
-    <r:element name="root">
-      <r:attribute name="id"/>
-    </r:element>
-  </r:start>
-</r:grammar>
-EOF
-
-# test RNG validation errors are thread safe
-{
-for(1..MAX_THREADS)
-{
-  threads->new(
-    sub {
-      for (1..100) {
-	my $x = $p->parse_string($xml);
-	eval { XML::LibXML::RelaxNG->new( string => $rngschema )->validate( $x ) };
-	die unless $@;
-      }; 1;
-    });
-}
-$_->join for(threads->list);
-ok(1);
-}
-
-my $xsdschema = <<EOF;
-<?xml version="1.0"?>
-<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
-  <xsd:element name="root">
-    <xsd:attribute name="partNum" type="SKU" use="required"/>
-  </xsd:element>
-</xsd:schema>
-EOF
-
-# test Schema validation errors are thread safe
-{
-for(1..MAX_THREADS)
-{
-  threads->new(
-    sub {
-      for (1..100) {
- 	my $x = $p->parse_string($xml);
- 	eval { XML::LibXML::Schema->new( string => $xsdschema )->validate( $x ) };
- 	die unless $@;
-      }; 1;
-    });
-}
-$_->join for(threads->list);
-ok(1);
-}
-
-my $bigfile = "example/libxml.dkb";
-open my $fh, "<:utf8", $bigfile or die $!;
-$xml = join '', <$fh>;
-close $fh;
-ok($xml);
-
-sub use_dom
-{
-	my $d = shift;
-	my @nodes = $d->getElementsByTagName("title",1);
-	for(@nodes)
-	{
-		my $title = $_->toString;
-	}
-	die unless $nodes[0]->toString eq '<title>XML::LibXML</title>';
-}
-
-{
-for(1..1) #MAX_THREADS)
-{
-	threads->new(sub { use_dom($p->parse_string($xml)) for 1..5; 1; });
-}
-$_->join for(threads->list);
-ok(1);
-}
-
-{
-package MyHandler;
-
-use base XML::SAX::Base;
-
-sub AUTOLOAD
-{
-}
-}
-
-use XML::LibXML::SAX;
-$p = XML::LibXML::SAX->new(
-	Handler=>MyHandler->new(),
-);
-ok($p);
-
-{
-for(1..MAX_THREADS)
-{
-	threads->new(sub { $p->parse_string($xml) for 1..10; 1; });
-}
-$_->join for(threads->list);
-ok(1);
-}
-
-$p = XML::LibXML->new(
-	Handler=>MyHandler->new(),
-);
-$p->parse_chunk($xml);
-$p->parse_chunk("",1);
-
-{
-for(1..MAX_THREADS)
-{
-	threads->new(sub {
-$p = XML::LibXML->new();
-$p->parse_chunk($xml);
-use_dom($p->parse_chunk("",1));
-1;
-});
-}
-$_->join for(threads->list);
-ok(1);
-}
-
-$p = XML::LibXML->new();
-
-{
-for(1..MAX_THREADS)
-{
-	threads->new(sub {
-open my $fh, "<$bigfile";
-$p->parse_fh($fh);
-close $fh;
-1;
-});
-}
-$_->join for(threads->list);
-ok(1);
-}
-
-

File t/30xpathcontext.t

View file
  • Ignore whitespace
+use Test;
+BEGIN { plan tests => 54 };
+
+use XML::LibXML;
+use XML::LibXML::XPathContext;
+
+my $doc = XML::LibXML->new->parse_string(<<'XML');
+<foo><bar a="b"></bar></foo>
+XML
+
+# test findnodes() in list context
+my @nodes = XML::LibXML::XPathContext->new($doc)->findnodes('/*');
+ok(@nodes == 1);
+ok($nodes[0]->nodeName eq 'foo');
+ok((XML::LibXML::XPathContext->new($nodes[0])->findnodes('bar'))[0]->nodeName
+   eq 'bar');
+
+# test findnodes() in scalar context
+my $nl = XML::LibXML::XPathContext->new($doc)->findnodes('/*');
+ok($nl->pop->nodeName eq 'foo');
+ok(!defined($nl->pop));
+
+# test findvalue()
+ok(XML::LibXML::XPathContext->new($doc)->findvalue('1+1') == 2);
+ok(XML::LibXML::XPathContext->new($doc)->findvalue('1=2') eq 'false');
+
+# test find()
+ok(XML::LibXML::XPathContext->new($doc)->find('/foo/bar')->pop->nodeName
+   eq 'bar');
+ok(XML::LibXML::XPathContext->new($doc)->find('1*3')->value == '3');
+ok(XML::LibXML::XPathContext->new($doc)->find('1=1')->to_literal eq 'true');
+
+my $doc1 = XML::LibXML->new->parse_string(<<'XML');
+<foo xmlns="http://example.com/foobar"><bar a="b"></bar></foo>
+XML
+
+# test registerNs()
+my $xc = XML::LibXML::XPathContext->new($doc1);
+$xc->registerNs('xxx', 'http://example.com/foobar');
+ok($xc->findnodes('/xxx:foo')->pop->nodeName eq 'foo');
+ok($xc->lookupNs('xxx') eq 'http://example.com/foobar');
+
+# test unregisterNs()
+$xc->unregisterNs('xxx');
+eval { $xc->findnodes('/xxx:foo') };
+ok($@);
+ok(!defined($xc->lookupNs('xxx')));
+
+# test getContextNode and setContextNode
+ok($xc->getContextNode->isSameNode($doc1));
+$xc->setContextNode($doc1->getDocumentElement);
+ok($xc->getContextNode->isSameNode($doc1->getDocumentElement));
+ok($xc->findnodes('.')->pop->isSameNode($doc1->getDocumentElement));
+
+# test xpath context preserves the document
+my $xc2 = XML::LibXML::XPathContext->new(
+	  XML::LibXML->new->parse_string(<<'XML'));
+<foo/>
+XML
+ok($xc2->findnodes('*')->pop->nodeName eq 'foo');
+
+# test xpath context preserves context node
+my $doc2 = XML::LibXML->new->parse_string(<<'XML');
+<foo><bar/></foo>
+XML
+my $xc3 = XML::LibXML::XPathContext->new($doc2->getDocumentElement);
+$xc3->find('/');
+ok($xc3->getContextNode->toString() eq '<foo><bar/></foo>');
+
+# check starting with empty context
+my $xc4 = XML::LibXML::XPathContext->new();
+ok(!defined($xc4->getContextNode));
+eval { $xc4->find('/') };
+ok($@);
+my $cn=$doc2->getDocumentElement;
+$xc4->setContextNode($cn);
+ok($xc4->find('/'));
+ok($xc4->getContextNode->isSameNode($doc2->getDocumentElement));
+$cn=undef;
+ok($xc4->getContextNode);
+ok($xc4->getContextNode->isSameNode($doc2->getDocumentElement));
+
+# check temporarily changed context node
+my ($bar)=$xc4->findnodes('foo/bar',$doc2);
+ok($bar->nodeName eq 'bar');
+ok($xc4->getContextNode->isSameNode($doc2->getDocumentElement));
+
+ok($xc4->findnodes('parent::*',$bar)->pop->nodeName eq 'foo');
+ok($xc4->getContextNode->isSameNode($doc2->getDocumentElement));
+
+# testcase for segfault found by Steve Hay
+my $xc5 = XML::LibXML::XPathContext->new();
+$xc5->registerNs('pfx', 'http://www.foo.com');
+$doc = XML::LibXML->new->parse_string('<foo xmlns="http://www.foo.com" />');
+$xc5->setContextNode($doc);
+$xc5->findnodes('/');
+$xc5->setContextNode(undef);
+$xc5->getContextNode();
+$xc5->setContextNode($doc);
+$xc5->findnodes('/');
+ok(1);
+
+# check setting context position and size
+ok($xc4->getContextPosition() == -1);
+ok($xc4->getContextSize() == -1);
+eval { $xc4->setContextPosition(4); };
+ok($@);
+eval { $xc4->setContextPosition(-4); };
+ok($@);
+eval { $xc4->setContextSize(-4); };
+ok($@);
+eval { $xc4->findvalue('position()') };
+ok($@);
+eval { $xc4->findvalue('last()') };
+ok($@);
+
+$xc4->setContextSize(0);
+ok($xc4->getContextSize() == 0);
+ok($xc4->getContextPosition() == 0);
+ok($xc4->findvalue('position()')==0);
+ok($xc4->findvalue('last()')==0);
+
+$xc4->setContextSize(4);
+ok($xc4->getContextSize() == 4);
+ok($xc4->getContextPosition() == 1);
+ok($xc4->findvalue('last()')==4);
+ok($xc4->findvalue('position()')==1);
+eval { $xc4->setContextPosition(5); };
+ok($@);
+ok($xc4->findvalue('position()')==1);
+ok($xc4->getContextSize() == 4);
+$xc4->setContextPosition(4);
+ok($xc4->findvalue('position()')==4);
+ok($xc4->findvalue('position()=last()'));
+
+$xc4->setContextSize(-1);
+ok($xc4->getContextPosition() == -1);
+ok($xc4->getContextSize() == -1);
+eval { $xc4->findvalue('position()') };
+ok($@);
+eval { $xc4->findvalue('last()') };
+ok($@);
+
+

File t/31xpc_functions.t

View file
  • Ignore whitespace
+# -*- cperl -*-
+use Test;
+BEGIN { plan tests => 32 };
+
+use XML::LibXML;
+use XML::LibXML::XPathContext;
+
+my $doc = XML::LibXML->new->parse_string(<<'XML');
+<foo><bar a="b">Bla</bar><bar/></foo>
+XML
+ok($doc);
+
+my $xc = XML::LibXML::XPathContext->new($doc);
+$xc->registerNs('foo','urn:foo');
+
+$xc->registerFunctionNS('copy','urn:foo',
+			sub { @_==1 ? $_[0] : die "too many parameters"}
+		       );
+
+# copy string, real, integer, nodelist
+ok($xc->findvalue('foo:copy("bar")') eq 'bar');
+ok($xc->findvalue('foo:copy(3.14)') < 3.141); # can't use == here because of
+ok($xc->findvalue('foo:copy(3.14)') > 3.139); # float math
+ok($xc->findvalue('foo:copy(7)') == 7);
+ok($xc->find('foo:copy(//*)')->size() == 3);
+my ($foo)=$xc->findnodes('(//*)[2]');
+ok($xc->findnodes('foo:copy(//*)[2]')->pop->isSameNode($foo));
+
+# too many arguments
+eval { $xc->findvalue('foo:copy(1,xyz)') };
+ok ($@);
+
+# without a namespace
+$xc->registerFunction('dummy', sub { 'DUMMY' });
+ok($xc->findvalue('dummy()') eq 'DUMMY');
+
+# unregister it
+$xc->unregisterFunction('dummy');
+eval { $xc->findvalue('dummy()') };
+ok ($@);
+
+# retister by name
+sub dummy2 { 'DUMMY2' };
+$xc->registerFunction('dummy2', 'dummy2');
+ok($xc->findvalue('dummy2()') eq 'DUMMY2');
+
+# unregister
+$xc->unregisterFunction('dummy2');
+eval { $xc->findvalue('dummy2()') };
+ok ($@);
+
+
+# a mix of different arguments types
+$xc->registerFunction('join',
+    sub { join shift,
+          map { (ref($_)&&$_->isa('XML::LibXML::Node')) ? $_->nodeName : $_ }
+          map { (ref($_)&&$_->isa('XML::LibXML::NodeList')) ? @$_ : $_ }
+	  @_
+	});
+
+ok($xc->findvalue('join("","a","b","c")') eq 'abc');
+ok($xc->findvalue('join("-","a",/foo,//*)') eq 'a-foo-foo-bar-bar');
+ok($xc->findvalue('join("-",foo:copy(//*))') eq 'foo-bar-bar');
+
+# unregister foo:copy
+$xc->unregisterFunctionNS('copy','urn:foo');
+eval { $xc->findvalue('foo:copy("bar")') };
+ok ($@);
+
+# test context reentrance
+$xc->registerFunction('test-lock1', sub { $xc->find('string(//node())') });
+$xc->registerFunction('test-lock2', sub { $xc->findnodes('//bar') });
+ok($xc->find('test-lock1()') eq $xc->find('string(//node())'));
+ok($xc->find('count(//bar)=2'));
+ok($xc->find('count(test-lock2())=count(//bar)'));
+ok($xc->find('count(test-lock2()|//bar)=count(//bar)'));
+ok($xc->findnodes('test-lock2()[2]')->pop()->isSameNode($xc->findnodes('//bar[2]')));
+
+$xc->registerFunction('test-lock3', sub { $xc->findnodes('test-lock2(//bar)') });
+ok($xc->find('count(test-lock2())=count(test-lock3())'));
+ok($xc->find('count(test-lock3())=count(//bar)'));
+ok($xc->find('count(test-lock3()|//bar)=count(//bar)'));
+
+# function creating new nodes
+$xc->registerFunction('new-foo',
+		      sub {
+			return $doc->createElement('foo');
+		      });
+ok($xc->findnodes('new-foo()')->pop()->nodeName eq 'foo');
+my ($test_node) = $xc->findnodes('new-foo()');
+
+$xc->registerFunction('new-chunk',
+		      sub {
+			XML::LibXML->new->parse_string('<x><y><a/><a/></y><y><a/></y></x>')->find('//a')
+		      });
+ok($xc->findnodes('new-chunk()')->size() == 3);
+my ($x)=$xc->findnodes('new-chunk()/parent::*');
+ok($x->nodeName() eq 'y');
+ok($xc->findvalue('name(new-chunk()/parent::*)') eq 'y');
+ok($xc->findvalue('count(new-chunk()/parent::*)=2'));
+
+my $largedoc=XML::LibXML->new->parse_string('<a>'.('<b/>' x 3000).'</a>');
+$xc->setContextNode($largedoc);
+$xc->registerFunction('pass1',
+			sub {
+			  [$largedoc->findnodes('(//*)')]
+			});
+$xc->registerFunction('pass2',sub { $_[0] } );
+$xc->registerVarLookupFunc( sub { [$largedoc->findnodes('(//*)')] }, undef);
+$largedoc->toString();
+
+ok($xc->find('$a[name()="b"]')->size()==3000);
+my @pass1=$xc->findnodes('pass1()');
+ok(@pass1==3001);
+ok($xc->find('pass2(//*)')->size()==3001);

File t/32xpc_variables.t

View file
  • Ignore whitespace
+# -*- cperl -*-
+use Test;
+BEGIN { plan tests => 35 };
+
+use XML::LibXML;
+use XML::LibXML::XPathContext;
+
+my $doc = XML::LibXML->new->parse_string(<<'XML');
+<foo><bar a="b">Bla</bar><bar/></foo>
+XML
+
+my %variables = (
+	'a' => XML::LibXML::Number->new(2),
+	'b' => "b",
+	);
+
+sub get_variable {
+  my ($data, $name, $uri)=@_;
+  return exists($data->{$name}) ? $data->{$name} : undef;
+}
+
+# $c: nodelist
+$variables{c} = XML::LibXML::XPathContext->new($doc)->findnodes('//bar');
+ok($variables{c}->isa('XML::LibXML::NodeList'));
+ok($variables{c}->size() == 2);
+ok($variables{c}->get_node(1)->nodeName eq 'bar');
+
+# $d: a single element node
+$variables{d} = XML::LibXML::XPathContext->new($doc)->findnodes('/*')->pop;
+ok($variables{d}->nodeName() eq 'foo');
+
+# $e: a single text node
+$variables{e} = XML::LibXML::XPathContext->new($doc)->findnodes('//text()');
+ok($variables{e}->get_node(1)->data() eq 'Bla');
+
+# $f: a single attribute node
+$variables{f} = XML::LibXML::XPathContext->new($doc)->findnodes('//@*')->pop;
+ok($variables{f}->nodeName() eq 'a');
+ok($variables{f}->value() eq 'b');
+
+# $f: a single document node
+$variables{g} = XML::LibXML::XPathContext->new($doc)->findnodes('/')->pop;
+ok($variables{g}->nodeType() == XML::LibXML::XML_DOCUMENT_NODE);
+
+# test registerVarLookupFunc() and getVarLookupData()
+my $xc = XML::LibXML::XPathContext->new($doc);
+ok(!defined($xc->getVarLookupData));
+$xc->registerVarLookupFunc(\&get_variable,\%variables);
+ok(defined($xc->getVarLookupData));
+my $h1=$xc->getVarLookupData;
+my $h2=\%variables;
+ok("$h1" eq "$h2" );
+ok($h1 eq $xc->getVarLookupData);
+ok(\&get_variable eq $xc->getVarLookupFunc);
+
+# test values returned by XPath queries
+ok($xc->find('$a') == 2);
+ok($xc->find('$b') eq "b");
+ok($xc->findnodes('//@a[.=$b]')->size() == 1);
+ok($xc->findnodes('//@a[.=$b]')->size() == 1);
+ok($xc->findnodes('$c')->size() == 2);
+ok($xc->findnodes('$c')->size() == 2);
+ok($xc->findnodes('$c[1]')->pop->isSameNode($variables{c}->get_node(1)));
+ok($xc->findnodes('$c[@a="b"]')->size() == 1);
+ok($xc->findnodes('$d')->size() == 1);
+ok($xc->findnodes('$d/*')->size() == 2);
+ok($xc->findnodes('$d')->pop->isSameNode($variables{d}));
+ok($xc->findvalue('$e') eq 'Bla');
+ok($xc->findnodes('$e')->pop->isSameNode($variables{e}->get_node(1)));
+ok($xc->findnodes('$c[@*=$f]')->size() == 1);
+ok($xc->findvalue('$f') eq 'b');
+ok($xc->findnodes('$f')->pop->nodeName eq 'a');
+ok($xc->findnodes('$f')->pop->isSameNode($variables{f}));
+ok($xc->findnodes('$g')->pop->isSameNode($variables{g}));
+
+# unregiser variable lookup
+$xc->unregisterVarLookupFunc();
+eval { $xc->find('$a') };
+ok($@);
+ok(!defined($xc->getVarLookupFunc()));
+
+my $foo='foo';