Here's an implementation of {http://exslt.org/dynamic}map for libexslt.  It
seems like a goofy extension but can be very useful, as in when working with
multiple documents where the key() limited me.

The attached samples are really silly and not a good test but shows it works at
least.
Index: dynamic.c
===================================================================
RCS file: /cvs/gnome/libxslt/libexslt/dynamic.c,v
retrieving revision 1.3
diff -c -r1.3 dynamic.c
*** dynamic.c   18 Aug 2003 22:41:05 -0000      1.3
--- dynamic.c   8 Jul 2005 17:51:03 -0000
***************
*** 84,89 ****
--- 84,229 ----
  }
  
  /**
+  * exsltDynMapFunction:
+  * @ctxt:  an XPath parser context
+  * @nargs:  the number of arguments
+  *
+  * Evaluates the string as an XPath expression and returns the result
+  * value, which may be a boolean, number, string, node set, result tree
+  * fragment or external object.
+  */
+ 
+ static void
+ exsltDynMapFunction(xmlXPathParserContextPtr ctxt, int nargs) {
+       xmlChar *str = NULL;
+       xmlNodeSetPtr nodeset = NULL;
+       xmlXPathCompExprPtr comp = NULL;
+       xmlXPathObjectPtr ret = NULL;
+       xmlDocPtr oldDoc, container;
+       xmlNodePtr oldNode; 
+       int oldContextSize;
+       int oldProximityPosition;
+       int i, j;
+ 
+ 
+     if (nargs != 2) {
+               xmlXPathSetArityError(ctxt);
+               return;
+     }
+       str = xmlXPathPopString(ctxt);
+     if (xmlXPathCheckError(ctxt)) {
+               xmlXPathSetTypeError(ctxt);
+               return;
+     }
+ 
+     nodeset = xmlXPathPopNodeSet(ctxt);
+     if (xmlXPathCheckError(ctxt)) {
+               xmlXPathSetTypeError(ctxt);
+               return;
+     }
+       if (str == NULL || !xmlStrlen(str) || !(comp = xmlXPathCompile(str))) {
+               if (nodeset != NULL)
+                       xmlXPathFreeNodeSet(nodeset);
+               if (str != NULL)
+                       xmlFree(str);
+               valuePush(ctxt, xmlXPathNewNodeSet(NULL));
+               return;
+       }
+ 
+       ret = xmlXPathNewNodeSet(NULL);
+       if (ret == NULL) {
+               xsltGenericError(xsltGenericErrorContext,
+                        "exsltDynMapFunctoin: ret == NULL\n");
+               goto cleanup;
+       }
+ 
+       oldDoc = ctxt->context->doc;
+       oldNode = ctxt->context->node;
+       oldContextSize = ctxt->context->contextSize;
+       oldProximityPosition = ctxt->context->proximityPosition;
+ 
+       /** 
+        * since we really don't know we're going to be adding node(s) 
+        * down the road we create the RVT regardless 
+        */
+     container = xsltCreateRVT(xsltXPathGetTransformContext(ctxt));
+     if (container != NULL) 
+         xsltRegisterTmpRVT(xsltXPathGetTransformContext(ctxt), container);
+ 
+       if (nodeset && nodeset->nodeNr > 0 ) {
+               xmlXPathNodeSetSort(nodeset);
+               ctxt->context->contextSize = nodeset->nodeNr;
+               ctxt->context->proximityPosition = 0;
+               for (i = 0; i < nodeset->nodeNr; i++) {
+                       xmlXPathObjectPtr subResult = NULL;
+                       ctxt->context->proximityPosition++;
+                       ctxt->context->node = nodeset->nodeTab[i];
+                       ctxt->context->doc = nodeset->nodeTab[i]->doc;
+ 
+                       subResult = xmlXPathCompiledEval(comp, ctxt->context);
+                       if (subResult != NULL) {
+                               switch (subResult->type) {
+                                       case XPATH_NODESET:
+                                               if (subResult->nodesetval != 
NULL)
+                                                       for (j = 0; j < 
subResult->nodesetval->nodeNr; j++)
+                                                               
xmlXPathNodeSetAdd(ret->nodesetval, subResult->nodesetval->nodeTab[j]);
+                                               break;
+                                       case XPATH_BOOLEAN:
+                                               if (container != NULL) {
+                                                       xmlNodePtr cur = 
xmlNewChild((xmlNodePtr) container, NULL, BAD_CAST "boolean", BAD_CAST 
subResult->boolval ? "true" : "");
+                                                       if (cur != NULL)  {
+                                                               cur->ns = 
xmlNewNs(cur, BAD_CAST "http://exslt.org/common";, BAD_CAST "exsl");
+                                                               
xmlXPathNodeSetAddUnique(ret->nodesetval, cur);
+                                                       }
+                                               }
+                                               break;
+                                       case XPATH_NUMBER:
+                                               if (container != NULL) {
+                                                       xmlChar *val = 
xmlXPathCastNumberToString(subResult->floatval);
+                                                       xmlNodePtr cur = 
xmlNewChild((xmlNodePtr) container, NULL, BAD_CAST "number", val);
+                                                       if (val != NULL)
+                                                               xmlFree(val);
+ 
+                                                       if (cur != NULL)  {
+                                                               cur->ns = 
xmlNewNs(cur, BAD_CAST "http://exslt.org/common";, BAD_CAST "exsl");
+                                                               
xmlXPathNodeSetAddUnique(ret->nodesetval, cur);
+                                                       }
+                                               }
+                                               break;
+                                       case XPATH_STRING:
+                                               if (container != NULL) {
+                                                       xmlNodePtr cur = 
xmlNewChild((xmlNodePtr) container, NULL, BAD_CAST "string", 
subResult->stringval);
+                                                       if (cur != NULL)  {
+                                                               cur->ns = 
xmlNewNs(cur, BAD_CAST "http://exslt.org/common";, BAD_CAST "exsl");
+                                                               
xmlXPathNodeSetAddUnique(ret->nodesetval, cur);
+                                                       }
+                                               }
+                                               break;
+                               }
+                               xmlXPathFreeObject(subResult);
+                       }
+               }
+       }
+       ctxt->context->doc = oldDoc;
+       ctxt->context->node = oldNode;
+       ctxt->context->contextSize = oldContextSize;
+       ctxt->context->proximityPosition = oldProximityPosition;
+ 
+ 
+ cleanup:
+       /* restore the xpath context */
+       if (comp != NULL)
+               xmlXPathFreeCompExpr(comp);
+       if (nodeset != NULL)
+               xmlXPathFreeNodeSet(nodeset);
+       if (str != NULL)
+               xmlFree(str);
+       valuePush(ctxt, ret);
+       return;
+ }
+ 
+ 
+ /**
   * exsltDynRegister:
   *
   * Registers the EXSLT - Dynamic module
***************
*** 94,97 ****
--- 234,241 ----
      xsltRegisterExtModuleFunction ((const xmlChar *) "evaluate",
                                   EXSLT_DYNAMIC_NAMESPACE,
                                   exsltDynEvaluateFunction);
+   xsltRegisterExtModuleFunction ((const xmlChar *) "map",
+                                  EXSLT_DYNAMIC_NAMESPACE,
+                                  exsltDynMapFunction);
+ 
  }
<dynmap>
 <with-child>
  <childNode1>aaa</childNode1>
  <childNode2>bbbb</childNode2>
  <childNode3>ccc</childNode3>
  <childNode4>dddd</childNode4>
  <childNode5>eee</childNode5>
  <childNode6>ffff</childNode6>
  <childNode7>ggg</childNode7>
  <childNode8>hhhh</childNode8>
  <childNode9>iii</childNode9>
 </with-child>
 <without-child>some text</without-child>
 <without-child>some text</without-child>
 <without-child>some text</without-child>
 <without-child>some text</without-child>
 <without-child>some text</without-child>
 <with-child>
  <childNode1>jjjj</childNode1>
  <childNode2>kkk</childNode2>
  <childNode3>llll</childNode3>
  <childNode4>mmm</childNode4>
  <childNode5>nnnn</childNode5>
  <childNode6>ooo</childNode6>
  <childNode7>pppp</childNode7>
  <childNode8>qqq</childNode8>
  <childNode9>rrrr</childNode9>
 </with-child>
</dynmap>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"; version="1.0"
 xmlns:dyn="http://exslt.org/dynamic";
 exclude-result-prefixes="dyn"
>
<xsl:output indent="yes"/>

<xsl:template match="/dynmap">
<result>
 <node-set>
  <xsl:copy-of select="dyn:map(*, 'child::*[string-length(.) &gt; 3]')"/>
 </node-set> 
 <boolean>
  <xsl:copy-of select="dyn:map(*, 'string-length(name()) &gt; 3')"/>
 </boolean>
 <number>
  <xsl:copy-of select="dyn:map(*, 'count(*)')"/>
 </number>
 <string>
  <xsl:copy-of select="dyn:map(*, 'name()')"/>
 </string>  
</result> 
</xsl:template>
</xsl:stylesheet>

_______________________________________________
xslt mailing list, project page http://xmlsoft.org/XSLT/
[email protected]
http://mail.gnome.org/mailman/listinfo/xslt

Reply via email to