Next tiny step of refactoring - mostly bug fixes and cosmetic changes.
authorKasimier T. Buchcik <kbuchcik@src.gnome.org>
Mon, 22 May 2006 08:56:42 +0000 (08:56 +0000)
committerKasimier T. Buchcik <kbuchcik@src.gnome.org>
Mon, 22 May 2006 08:56:42 +0000 (08:56 +0000)
* libxslt/attributes.c libxslt/documents.c
  libxslt/extensions.c libxslt/keys.c libxslt/pattern.c
  libxslt/preproc.c libxslt/templates.c
  libxslt/transform.c libxslt/variables.c
  libxslt/xslt.c libxslt/xsltInternals.h:
  Next tiny step of refactoring - mostly bug fixes and
  cosmetic changes.
  Changes outside of the refactored code:
  1) Optimized xsl:attribute if the content consists of
   just 1 text node.
  2) Optimized computation of xsl:key. The keys will now be
   computed for a specific document not until the first call
   of a key() function; here only the keys with the specific
   name used by key() are computed. This means that this
   now avoids computation of all keys for all loaded
   input documents (even if no key() was called on them).
   One exception is the scenario where a key() is used in
   a template's match pattern; in this case all keys are
   computed for a document if there's a chance that
   a "keyed" template could match a node (this could still
   be optimized a bit).

12 files changed:
ChangeLog
libxslt/attributes.c
libxslt/documents.c
libxslt/extensions.c
libxslt/keys.c
libxslt/pattern.c
libxslt/preproc.c
libxslt/templates.c
libxslt/transform.c
libxslt/variables.c
libxslt/xslt.c
libxslt/xsltInternals.h

index 6f13041..52f1eab 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,27 @@
+Mon May 22 10:32:57 CEST 2006 Kasimier Buchcik <libxml2-cvs@cazic.net>
+
+       * libxslt/attributes.c libxslt/documents.c
+         libxslt/extensions.c libxslt/keys.c libxslt/pattern.c
+         libxslt/preproc.c libxslt/templates.c
+         libxslt/transform.c libxslt/variables.c
+         libxslt/xslt.c libxslt/xsltInternals.h:
+         Next tiny step of refactoring - mostly bug fixes and
+         cosmetic changes.
+         Changes outside of the refactored code:
+         1) Optimized xsl:attribute if the content consists of
+          just 1 text node.
+         2) Optimized computation of xsl:key. The keys will now be
+          computed for a specific document not until the first call
+          of a key() function; here only the keys with the specific
+          name used by key() are computed. This means that this
+          now avoids computation of all keys for all loaded
+          input documents (even if no key() was called on them).
+          One exception is the scenario where a key() is used in
+          a template's match pattern; in this case all keys are
+          computed for a document if there's a chance that
+          a "keyed" template could match a node (this could still
+          be optimized a bit).
+
 Mon May 15 22:32:13 CEST 2006 Kasimier Buchcik <libxml2-cvs@cazic.net>
 
        * libxslt/namespaces.c libxslt/attributes.c:
index d72f41d..4815b41 100644 (file)
@@ -38,6 +38,7 @@
 #include <libxml/hash.h>
 #include <libxml/xmlerror.h>
 #include <libxml/uri.h>
+#include <libxml/parserInternals.h>
 #include "xslt.h"
 #include "xsltInternals.h"
 #include "xsltutils.h"
@@ -64,6 +65,9 @@
 /*
  * Useful macros
  */
+#ifdef IS_BLANK
+#undef IS_BLANK
+#endif
 
 #define IS_BLANK(c) (((c) == 0x20) || ((c) == 0x09) || ((c) == 0xA) || \
                      ((c) == 0x0D))
@@ -626,7 +630,7 @@ xsltResolveStylesheetAttributeSet(xsltStylesheetPtr style) {
  * xsltAttributeInternal:
  * @ctxt:  a XSLT process context
  * @node:  the node in the source tree.
- * @inst:  the xslt attribute node
+ * @inst:  the xsl:attribute element
  * @comp:  precomputed information
  * @fromset:  the attribute comes from an attribute-set
  *
@@ -749,26 +753,81 @@ xsltAttributeInternal(xsltTransformContextPtr ctxt, xmlNodePtr node,
        if (attr != NULL)
            return;
     }
-    value = xsltEvalTemplateString(ctxt, node, inst); /* OPTIMIZE TODO: expensive! */
-    if (value == NULL) {
-        if (ns) {
-            attr = xmlSetNsProp(ctxt->insert, ns, name,
-                                (const xmlChar *) "");
-        } else {
-            attr =
-                xmlSetProp(ctxt->insert, name, (const xmlChar *) "");
-        }
+    
+    if (inst->children == NULL) {
+       /*
+       * No content.
+       */
+       attr = xmlSetNsProp(ctxt->insert, ns, name, (const xmlChar *) "");
+    } else if ((inst->children->next == NULL) && 
+           ((inst->children->type == XML_TEXT_NODE) ||
+            (inst->children->type == XML_CDATA_SECTION_NODE)))
+    {
+       xmlNodePtr origTxt = inst->children, copyTxt;
+       /*
+       * Optimization: if the content is just 1 text node, then
+       * just need to copy it over, or just assign it to the result
+       * if the string is shared.
+       * NOTE that xmlSetNsProp() will query if this is an ID attribute :-/
+       */
+       attr = xmlSetNsProp(ctxt->insert, ns, name, NULL);
+       if (attr == NULL) /* TODO: report error */
+           goto internal_err;
+       /*
+       * This was taken over from xsltCopyText() (transform.c).
+       */
+       if (ctxt->internalized &&
+           (ctxt->insert->doc != NULL) &&
+           (ctxt->insert->doc->dict == ctxt->dict))
+       {
+           copyTxt = xmlNewText(NULL);
+           if (copyTxt == NULL) /* TODO: report error */
+               goto internal_err;
+           /*
+           * This is a safe scenario where we don't need to lookup
+           * the dict.
+           */
+           copyTxt->content = origTxt->content;
+           /*
+           * Copy "disable-output-escaping" information.
+           */
+           if (origTxt->name == xmlStringTextNoenc)
+               copyTxt->name = xmlStringTextNoenc;                             
+       } else {
+           /*
+           * Copy the value.
+           */
+           copyTxt = xmlNewText(origTxt->content);
+           if (copyTxt == NULL) /* TODO: report error */
+               goto internal_err;
+           if (origTxt->name == xmlStringTextNoenc)
+               copyTxt->name = xmlStringTextNoenc;             
+       }
+       if (copyTxt != NULL) {      
+           copyTxt->doc = attr->doc;
+           xmlAddChild((xmlNodePtr) attr, copyTxt);        
+       }
     } else {
-        if (ns) {
-            attr = xmlSetNsProp(ctxt->insert, ns, name, value);
-        } else {
-            attr = xmlSetProp(ctxt->insert, name, value);
-        }
+       /*
+       * The sequence constructor might be complex, so instantiate it.
+       */
+       value = xsltEvalTemplateString(ctxt, node, inst);
+       if (value != NULL) {
+           attr = xmlSetNsProp(ctxt->insert, ns, name, value);
+           xmlFree(value);
+       } else {
+           /*
+           * TODO: Do we have to add the empty string to the attr?
+           */
+           attr = xmlSetNsProp(ctxt->insert, ns, name,
+               (const xmlChar *) "");      
+           
+       }
     }
 
 error:
-    if (value != NULL)
-        xmlFree(value);
+internal_err:
+    return;    
 }
 
 /**
index f762185..75fd90d 100644 (file)
@@ -153,11 +153,24 @@ xsltNewDocument(xsltTransformContextPtr ctxt, xmlDocPtr doc) {
     memset(cur, 0, sizeof(xsltDocument));
     cur->doc = doc;
     if (ctxt != NULL) {
-        if (!xmlStrEqual((xmlChar *)doc->name, BAD_CAST " fake node libxslt")) {
+        if (! XSLT_IS_RES_TREE_FRAG(doc)) {
            cur->next = ctxt->docList;
            ctxt->docList = cur;
        }
+#ifdef XSLT_REFACTORED_KEYCOMP
+       /*
+       * A key with a specific name for a specific document
+       * will only be computed if there's a call to the key()
+       * function using that specific name for that specific
+       * document. I.e. computation of keys will be done in
+       * xsltGetKey() (keys.c) on an on-demand basis.
+       */
+#else
+       /*
+       * Old behaviour.
+       */
        xsltInitCtxtKeys(ctxt, cur);
+#endif
     }
     return(cur);
 }
index 0a7bc72..b28ad6b 100644 (file)
@@ -494,8 +494,7 @@ xsltRegisterExtPrefix(xsltStylesheetPtr style,
 
 #ifdef WITH_XSLT_DEBUG_EXTENSIONS
     xsltGenericDebug(xsltGenericDebugContext,
-                     "Registering extension prefix %s : %s\n", prefix,
-                     URI);
+       "Registering extension namespace '%s'.\n", URI);
 #endif
     def = (xsltExtDefPtr) style->nsDefs;
 #ifdef XSLT_REFACTORED
index c03d172..220b741 100644 (file)
 #define WITH_XSLT_DEBUG_KEYS
 #endif
 
-typedef struct _xsltKeyDef xsltKeyDef;
-typedef xsltKeyDef *xsltKeyDefPtr;
-struct _xsltKeyDef {
-    struct _xsltKeyDef *next;
-    xmlNodePtr inst;
-    xmlChar *name;
-    xmlChar *nameURI;
-    xmlChar *match;
-    xmlChar *use;
-    xmlXPathCompExprPtr comp;
-    xmlXPathCompExprPtr usecomp;
-    xmlNsPtr *nsList;           /* the namespaces in scope */
-    int nsNr;                   /* the number of namespaces in scope */
-};
-
-typedef struct _xsltKeyTable xsltKeyTable;
-typedef xsltKeyTable *xsltKeyTablePtr;
-struct _xsltKeyTable {
-    struct _xsltKeyTable *next;
-    xmlChar *name;
-    xmlChar *nameURI;
-    xmlHashTablePtr keys;
-};
-
 
 /************************************************************************
  *                                                                     *
@@ -415,6 +391,9 @@ xsltGetKey(xsltTransformContextPtr ctxt, const xmlChar *name,
           const xmlChar *nameURI, const xmlChar *value) {
     xmlNodeSetPtr ret;
     xsltKeyTablePtr table;
+#ifdef XSLT_REFACTORED_KEYCOMP
+    int found = 0;
+#endif
 
     if ((ctxt == NULL) || (name == NULL) || (value == NULL) ||
        (ctxt->document == NULL))
@@ -426,15 +405,62 @@ xsltGetKey(xsltTransformContextPtr ctxt, const xmlChar *name,
 #endif
     table = (xsltKeyTablePtr) ctxt->document->keys;
     while (table != NULL) {
-       if (xmlStrEqual(table->name, name) &&
-           (((nameURI == NULL) && (table->nameURI == NULL)) ||
-            ((nameURI != NULL) && (table->nameURI != NULL) &&
-             (xmlStrEqual(table->nameURI, nameURI))))) {
+       if (((nameURI != NULL) == (table->nameURI != NULL)) &&
+           xmlStrEqual(table->name, name) &&
+           xmlStrEqual(table->nameURI, nameURI))
+       {
+#ifdef XSLT_REFACTORED_KEYCOMP
+           found = 1;
+#endif
            ret = (xmlNodeSetPtr)xmlHashLookup(table->keys, value);
            return(ret);
        }
        table = table->next;
     }
+#ifdef XSLT_REFACTORED_KEYCOMP
+    if (! found) {
+       xsltStylesheetPtr style = ctxt->style;  
+       xsltKeyDefPtr keyd;
+       /*
+       * This might be the first call to the key with the specified
+       * name and the specified document.
+       * Find all keys with a matching name and compute them for the
+       * current tree.
+       */
+       found = 0;
+       while (style != NULL) {
+           keyd = (xsltKeyDefPtr) style->keys;
+           while (keyd != NULL) {
+               if (((nameURI != NULL) == (keyd->nameURI != NULL)) &&
+                   xmlStrEqual(keyd->name, name) &&
+                   xmlStrEqual(keyd->nameURI, nameURI))
+               {
+                   found = 1;
+                   xsltInitCtxtKey(ctxt, ctxt->document, keyd);
+               }
+               keyd = keyd->next;              
+           }       
+           style = xsltNextImport(style);
+       }
+       if (found) {
+           /*
+           * The key was computed, so look it up.
+           */
+           table = (xsltKeyTablePtr) ctxt->document->keys;
+           while (table != NULL) {
+               if (((nameURI != NULL) == (table->nameURI != NULL)) &&
+                   xmlStrEqual(table->name, name) &&
+                   xmlStrEqual(table->nameURI, nameURI))
+               {
+                   ret = (xmlNodeSetPtr)xmlHashLookup(table->keys, value);
+                   return(ret);
+               }
+               table = table->next;
+           }
+
+       }
+    }
+#endif
     return(NULL);
 }
 
@@ -521,6 +547,7 @@ xsltEvalXPathKeys(xsltTransformContextPtr ctxt, xmlXPathCompExprPtr comp,
     ctxt->xpathCtxt->namespaces = oldNamespaces;
     return(ret);
 }
+
 /**
  * xsltInitCtxtKey:
  * @ctxt: an XSLT transformation context
@@ -529,7 +556,7 @@ xsltEvalXPathKeys(xsltTransformContextPtr ctxt, xmlXPathCompExprPtr comp,
  *
  * Computes the key tables this key and for the current input document.
  */
-static void
+int
 xsltInitCtxtKey(xsltTransformContextPtr ctxt, xsltDocumentPtr doc,
                xsltKeyDefPtr keyd) {
     int i;
@@ -545,6 +572,7 @@ xsltInitCtxtKey(xsltTransformContextPtr ctxt, xsltDocumentPtr doc,
     int oldNsNr;
     xmlNsPtr *oldNamespaces;
 
+    doc->nbKeysComputed++;
     /*
      * Evaluate the nodelist
      */
@@ -700,6 +728,7 @@ error:
     ctxt->node = oldNode;
     if (res != NULL)
        xmlXPathFreeObject(res);
+    return(0);
 }
 
 /**
@@ -709,6 +738,7 @@ error:
  *
  * Computes all the keys tables for the current input document.
  * Should be done before global varibales are initialized.
+ * NOTE: Not used anymore in the refactored code.
  */
 void
 xsltInitCtxtKeys(xsltTransformContextPtr ctxt, xsltDocumentPtr doc) {
index 6c72499..4fd593a 100644 (file)
@@ -466,11 +466,7 @@ xsltTestCompMatchDirect(xsltTransformContextPtr ctxt, xsltCompMatchPtr comp,
     int isRVT;
 
     doc = node->doc;
-    if ((doc != NULL) &&
-       (doc->name != NULL) &&
-       (doc->name[0] == ' ') &&
-       (xmlStrEqual(BAD_CAST doc->name,
-                    BAD_CAST " fake node libxslt")))
+    if (XSLT_IS_RES_TREE_FRAG(doc))
        isRVT = 1;
     else
        isRVT = 0;
@@ -825,11 +821,7 @@ restart:
                }
 
                doc = node->doc;
-               if ((doc != NULL) &&
-                   (doc->name != NULL) &&
-                   (doc->name[0] == ' ') &&
-                   (xmlStrEqual(BAD_CAST doc->name,
-                                BAD_CAST " fake node libxslt")))
+               if (XSLT_IS_RES_TREE_FRAG(doc))
                    isRVT = 1;
                else
                    isRVT = 0;
@@ -1424,7 +1416,7 @@ xsltCompileIdKeyPattern(xsltParserContextPtr ctxt, xmlChar *name,
            return;
        }
        NEXT;
-       /* TODO: support namespace in keys */
+       /* URGENT TODO: support namespace in keys */
        PUSH(XSLT_OP_KEY, lit, lit2, novar);
     } else if (xmlStrEqual(name, (const xmlChar *)"processing-instruction")) {
        NEXT;
@@ -2217,6 +2209,71 @@ xsltAddTemplate(xsltStylesheetPtr style, xsltTemplatePtr cur,
     return(0);
 }
 
+#ifdef XSLT_REFACTORED_KEYCOMP
+static int
+xsltComputeAllKeys(xsltTransformContextPtr ctxt,
+               xsltDocumentPtr document)
+{
+    xsltStylesheetPtr style, style2;
+    xsltKeyDefPtr keyd, keyd2;
+    xsltKeyTablePtr table;
+
+    if ((ctxt == NULL) || (document == NULL))
+       return(-1);
+    if (document->nbKeysComputed == ctxt->nbKeys)
+       return(0);
+    /*
+    * TODO: This could be further optimized
+    */
+    style = ctxt->style;
+    while (style) {
+       keyd = (xsltKeyDefPtr) style->keys;
+       while (keyd != NULL) {      
+           /*
+           * Check if keys with this QName have been already
+           * computed.
+           */
+           table = (xsltKeyTablePtr) document->keys;
+           while (table) {
+               if (((keyd->nameURI != NULL) == (table->nameURI != NULL)) &&
+                   xmlStrEqual(keyd->name, table->name) &&
+                   xmlStrEqual(keyd->nameURI, table->nameURI))
+               {
+                   break;
+               }               
+               table = table->next;
+           }
+           if (table == NULL) {
+               /*
+               * Keys with this QName have not been yet computed.
+               */
+               style2 = ctxt->style;
+               while (style2 != NULL) {
+                   keyd2 = (xsltKeyDefPtr) style2->keys;
+                   while (keyd2 != NULL) {
+                       if (((keyd2->nameURI != NULL) ==
+                            (keyd->nameURI != NULL)) &&
+                           xmlStrEqual(keyd2->name, keyd->name) &&
+                           xmlStrEqual(keyd2->nameURI, keyd->nameURI))
+                       {
+                           xsltInitCtxtKey(ctxt, document, keyd2);
+                           if (document->nbKeysComputed == ctxt->nbKeys)
+                               return(0);
+                       }
+                       keyd2 = keyd2->next;            
+                   }       
+                   style2 = xsltNextImport(style2);
+               }
+           }       
+           keyd = keyd->next;
+       }
+       style = xsltNextImport(style);
+    }
+    return(0);
+}
+#endif
+
 /**
  * xsltGetTemplate:
  * @ctxt:  a XSLT process context
@@ -2403,6 +2460,9 @@ xsltGetTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node,
            }
        }
 
+#ifdef XSLT_REFACTORED_KEYCOMP
+keyed_match:
+#endif
        if (keyed) {
            list = curstyle->keyMatch;
            while ((list != NULL) &&
@@ -2416,6 +2476,41 @@ xsltGetTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node,
                list = list->next;
            }
        }
+#ifdef XSLT_REFACTORED_KEYCOMP 
+       else if (ctxt->hasTemplKeyPatterns &&
+           (ctxt->document->nbKeysComputed < ctxt->nbKeys))
+       {
+           /*
+           * Compute all remaining keys for this document.
+           *
+           * REVISIT TODO: I think this could be further optimized.        
+           */
+           xsltComputeAllKeys(ctxt, ctxt->document);
+
+           switch (node->type) {
+               case XML_ELEMENT_NODE:              
+                   if (node->psvi != NULL) keyed = 1;
+                   break;
+               case XML_ATTRIBUTE_NODE:
+                   if (((xmlAttrPtr) node)->psvi != NULL) keyed = 1;
+                   break;
+               case XML_TEXT_NODE:
+               case XML_CDATA_SECTION_NODE:
+               case XML_COMMENT_NODE:
+               case XML_PI_NODE:               
+                   if (node->psvi != NULL) keyed = 1;
+                   break;
+               case XML_DOCUMENT_NODE:
+               case XML_HTML_DOCUMENT_NODE:
+                   if (((xmlDocPtr) node)->psvi != NULL) keyed = 1;
+                   break;              
+               default:
+                   break;
+           }
+           if (keyed)
+               goto keyed_match;
+       }
+#endif /* XSLT_REFACTORED_KEYCOMP */
        if (ret != NULL)
            return(ret);
 
index 6e8bf03..c6c020b 100644 (file)
@@ -370,20 +370,6 @@ xsltNewStylePreComp(xsltStylesheetPtr style, xsltStyleType type) {
     return(cur);
 }
 
-#ifdef XSLT_REFACTORED
-static void
-xsltLREEffectiveNsNodesFree(xsltEffectiveNsPtr first)
-{
-    xsltEffectiveNsPtr tmp;
-
-    while (first != NULL) {
-       tmp = first;
-       first = first->next;
-       xmlFree(tmp);
-    }
-}
-#endif
-
 /**
  * xsltFreeStylePreComp:
  * @comp:  an XSLT Style precomputed block
@@ -399,12 +385,7 @@ xsltFreeStylePreComp(xsltStylePreCompPtr comp) {
     * URGENT TODO: Implement destructors.
     */
     switch (comp->type) {
-       case XSLT_FUNC_LITERAL_RESULT_ELEMENT: {
-               xsltStyleItemLRElementInfoPtr item =
-                   (xsltStyleItemLRElementInfoPtr) comp;
-               if (item->effectiveNs)
-                   xsltLREEffectiveNsNodesFree(item->effectiveNs);
-           }
+       case XSLT_FUNC_LITERAL_RESULT_ELEMENT:
            break;
        case XSLT_FUNC_COPY:
             break;
index 780b80a..d6dc728 100644 (file)
@@ -202,6 +202,12 @@ xsltEvalTemplateString(xsltTransformContextPtr ctxt, xmlNodePtr node,
     if (parent->children == NULL)
        return(NULL);
 
+    /*
+    * This creates a temporary element-node to add the resulting
+    * text content to.
+    * OPTIMIZE TODO: Keep such an element-node in the transformation
+    *  context to avoid creating it every time.
+    */
     insert = xmlNewDocNode(ctxt->output, NULL,
                           (const xmlChar *)"fake", NULL);
     if (insert == NULL) {
@@ -211,7 +217,7 @@ xsltEvalTemplateString(xsltTransformContextPtr ctxt, xmlNodePtr node,
     }
     oldInsert = ctxt->insert;
     ctxt->insert = insert;
-
+    /* OPTIMIZE TODO: if parent->children consists only of text-nodes. */
     xsltApplyOneTemplate(ctxt, node, parent->children, NULL, NULL);
 
     ctxt->insert = oldInsert;
index 0c94128..0f0fa8a 100644 (file)
@@ -709,7 +709,7 @@ xsltCopyText(xsltTransformContextPtr ctxt, xmlNodePtr target,
         ((target->ns != NULL) &&
          (xmlHashLookup2(ctxt->style->cdataSection,
                          target->name, target->ns->href) != NULL)))) {
-       /*
+       /* OPTIMIZE TODO: xsltCopyText() is also used for attribute content. 
         * nodes which must be output as CDATA due to the stylesheet
         */
        copy = xmlNewCDataBlock(ctxt->output, cur->content,
@@ -731,7 +731,7 @@ xsltCopyText(xsltTransformContextPtr ctxt, xmlNodePtr target,
            return NULL;
        if (cur->name == xmlStringTextNoenc)
            copy->name = xmlStringTextNoenc;
-       /*
+       /* OPTIMIZE TODO: get rid of xmlDictOwns() in safe cases; e.g. attribute values don't need the lookup
         * Must confirm that content is in dict
         * (bug 302821)
         */
@@ -1046,9 +1046,8 @@ xsltCopyTree(xsltTransformContextPtr ctxt, xmlNodePtr node,
         case XML_XINCLUDE_START:
         case XML_XINCLUDE_END:
             return(NULL);
-    }
-    if ((node->name != NULL) && (node->name[0] == ' ') &&
-       (xmlStrEqual(node->name, (const xmlChar *) " fake node libxslt"))) {
+    }    
+    if (XSLT_IS_RES_TREE_FRAG(node)) {
        if (node->children != NULL)
            copy = xsltCopyTreeList(ctxt, node->children, insert, 0);
        else
@@ -1482,8 +1481,96 @@ xsltProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node,
     }
 }
 
+#ifdef XSLT_REFACTORED
+/**
+* xsltTransLREUndeclareDefaultNs:
+* @ctxt:  the transformation context
+* @cur:  the literal result element
+* @ns:  the namespace
+* @out:  the output node (or its parent)
+*
+*
+* Find a matching (prefix and ns-name) ns-declaration
+*  for the given @ns in the result tree.
+* If none is found then a new ns-declaration will be
+*  added to @out. If, in this case, the given prefix is already
+*  in use, then a ns-declaration with a modified ns-prefix
+*  be we created.
+*
+* Returns the acquired ns-declaration
+*         or NULL in case of an API or internal error.
+*/
+static int
+xsltTransLREUndeclareResultDefaultNs(xsltTransformContextPtr ctxt,
+                                    xmlNodePtr cur,
+                                    xmlNodePtr resultElem)
+{
+    xmlNsPtr ns;
+    /*
+    * OPTIMIZE TODO: This all could be optimized by keeping track of
+    *  the ns-decls currently in-scope via a specialized context.
+    */
+    /*
+    * Search on the result element itself.
+    */
+    if (resultElem->nsDef != NULL) {
+       ns = resultElem->nsDef;
+       do {
+           if (ns->prefix == NULL) {
+               if ((ns->href != NULL) && (ns->href[0] != 0)) {
+                   /*
+                   * Raise a namespace normalization error.
+                   */
+                   xsltTransformError(ctxt, NULL, cur,
+                       "Namespace normalization error: Cannot undeclare "
+                       "the default namespace, since the default namespace "
+                       "'%s' is already declared on the result element.\n",
+                       ns->href);
+                   return(1);
+               } else {
+                   /*
+                   * The default namespace was undeclared on the
+                   * result element.
+                   */
+                   return(0);
+               }
+               break;
+           }
+           ns = ns->next;
+       } while (ns != NULL);
+    }
+
+    if ((resultElem->parent != NULL) &&
+       (resultElem->parent->type == XML_ELEMENT_NODE))
+    {
+       /*
+       * The parent element is in no namespace, so assume
+       * that there is no default namespace in scope.
+       */
+       if (resultElem->parent->ns == NULL)
+           return(0);
+
+       ns = xmlSearchNs(resultElem->doc, resultElem->parent,
+           NULL);
+       /*
+       * Fine if there's no default ns is scope, or if the
+       * default ns was undeclared.
+       */
+       if ((ns == NULL) || (ns->href == NULL) || (ns->href[0] == 0))
+           return(0);
+
+       /*
+       * Undeclare the default namespace.
+       */
+       ns = xmlNewNs(resultElem, BAD_CAST "", NULL);
+       /* TODO: Check result */        
+       return(0);
+    }
+    return(0);
+}
+
 /**
-* xsltTransLREAcquireInScopeNs:
+* xsltTransLREAcquireResultInScopeNs:
 * @ctxt:  the transformation context
 * @cur:  the literal result element
 * @ns:  the namespace
@@ -1500,16 +1587,15 @@ xsltProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node,
 * Returns the acquired ns-declaration
 *         or NULL in case of an API or internal error.
 */
-xmlNsPtr
-xsltTransLREAcquireInScopeNs(xsltTransformContextPtr ctxt,                          
-                            xmlNodePtr cur, xmlNsPtr literalNs,
-                            xmlNodePtr resultElem)
+static xmlNsPtr
+xsltTransLREAcquireResultInScopeNs(xsltTransformContextPtr ctxt,
+                                  xmlNodePtr cur, xmlNsPtr literalNs,
+                                  xmlNodePtr resultElem)
 {    
     xmlNsPtr ns;
     int prefixOccupied = 0;
 
-    if ((ctxt == NULL) || (cur == NULL) ||
-       (resultElem == NULL) || (literalNs == NULL))
+    if ((ctxt == NULL) || (cur == NULL) || (resultElem == NULL))
        return(NULL);
     
     /*
@@ -1607,13 +1693,13 @@ xsltTransLREAcquireInScopeNs(xsltTransformContextPtr ctxt,
        *  1) If keeping the prefix: create a new ns-decl.
        *  2) If reusal: first lookup ns-names; then fallback
        *     to creation of a new ns-decl.
+       * REVISIT TODO: this currently uses case 2) since this
+       *  is the way it used to be before refactoring.
        */
-#if 0
        ns = xmlSearchNsByHref(resultElem->doc, resultElem,
            literalNs->href);
        if (ns != NULL)
            return(ns);
-#endif
        /*
        * Create the ns-decl on the current result element.
        */
@@ -1663,6 +1749,8 @@ xsltTransLREAcquireInScopeNs(xsltTransformContextPtr ctxt,
     return(NULL);    
 }
 
+#endif /* XSLT_REFACTORED */
+
 /**
  * xsltApplyOneTemplate:
  * @ctxt:  a XSLT process context
@@ -1901,16 +1989,14 @@ xsltApplyOneTemplateInt(xsltTransformContextPtr ctxt, xmlNodePtr node,
                            * declarations; thus lookup if there is already
                            * such a ns-decl in the result.
                            */                      
-                           ns = xmlSearchNs(copy->doc, copy,
-                               effNs->prefix);
+                           ns = xmlSearchNs(copy->doc, copy, effNs->prefix);
                            if ((ns != NULL) &&
                                (xmlStrEqual(ns->href, effNs->nsName)))
                            {
                                effNs = effNs->next;
                                continue;                           
                            }
-                           ns = xmlNewNs(copy, effNs->nsName,
-                               effNs->prefix);
+                           ns = xmlNewNs(copy, effNs->nsName, effNs->prefix);
                            if (ns == NULL) {
                                xsltTransformError(ctxt, NULL, cur,
                                    "Internal error in xsltApplyOneTemplateInt(): "
@@ -1945,23 +2031,33 @@ xsltApplyOneTemplateInt(xsltTransformContextPtr ctxt, xmlNodePtr node,
                        *  OLD: copy->ns = xsltGetNamespace(ctxt, cur,
                        *                     cur->ns, copy);
                        */
-                       copy->ns = xsltTransLREAcquireInScopeNs(ctxt,
-                           cur, cur->ns, copy);                                                
-                   } else if ((insert->type == XML_ELEMENT_NODE) &&
-                       (insert->ns != NULL))
-                   {
-                       xmlNsPtr defaultNs;
-
+                       copy->ns = xsltTransLREAcquireResultInScopeNs(ctxt,
+                           cur, cur->ns, copy);
+                   } else {
                        /*
                        * Undeclare the default namespace if needed.
+                       * This can be skipped, if:
+                       * 1) If the result element has no ns-decls, in which
+                       *    case the result element abviously does not
+                       *    declare a default namespace.
+                       * 2) AND there's either no parent, or the parent
+                       *   is in no namespace; this means there's no
+                       *   default namespace is scope to care about.
+                       *
                        * REVISIT TODO: This might result in massive
                        *  generation of ns-decls if nodes in a default
                        *  namespaces are mixed with nodes in no namespace.
                        *  
-                       */                      
+                       */
+                       if (copy->nsDef ||
+                           ((insert != NULL) && (insert->ns != NULL)))
+                           xsltTransLREUndeclareResultDefaultNs(ctxt,
+                               cur, copy);
+#if 0
                        defaultNs = xmlSearchNs(insert->doc, insert, NULL);
                        if ((defaultNs != NULL) && (defaultNs->href != NULL))
                            xmlNewNs(copy, BAD_CAST "", NULL);
+#endif
                    }
                }
                /*
@@ -3101,8 +3197,12 @@ xsltCopy(xsltTransformContextPtr ctxt, xmlNodePtr node,
            case XML_HTML_DOCUMENT_NODE:
                break;
            case XML_ELEMENT_NODE:
-               if (xmlStrEqual(node->name, BAD_CAST " fake node libxslt"))
-                   return;
+               /*
+               * NOTE: The "fake" is a doc-node, not an element node.
+               * OLD:
+               *   if (xmlStrEqual(node->name, BAD_CAST " fake node libxslt"))
+               *    return;
+               */              
 
 #ifdef WITH_XSLT_DEBUG_PROCESS
                XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext,
@@ -3383,6 +3483,10 @@ xsltElement(xsltTransformContextPtr ctxt, xmlNodePtr node,
        if (comp->use != NULL) {
            xsltApplyAttributeSet(ctxt, node, inst, comp->use);
        } else {
+           /*
+           * BUG TODO: use-attribute-sets is not a value template.
+           *  use-attribute-sets = qnames
+           */
            attributes = xsltEvalAttrValueTemplate(ctxt, inst,
                       (const xmlChar *)"use-attribute-sets", NULL);
            if (attributes != NULL) {
@@ -3980,8 +4084,9 @@ xsltApplyTemplates(xsltTransformContextPtr ctxt, xmlNodePtr node,
                if ((list != NULL) && (ctxt->document->keys != NULL)) {
                    if ((list->nodeNr != 0) &&
                        (list->nodeTab[0]->doc != NULL) &&
-                       (xmlStrEqual((xmlChar *)list->nodeTab[0]->doc->name,
-                          (const xmlChar *) " fake node libxslt")) &&
+
+                       XSLT_IS_RES_TREE_FRAG(list->nodeTab[0]->doc) &&
+
                        (list->nodeTab[0]->doc->_private == NULL)) {
                            list->nodeTab[0]->doc->_private = xsltNewDocument(
                                ctxt, list->nodeTab[0]->doc);
@@ -4769,6 +4874,45 @@ done:
     return;
 }
 
+#ifdef XSLT_REFACTORED_KEYCOMP
+static int
+xsltCountKeys(xsltTransformContextPtr ctxt)
+{
+    xsltStylesheetPtr style;
+    xsltKeyDefPtr keyd;
+
+    if (ctxt == NULL)
+       return(-1);    
+
+    /*
+    * Do we have those nastly templates with a key() in the match pattern?
+    */
+    ctxt->hasTemplKeyPatterns = 0;
+    style = ctxt->style;
+    while (style != NULL) {
+       if (style->keyMatch != NULL) {
+           ctxt->hasTemplKeyPatterns = 1;
+           break;
+       }
+       style = xsltNextImport(style);
+    }
+    /*
+    * Count number of key declarations.
+    */
+    ctxt->nbKeys = 0;
+    style = ctxt->style;
+    while (style != NULL) {
+       keyd = style->keys;
+       while (keyd) {
+           ctxt->nbKeys++;
+           keyd = keyd->next;
+       }
+       style = xsltNextImport(style);
+    }        
+    return(ctxt->nbKeys);
+}
+#endif /* XSLT_REFACTORED_KEYCOMP */
+
 /**
  * xsltApplyStylesheetInternal:
  * @style:  a parsed XSLT stylesheet
@@ -4963,6 +5107,9 @@ xsltApplyStylesheetInternal(xsltStylesheetPtr style, xmlDocPtr doc,
     if (params != NULL)
         xsltEvalUserParams(ctxt, params);
     xsltEvalGlobalVariables(ctxt);
+#ifdef XSLT_REFACTORED_KEYCOMP    
+    xsltCountKeys(ctxt);
+#endif
     ctxt->node = (xmlNodePtr) doc;
     varsPush(ctxt, NULL);
     ctxt->varsBase = ctxt->varsNr - 1;
index 8bb8771..a1fd3f8 100644 (file)
 #define WITH_XSLT_DEBUG_VARIABLE
 #endif
 
+#ifdef XSLT_REFACTORED
+const xmlChar *xsltDocFragFake = (const xmlChar *) " fake node libxslt";
+#endif
+
 /************************************************************************
  *                                                                     *
  *                     Result Value Tree interfaces                    *
@@ -54,6 +58,9 @@ xsltCreateRVT(xsltTransformContextPtr ctxt)
 {
     xmlDocPtr container;
 
+    /*
+    * TODO: Couldn't we use an XML_DOCUMENT_FRAG_NODE for this?
+    */
     if (ctxt == NULL) return(NULL);
 
     container = xmlNewDoc(NULL);
@@ -66,7 +73,7 @@ xsltCreateRVT(xsltTransformContextPtr ctxt)
                      "reusing transformation dict for RVT\n");
 #endif
 
-    container->name = (char *) xmlStrdup(BAD_CAST " fake node libxslt");
+    XSLT_MARK_RES_TREE_FRAG(container);
     container->doc = container;
     container->parent = NULL;
     return(container);
index ce31817..513fbf7 100644 (file)
@@ -447,15 +447,14 @@ xsltFreeNamespaceMap(xsltNsMapPtr item)
 }
 
 static xsltNsMapPtr
-xsltNewNamespaceMapItem(xsltCompilerCtxtPtr cctxt,                     
+xsltNewNamespaceMapItem(xsltCompilerCtxtPtr cctxt,
                        xmlDocPtr doc,
-                       xmlNodePtr elem,
-                       xmlNsPtr ns)
+                       xmlNsPtr ns,
+                       xmlNodePtr elem)
 {
     xsltNsMapPtr ret;
 
-    if ((cctxt == NULL) || (doc == NULL) || (elem == NULL) ||
-       (ns == NULL))
+    if ((cctxt == NULL) || (doc == NULL) || (ns == NULL))
        return(NULL);
 
     ret = (xsltNsMapPtr) xmlMalloc(sizeof(xsltNsMap));
@@ -490,6 +489,14 @@ xsltCompilationCtxtFree(xsltCompilerCtxtPtr cctxt)
 {    
     if (cctxt == NULL)
        return;
+#ifdef WITH_XSLT_DEBUG_PARSING
+    xsltGenericDebug(xsltGenericDebugContext,
+       "Freeing compilation context\n");
+    xsltGenericDebug(xsltGenericDebugContext,
+       "### Max inodes: %d\n", cctxt->maxNodeInfos);
+    xsltGenericDebug(xsltGenericDebugContext,
+       "### Max LREs  : %d\n", cctxt->maxLREs);
+#endif
     /*
     * Free node-infos.
     */
@@ -503,8 +510,10 @@ xsltCompilationCtxtFree(xsltCompilerCtxtPtr cctxt)
     }
     if (cctxt->tmpList != NULL)
        xsltPointerListFree(cctxt->tmpList);
+#ifdef XSLT_REFACTORED_XPATHCOMP
     if (cctxt->xpathCtxt != NULL)
        xmlXPathFreeContext(cctxt->xpathCtxt);
+#endif
     if (cctxt->nsAliases != NULL)
        xsltFreeNsAliasList(cctxt->nsAliases);
 
@@ -537,6 +546,7 @@ xsltCompilationCtxtCreate(xsltStylesheetPtr style) {
     if (ret->tmpList == NULL) {
        goto internal_err;
     }
+#ifdef XSLT_REFACTORED_XPATHCOMP
     /*
     * Create the XPath compilation context in order
     * to speed up precompilation of XPath expressions.
@@ -544,6 +554,7 @@ xsltCompilationCtxtCreate(xsltStylesheetPtr style) {
     ret->xpathCtxt = xmlXPathNewContext(NULL);
     if (ret->xpathCtxt == NULL)
        goto internal_err;
+#endif
     
     return(ret);
 
@@ -552,6 +563,17 @@ internal_err:
     return(NULL);
 }
 
+static void
+xsltLREEffectiveNsNodesFree(xsltEffectiveNsPtr first)
+{
+    xsltEffectiveNsPtr tmp;
+
+    while (first != NULL) {
+       tmp = first;
+       first = first->nextInStore;
+       xmlFree(tmp);
+    }
+}
 
 static void
 xsltFreePrincipalStylesheetData(xsltPrincipalStylesheetDataPtr data)
@@ -601,6 +623,10 @@ xsltFreePrincipalStylesheetData(xsltPrincipalStylesheetDataPtr data)
        xsltPointerListFree(list);
        data->extElemNamespaces = NULL;
     }
+    if (data->effectiveNs) {
+       xsltLREEffectiveNsNodesFree(data->effectiveNs);
+       data->effectiveNs = NULL;
+    }
 #ifdef XSLT_REFACTORED_XSLT_NSCOMP
     xsltFreeNamespaceMap(data->nsMap);
 #endif
@@ -1697,7 +1723,17 @@ xsltTreeEnsureXMLDecl(xmlDocPtr doc)
        }
        memset(ns, 0, sizeof(xmlNs));
        ns->type = XML_LOCAL_NAMESPACE;
+       /*
+       * URGENT TODO: revisit this.
+       */
+#ifdef LIBXML_NAMESPACE_DICT
+       if (doc->dict)
+           ns->href = xmlDictLookup(doc->dict, XML_XML_NAMESPACE, -1);
+       else
+           ns->href = xmlStrdup(XML_XML_NAMESPACE);
+#else
        ns->href = xmlStrdup(XML_XML_NAMESPACE); 
+#endif
        ns->prefix = xmlStrdup((const xmlChar *)"xml");
        doc->oldNs = ns;
        return (ns);
@@ -1936,11 +1972,12 @@ xsltLREBuildEffectiveNs(xsltCompilerCtxtPtr cctxt,
 static int
 xsltLREBuildEffectiveNsNodes(xsltCompilerCtxtPtr cctxt,
                             xsltStyleItemLRElementInfoPtr item,
-                            xmlNodePtr elem)
+                            xmlNodePtr elem,
+                            int isLRE)
 {
-    xmlNsPtr ns;
+    xmlNsPtr ns, tmpns;
     xsltEffectiveNsPtr effNs, lastEffNs = NULL;
-    int i;
+    int i, j, holdByElem;
     xsltPointerListPtr extElemNs = cctxt->inode->extElemNs;
     xsltPointerListPtr exclResultNs = cctxt->inode->exclResultNs;
 
@@ -1948,13 +1985,14 @@ xsltLREBuildEffectiveNsNodes(xsltCompilerCtxtPtr cctxt,
        (item == NULL) || (item->effectiveNs != NULL))
        return(-1);
 
-    if (elem->nsDef == NULL)
+    if (item->inScopeNs == NULL)    
        return(0);
 
     extElemNs = cctxt->inode->extElemNs;
     exclResultNs = cctxt->inode->exclResultNs;
 
-    for (ns = elem->nsDef; ns != NULL; ns = ns->next) {
+    for (i = 0; i < item->inScopeNs->number; i++) {
+       ns = item->inScopeNs->list[i];
        /*
        * Skip namespaces designated as excluded namespaces
        * -------------------------------------------------
@@ -1972,19 +2010,34 @@ xsltLREBuildEffectiveNsNodes(xsltCompilerCtxtPtr cctxt,
        * Exclude excluded result namespaces.
        */
        if (exclResultNs) {
-           for (i = 0; i < exclResultNs->number; i++)
-               if (xmlStrEqual(ns->href, BAD_CAST exclResultNs->items[i]))
+           for (j = 0; j < exclResultNs->number; j++)
+               if (xmlStrEqual(ns->href, BAD_CAST exclResultNs->items[j]))
                    goto skip_ns;
        }
        /*
        * Exclude extension-element namespaces.
        */
        if (extElemNs) {
-           for (i = 0; i < extElemNs->number; i++)
-               if (xmlStrEqual(ns->href, BAD_CAST extElemNs->items[i]))
+           for (j = 0; j < extElemNs->number; j++)
+               if (xmlStrEqual(ns->href, BAD_CAST extElemNs->items[j]))
                    goto skip_ns;
        }
        /*
+       * OPTIMIZE TODO: This information may not be needed.
+       */
+       if (isLRE && (elem->nsDef != NULL)) {
+           holdByElem = 0;
+           tmpns = elem->nsDef;
+           do {
+               if (tmpns == ns) {
+                   holdByElem = 1;
+                   break;
+               }
+               tmpns = tmpns->next;
+           } while (tmpns != NULL);        
+       } else
+           holdByElem = 0;
+       /*
        * Apply namespace aliasing
        * ------------------------
        * 
@@ -2002,7 +2055,7 @@ xsltLREBuildEffectiveNsNodes(xsltCompilerCtxtPtr cctxt,
                * TODO: What to do with xmlns="" ?
                */
                if ((alias->literalNs != NULL) &&
-                   (alias->literalNs->href == ns->href))
+                   (xmlStrEqual(alias->literalNs->href, ns->href)))
                {
                    /*
                    * Recognized as an namespace alias; convert it to
@@ -2014,6 +2067,7 @@ xsltLREBuildEffectiveNsNodes(xsltCompilerCtxtPtr cctxt,
                alias = alias->next;
            } while (alias != NULL);            
        }
+       
        /*
        * Add the effective namespace declaration.
        */
@@ -2025,15 +2079,24 @@ xsltLREBuildEffectiveNsNodes(xsltCompilerCtxtPtr cctxt,
            cctxt->style->errors++;
            return(-1);
        }
+       if (cctxt->psData->effectiveNs == NULL) {
+           cctxt->psData->effectiveNs = effNs;
+           effNs->nextInStore = NULL;   
+       } else {
+           effNs->nextInStore = cctxt->psData->effectiveNs;
+           cctxt->psData->effectiveNs = effNs;
+       }
+
        effNs->next = NULL;
        effNs->prefix = ns->prefix;
        effNs->nsName = ns->href;
+       effNs->holdByElem = holdByElem;
        
        if (lastEffNs == NULL)
            item->effectiveNs = effNs;
        else
            lastEffNs->next = effNs;
-       lastEffNs = effNs;              
+       lastEffNs = effNs;
        
 skip_ns:
        {}
@@ -2045,11 +2108,14 @@ skip_ns:
 /**
  * xsltLREInfoCreate:
  *
+ * @isLRE: indicates if the given @elem is a literal result element
+ *
  * Creates a new info for a literal result element.
  */
 static int
 xsltLREInfoCreate(xsltCompilerCtxtPtr cctxt,
-                                  xmlNodePtr elem)
+                 xmlNodePtr elem,
+                 int isLRE)
 {
     xsltStyleItemLRElementInfoPtr item;
 
@@ -2078,11 +2144,12 @@ xsltLREInfoCreate(xsltCompilerCtxtPtr cctxt,
     */
     item->inScopeNs = cctxt->inode->inScopeNs;
     
-    if (elem && (elem->nsDef != NULL)) 
-       xsltLREBuildEffectiveNsNodes(cctxt, item, elem);        
+    if (elem)
+       xsltLREBuildEffectiveNsNodes(cctxt, item, elem, isLRE);
 
     cctxt->inode->litResElemInfo = item;
     cctxt->inode->nsChanged = 0;
+    cctxt->maxLREs++;
     return(0);
 }
 
@@ -2098,7 +2165,7 @@ xsltLREInfoCreate(xsltCompilerCtxtPtr cctxt,
 xsltCompilerNodeInfoPtr
 xsltCompilerNodePush(xsltCompilerCtxtPtr cctxt, xmlNodePtr node)
 {    
-    xsltCompilerNodeInfoPtr inode;
+    xsltCompilerNodeInfoPtr inode, iprev;
 
     if ((cctxt->inode != NULL) && (cctxt->inode->next != NULL)) {      
        inode = cctxt->inode->next;
@@ -2130,7 +2197,7 @@ xsltCompilerNodePush(xsltCompilerCtxtPtr cctxt, xmlNodePtr node)
            * Create an initial literal result element info for
            * the root of the stylesheet.
            */
-           xsltLREInfoCreate(cctxt, NULL);
+           xsltLREInfoCreate(cctxt, NULL, 0);
        } 
     }       
     cctxt->depth++;
@@ -2150,35 +2217,36 @@ xsltCompilerNodePush(xsltCompilerCtxtPtr cctxt, xmlNodePtr node)
     inode->extContentHandled = 0;
     inode->isRoot = 0;
     
-    if (inode->prev != NULL) { 
+    if (inode->prev != NULL) {
+       iprev = inode->prev;
        /*
        * Inherit the following information:
        * ---------------------------------
        *
        * In-scope namespaces
        */
-       inode->inScopeNs = inode->prev->inScopeNs;
+       inode->inScopeNs = iprev->inScopeNs;
        /*
        * Info for literal result elements
        */
-       inode->litResElemInfo = inode->prev->litResElemInfo;
-       inode->nsChanged = inode->prev->nsChanged;
+       inode->litResElemInfo = iprev->litResElemInfo;
+       inode->nsChanged = iprev->nsChanged;
        /*
        * Excluded result namespaces
        */
-       inode->exclResultNs = inode->prev->exclResultNs;
+       inode->exclResultNs = iprev->exclResultNs;
        /*
        * Extension instruction namespaces
        */
-       inode->extElemNs = inode->prev->extElemNs;
+       inode->extElemNs = iprev->extElemNs;
        /*
        * Whitespace preservation
        */
-       inode->preserveWhitespace = inode->prev->preserveWhitespace;
+       inode->preserveWhitespace = iprev->preserveWhitespace;
        /*
        * Forwards-compatible mode
        */
-       inode->forwardsCompat = inode->prev->forwardsCompat;    
+       inode->forwardsCompat = iprev->forwardsCompat;  
     } else {
        inode->inScopeNs = NULL;
        inode->exclResultNs = NULL;
@@ -2477,7 +2545,7 @@ xsltCompilerUtilsCreateMergedList(xsltPointerListPtr first,
 static xsltPointerListPtr
 xsltParseExclResultPrefixes(xsltCompilerCtxtPtr cctxt, xmlNodePtr node,
                            xsltPointerListPtr def,
-                           int isXsltElem)
+                           int instrCategory)
 {    
     xsltPointerListPtr list = NULL;
     xmlChar *value = NULL;
@@ -2485,7 +2553,7 @@ xsltParseExclResultPrefixes(xsltCompilerCtxtPtr cctxt, xmlNodePtr node,
     if ((cctxt == NULL) || (node == NULL))
        return(NULL);
     
-    if (isXsltElem)
+    if (instrCategory == XSLT_ELEMENT_CATEGORY_XSLT)
        value = xmlGetNsProp(node, BAD_CAST "exclude-result-prefixes", NULL);
     else
        value = xmlGetNsProp(node, BAD_CAST "exclude-result-prefixes",
@@ -2543,23 +2611,34 @@ exit:
 static xsltPointerListPtr
 xsltParseExtElemPrefixes(xsltCompilerCtxtPtr cctxt, xmlNodePtr node,
                         xsltPointerListPtr def,
-                        int isXsltElem)
+                        int instrCategory)
 {    
     xsltPointerListPtr list = NULL;
+    xmlAttrPtr attr;
     xmlChar *value;
     int i;
 
     if ((cctxt == NULL) || (node == NULL))
        return(NULL);
 
-    if (isXsltElem)
-       value = xmlGetNsProp(node, BAD_CAST "extension-element-prefixes", NULL);
+    if (instrCategory == XSLT_ELEMENT_CATEGORY_XSLT)
+       attr = xmlHasNsProp(node, BAD_CAST "extension-element-prefixes", NULL);
     else
-       value = xmlGetNsProp(node, BAD_CAST "extension-element-prefixes",
+       attr = xmlHasNsProp(node, BAD_CAST "extension-element-prefixes",
            XSLT_NAMESPACE);
+    if (attr == NULL)  
+       return(def);
 
-    if (value == NULL) 
+    if ((attr->children != NULL) &&    
+       (attr->children->content != NULL))
+       value = attr->children->content;
+    else {
+       xsltTransformError(NULL, cctxt->style, node,
+           "Attribute 'extension-element-prefixes': Invalid value.\n");
+       cctxt->style->errors++;
        return(def);
+    }
+
 
     if (xsltParseNsPrefixList(cctxt, cctxt->tmpList, node,
        BAD_CAST value) != 0)
@@ -2596,8 +2675,13 @@ xsltParseExtElemPrefixes(xsltCompilerCtxtPtr cctxt, xmlNodePtr node,
        cctxt->inode->nsChanged = 1;
 
 exit:
-    if (value != NULL)
-       xmlFree(value);
+    if (attr && (instrCategory == XSLT_ELEMENT_CATEGORY_LRE)) {
+       /*
+       * Remove the XSLT attribute from the literal result element.
+       */
+       xmlUnlinkNode((xmlNodePtr) attr);
+       xmlFreeProp(attr);
+    }
     if (list != NULL)
        return(list);
     else
@@ -2619,20 +2703,31 @@ exit:
 */
 static int
 xsltParseAttrXSLTVersion(xsltCompilerCtxtPtr cctxt, xmlNodePtr node,                    
-                        int isXsltElem)
+                        int instrCategory)
 {
     xmlChar *value;
+    xmlAttrPtr attr;
 
     if ((cctxt == NULL) || (node == NULL))
        return(-1);
 
-    if (isXsltElem)
-       value = xmlGetNsProp(node, BAD_CAST "version", NULL);
+    if (instrCategory == XSLT_ELEMENT_CATEGORY_XSLT)
+       attr = xmlHasNsProp(node, BAD_CAST "version", NULL);
     else
-       value = xmlGetNsProp(node, BAD_CAST "version", XSLT_NAMESPACE);
+       attr = xmlHasNsProp(node, BAD_CAST "version", XSLT_NAMESPACE);
 
-    if (value == NULL) 
+    if (attr == NULL)  
        return(0);
+
+    if ((attr->children != NULL) &&    
+       (attr->children->content != NULL))
+       value = attr->children->content;
+    else {
+       xsltTransformError(NULL, cctxt->style, node,
+           "Attribute 'version': Invalid value.\n");
+       cctxt->style->errors++;
+       return(1);
+    }
     
     if (! xmlStrEqual(value, (const xmlChar *)"1.0")) {
        cctxt->inode->forwardsCompat = 1;
@@ -2657,10 +2752,38 @@ xsltParseAttrXSLTVersion(xsltCompilerCtxtPtr cctxt, xmlNodePtr node,
     } else {
        cctxt->inode->forwardsCompat = 0;
     }
-    xmlFree(value);
+
+    if (attr && (instrCategory == XSLT_ELEMENT_CATEGORY_LRE)) {
+       /*
+       * Remove the XSLT attribute from the literal result element.
+       */
+       xmlUnlinkNode((xmlNodePtr) attr);
+       xmlFreeProp(attr);
+    }
     return(1);
 }
 
+#if 0
+static int
+xsltParseRemoveXSLTAttrs(xsltCompilerCtxtPtr cctxt, xmlNodePtr node)
+{
+    if (node->properties == NULL)
+       return(0);
+    else {
+       xmlAttrPtr tmpattr, attr = node->properties;
+       do {        
+           if (IS_XSLT_ATTR_FAST(attr)) {
+               tmpattr = attr;
+               attr = attr->next;
+               xmlUnlinkNode((xmlNodePtr) tmpattr);
+               xmlFreeProp(tmpattr);
+           } else
+               attr = attr->next;
+       } while (attr != NULL);
+    }
+}
+#endif
+
 static int
 xsltParsePreprocessStylesheetTree(xsltCompilerCtxtPtr cctxt, xmlNodePtr node)
 {
@@ -2746,8 +2869,8 @@ xsltParsePreprocessStylesheetTree(xsltCompilerCtxtPtr cctxt, xmlNodePtr node)
            if (IS_XSLT_ELEM(cur)) {
 #ifdef XSLT_REFACTORED_XSLT_NSCOMP
                if (cur->ns->href != nsNameXSLT) {
-                   nsMapItem = xsltNewNamespaceMapItem(cctxt,                  
-                       doc, cur, cur->ns);
+                   nsMapItem = xsltNewNamespaceMapItem(cctxt,
+                       doc, cur->ns, cur);
                    if (nsMapItem == NULL)
                        goto internal_err;
                    cur->ns->href = nsNameXSLT;
@@ -2868,6 +2991,17 @@ process_attributes:
                    findSpaceAttr = 0;
                attr = cur->properties;
                do {
+#ifdef XSLT_REFACTORED_XSLT_NSCOMP
+                   if ((attr->ns) && (attr->ns->href != nsNameXSLT) &&
+                       xmlStrEqual(attr->ns->href, nsNameXSLT))
+                   {                   
+                       nsMapItem = xsltNewNamespaceMapItem(cctxt,
+                           doc, attr->ns, cur);
+                       if (nsMapItem == NULL)
+                           goto internal_err;
+                       attr->ns->href = nsNameXSLT;
+                   }               
+#endif
                    if (internalize) {
                        /*
                        * Internalize the attribute's value; the goal is to
@@ -4135,7 +4269,8 @@ xsltParseSequenceConstructor(xsltCompilerCtxtPtr cctxt, xmlNodePtr cur)
                if (cur->properties)
                    cctxt->inode->extElemNs =
                        xsltParseExtElemPrefixes(cctxt,
-                           cur, cctxt->inode->extElemNs, 0);
+                           cur, cctxt->inode->extElemNs,
+                           XSLT_ELEMENT_CATEGORY_LRE);
                /*
                * Eval if we have an extension instruction here.
                */
@@ -4218,24 +4353,45 @@ xsltParseSequenceConstructor(xsltCompilerCtxtPtr cctxt, xmlNodePtr cur)
                    *  xsl:version NMTOKEN #IMPLIED
                    */
                    cur->psvi = NULL;
-                   cctxt->inode->category = XSLT_ELEMENT_CATEGORY_LR;
+                   cctxt->inode->category = XSLT_ELEMENT_CATEGORY_LRE;
                    if (cur->properties != NULL) {
                        /*
                        * Attribute "xsl:exclude-result-prefixes".
                        */
                        cctxt->inode->exclResultNs =
                            xsltParseExclResultPrefixes(cctxt, cur,
-                               cctxt->inode->exclResultNs, 0);
+                               cctxt->inode->exclResultNs,
+                               XSLT_ELEMENT_CATEGORY_LRE);
                        /*
                        * Attribute "xsl:version".
                        */
-                       xsltParseAttrXSLTVersion(cctxt, cur, 0);
+                       xsltParseAttrXSLTVersion(cctxt, cur,
+                           XSLT_ELEMENT_CATEGORY_LRE);
+                       /*
+                       * Report invalid XSLT attributes.
+                       */
+                       if (cur->properties) {
+                           xmlAttrPtr attr = cur->properties;
+
+                           do {
+                               if (IS_XSLT_ATTR_FAST(attr) &&
+                                   (! xmlStrEqual(attr->name,
+                                       BAD_CAST "use-attribute-sets")))
+                               {                               
+                                   xsltTransformError(NULL, cctxt->style, cur,
+                                       "Unknown XSLT attribute '%s'.\n",
+                                       attr->name);                            
+                                    cctxt->style->errors++;
+                               }
+                               attr = attr->next;
+                           } while (attr != NULL);                         
+                       }
                    }
                    /*
                    * Create/reuse info for the literal result element.
                    */
                    if (cctxt->inode->nsChanged)
-                       xsltLREInfoCreate(cctxt, cur);
+                       xsltLREInfoCreate(cctxt, cur, 1);
                    cur->psvi = cctxt->inode->litResElemInfo;
                    /*
                    * Apply ns-aliasing on the element and on its attributes.
@@ -4846,7 +5002,6 @@ skip_ns:
 
 error:
     xsltCompilerNodePop(cctxt, templNode);
-
     return;
 }
 
@@ -5204,7 +5359,7 @@ xsltParseXSLTStylesheetElemCore(xsltCompilerCtxtPtr cctxt, xmlNodePtr node)
     cctxt->inode->isRoot = 1;
     cctxt->inode->nsChanged = 0;
     /*
-    * Start with the dummy info for literal result elements.
+    * Start with the naked dummy info for literal result elements.
     */
     cctxt->inode->litResElemInfo = cctxt->inodeList->litResElemInfo;
 
@@ -5227,7 +5382,9 @@ xsltParseXSLTStylesheetElemCore(xsltCompilerCtxtPtr cctxt, xmlNodePtr node)
     *  exclude-result-prefixes = tokens
     *  version = number (mandatory)    
     */
-    if (xsltParseAttrXSLTVersion(cctxt, node, 1) == 0) {    
+    if (xsltParseAttrXSLTVersion(cctxt, node,
+       XSLT_ELEMENT_CATEGORY_XSLT) == 0)
+    {    
        /*
        * Attribute "version".
        * XSLT 1.0: "An xsl:stylesheet element *must* have a version
@@ -5258,17 +5415,19 @@ xsltParseXSLTStylesheetElemCore(xsltCompilerCtxtPtr cctxt, xmlNodePtr node)
     * Attribute "extension-element-prefixes".
     */
     cctxt->inode->extElemNs =
-       xsltParseExtElemPrefixes(cctxt, node, NULL, 1);
+       xsltParseExtElemPrefixes(cctxt, node, NULL,
+           XSLT_ELEMENT_CATEGORY_XSLT);
     /*
     * Attribute "exclude-result-prefixes".
     */
     cctxt->inode->exclResultNs =
-       xsltParseExclResultPrefixes(cctxt, node, NULL, 1);
+       xsltParseExclResultPrefixes(cctxt, node, NULL,
+           XSLT_ELEMENT_CATEGORY_XSLT);
     /*
     * Create/reuse info for the literal result element.
     */
     if (cctxt->inode->nsChanged)
-       xsltLREInfoCreate(cctxt, NULL);
+       xsltLREInfoCreate(cctxt, node, 0);
     /*
     * Processed top-level elements:
     * ----------------------------
@@ -5455,8 +5614,8 @@ xsltParseXSLTStylesheetElemCore(xsltCompilerCtxtPtr cctxt, xmlNodePtr node)
                    function(style, cur);
 #ifdef WITH_XSLT_DEBUG_PARSING
                xsltGenericDebug(xsltGenericDebugContext,
-                   "xsltParseStylesheetTop : found foreign element %s\n",
-                   cur->name);
+                   "xsltParseXSLTStylesheetElemCore : User-defined "
+                   "data element '%s'.\n", cur->name);
 #endif
            }
        }
@@ -5464,11 +5623,22 @@ xsltParseXSLTStylesheetElemCore(xsltCompilerCtxtPtr cctxt, xmlNodePtr node)
     }
 
 exit:
-    xsltCompilerNodePop(cctxt, node);
+
 #ifdef WITH_XSLT_DEBUG_PARSING
     xsltGenericDebug(xsltGenericDebugContext,
-                   "parsed %d templates\n", templates);
-#endif
+       "### END of parsing top-level elements of doc '%s'.\n",
+       node->doc->URL);
+    xsltGenericDebug(xsltGenericDebugContext,
+       "### Templates: %d\n", templates);
+#ifdef XSLT_REFACTORED
+    xsltGenericDebug(xsltGenericDebugContext,
+       "### Max inodes: %d\n", cctxt->maxNodeInfos);
+    xsltGenericDebug(xsltGenericDebugContext,
+       "### Max LREs  : %d\n", cctxt->maxLREs);
+#endif /* XSLT_REFACTORED */
+#endif /* WITH_XSLT_DEBUG_PARSING */
+
+    xsltCompilerNodePop(cctxt, node);
     return(0);
 }
 
@@ -5503,7 +5673,8 @@ xsltParseXSLTStylesheetElem(xsltCompilerCtxtPtr cctxt, xmlNodePtr node)
        return(-1);
     
     if (node->children == NULL)
-       goto exit;    
+       goto exit;
+
     /*
     * Process top-level elements:
     *  xsl:import (must be first)
@@ -5541,6 +5712,8 @@ xsltParseXSLTStylesheetElem(xsltCompilerCtxtPtr cctxt, xmlNodePtr node)
     }
     /*
     * Pre-process all xsl:namespace-alias elements.
+    * URGENT TODO: This won't work correctly: the order of included
+    *  aliases and aliases defined here is significant.
     */
     cur = start;
     while ((cur != NULL) &&
@@ -5562,7 +5735,8 @@ xsltParseXSLTStylesheetElem(xsltCompilerCtxtPtr cctxt, xmlNodePtr node)
     * Now parse the rest of the top-level elements.
     */
     xsltParseXSLTStylesheetElemCore(cctxt, node);      
-exit:    
+exit:
+
     return(0);
 }
 
@@ -5741,10 +5915,9 @@ xsltParseSimplifiedStylesheetTree(xsltCompilerCtxtPtr cctxt,
     
     if ((cctxt == NULL) || (node == NULL))
        return(-1);
-    /*
-    * 
-    */
-    if (xsltParseAttrXSLTVersion(cctxt, node, 0) == 0) {
+
+    if (xsltParseAttrXSLTVersion(cctxt, node, 0) == XSLT_ELEMENT_CATEGORY_LRE)
+    {
        /*
        * TODO: Adjust report, since this might be an
        * embedded stylesheet.
@@ -5811,8 +5984,8 @@ xsltRestoreDocumentNamespaces(xsltNsMapPtr ns, xmlDocPtr doc)
     while (ns != NULL) {
        if ((ns->doc == doc) && (ns->ns != NULL)) {
            ns->ns->href = ns->origNsName;
-           ns->ns = NULL;
-           ns->origNsName = NULL;          
+           ns->origNsName = NULL;
+           ns->ns = NULL;          
        }
        ns = ns->next;
     }
@@ -6124,10 +6297,15 @@ xsltParseStylesheetImportedDoc(xmlDocPtr doc,
                */
                retStyle->doc = NULL;
                /*
-               * Cleanup the doc if its the main doc.
+               * Cleanup the doc if its the main stylesheet.
                */
-               if (parentStyle == NULL)
+               if (parentStyle == NULL) {
                    xsltCleanupStylesheetTree(doc, xmlDocGetRootElement(doc));
+                   if (retStyle->compCtxt != NULL) {                   
+                       xsltCompilationCtxtFree(retStyle->compCtxt);
+                       retStyle->compCtxt = NULL;
+                   }
+               }
 
                xsltFreeStylesheet(retStyle);
                retStyle = NULL;
@@ -6181,8 +6359,6 @@ xsltParseStylesheetDoc(xmlDocPtr doc) {
     *   xsltParseStylesheetImportedDoc().
     */
     if (ret->compCtxt != NULL) {
-       /* TEST TODO: REMOVE test output*/
-       /* printf("MAX NODE INFO: %d\n", XSLT_CCTXT(ret)->maxNodeInfos); */ 
        xsltCompilationCtxtFree(XSLT_CCTXT(ret));
        ret->compCtxt = NULL;
     }
index a43c69b..e76f170 100644 (file)
@@ -18,6 +18,7 @@
 #include <libxml/xpath.h>
 #include <libxml/xmlerror.h>
 #include <libxml/dict.h>
+#include <libxml/xmlstring.h>
 #include <libxslt/xslt.h>
 #include "xsltexports.h"
 #include "numbersInternals.h"
 extern "C" {
 #endif
 
-#define XSLT_IS_TEXT_NODE(n) (((n) != NULL) && \
+#define XSLT_IS_TEXT_NODE(n) ((n != NULL) && \
     (((n)->type == XML_TEXT_NODE) || \
      ((n)->type == XML_CDATA_SECTION_NODE)))
 
+
+#if 0
+
+extern const xmlChar *xsltDocFragFake;
+
+#define XSLT_MARK_RES_TREE_FRAG(n) (n)->psvi = (void *) xsltDocFragFake;
+
+#define XSLT_IS_RES_TREE_FRAG(n) \
+    ((n != NULL) && ((n)->type == XML_DOCUMENT_NODE) && \
+     ((n)->psvi == xsltDocFragFake))
+
+#else
+
+#define XSLT_MARK_RES_TREE_FRAG(n) \
+    (n)->name = (char *) xmlStrdup(BAD_CAST " fake node libxslt");
+
+#define XSLT_IS_RES_TREE_FRAG(n) \
+    ((n != NULL) && ((n)->type == XML_DOCUMENT_NODE) && \
+     ((n)->name != NULL) && ((n)->name[0] == ' ') && \
+    xmlStrEqual(BAD_CAST (n)->name, BAD_CAST " fake node libxslt"))
+
+#endif
+
+/**
+ * XSLT_REFACTORED_KEYCOMP:
+ *
+ * Internal define to enable on-demand xsl:key computation.
+ */
+#define XSLT_REFACTORED_KEYCOMP
+
 /**
  * XSLT_REFACTORED:
  *
  * Internal define to enable the refactored parts of Libxslt.
  */
 /* #define XSLT_REFACTORED */
+/* ==================================================================== */
 
 #ifdef XSLT_REFACTORED
-
 /* TODO: REMOVE: #define XSLT_REFACTORED_EXCLRESNS */
 
 /* TODO: REMOVE: #define XSLT_REFACTORED_NSALIAS */
@@ -67,6 +98,10 @@ extern const xmlChar *xsltConstNamespaceNameXSLT;
     (((n) != NULL) && ((n)->ns != NULL) && \
     ((n)->ns->href == xsltConstNamespaceNameXSLT))
 
+#define IS_XSLT_ATTR_FAST(a) \
+    (((a) != NULL) && ((a)->ns != NULL) && \
+    ((a)->ns->href == xsltConstNamespaceNameXSLT))
+
 #define XSLT_HAS_INTERNAL_NSMAP(s) \
     (((s) != NULL) && ((s)->principal) && \
      ((s)->principal->principalData) && \
@@ -80,6 +115,10 @@ extern const xmlChar *xsltConstNamespaceNameXSLT;
     (((n) != NULL) && ((n)->ns != NULL) && \
      (xmlStrEqual((n)->ns->href, XSLT_NAMESPACE)))
 
+#define IS_XSLT_ATTR_FAST(a) \
+    (((a) != NULL) && ((a)->ns != NULL) && \
+     (xmlStrEqual((a)->ns->href, XSLT_NAMESPACE)))
+
 
 #endif /* XSLT_REFACTORED_XSLT_NSCOMP */
 
@@ -191,6 +230,10 @@ struct _xsltTemplate {
     xmlNodePtr content;        /* the template replacement value */
     xmlNodePtr elem;   /* the source element */
 
+    /*
+    * TODO: @inheritedNsNr and @inheritedNs won't be used in the
+    *  refactored code.
+    */
     int inheritedNsNr;  /* number of inherited namespaces */
     xmlNsPtr *inheritedNs;/* inherited non-excluded namespaces */
 
@@ -239,6 +282,42 @@ struct _xsltDocument {
     void *keys;                        /* key tables storage */
     struct _xsltDocument *includes; /* subsidiary includes */
     int preproc;               /* pre-processing already done */
+    int nbKeysComputed;
+};
+
+/**
+ * xsltKeyDef:
+ *
+ * Representation of an xsl:key.
+ */
+typedef struct _xsltKeyDef xsltKeyDef;
+typedef xsltKeyDef *xsltKeyDefPtr;
+struct _xsltKeyDef {
+    struct _xsltKeyDef *next;
+    xmlNodePtr inst;
+    xmlChar *name;
+    xmlChar *nameURI;
+    xmlChar *match;
+    xmlChar *use;
+    xmlXPathCompExprPtr comp;
+    xmlXPathCompExprPtr usecomp;
+    xmlNsPtr *nsList;           /* the namespaces in scope */
+    int nsNr;                   /* the number of namespaces in scope */
+};
+
+/**
+ * xsltKeyTable:
+ *
+ * Holds the computed keys for key definitions of the same QName.
+ * Is owned by an xsltDocument.
+ */
+typedef struct _xsltKeyTable xsltKeyTable;
+typedef xsltKeyTable *xsltKeyTablePtr;
+struct _xsltKeyTable {
+    struct _xsltKeyTable *next;
+    xmlChar *name;
+    xmlChar *nameURI;
+    xmlHashTablePtr keys;
 };
 
 /*
@@ -1006,9 +1085,15 @@ struct _xsltStyleItemExtElement {
 typedef struct _xsltEffectiveNs xsltEffectiveNs;
 typedef xsltEffectiveNs *xsltEffectiveNsPtr;
 struct _xsltEffectiveNs {
+    xsltEffectiveNsPtr nextInStore; /* storage next */
     xsltEffectiveNsPtr next; /* next item in the list */
     const xmlChar *prefix;
     const xmlChar *nsName;
+    /* 
+    * Indicates if eclared on the literal result element; dunno if really
+    * needed.
+    */
+    int holdByElem;
 };
 
 /*
@@ -1032,6 +1117,7 @@ struct _xsltStyleItemLRElementInfo {
     *  Namespace-aliasing was applied on the @effectiveNs.
     */
     xsltEffectiveNsPtr effectiveNs;
+
 };
 
 #ifdef XSLT_REFACTORED
@@ -1078,7 +1164,7 @@ struct _xsltNsList {
 
 #define XSLT_ELEMENT_CATEGORY_XSLT 0
 #define XSLT_ELEMENT_CATEGORY_EXTENSION 1
-#define XSLT_ELEMENT_CATEGORY_LR 2
+#define XSLT_ELEMENT_CATEGORY_LRE 2
 
 /**
  * xsltCompilerNodeInfo:
@@ -1157,7 +1243,8 @@ struct _xsltCompilerCtxt {
     int isInclude;
     int hasForwardsCompat; /* whether forwards-compatible mode was used
                             in a parsing episode */
-    int maxNodeInfos; /* just for the interest */
+    int maxNodeInfos; /* TEMP TODO: just for the interest */
+    int maxLREs;  /* TEMP TODO: just for the interest */
     /* 
     * In order to keep the old behaviour, applying strict rules of
     * the spec can be turned off. This has effect only on special
@@ -1170,7 +1257,7 @@ struct _xsltCompilerCtxt {
 #endif
     xsltStyleItemUknownPtr unknownItem;
     int hasNsAliases; /* Indicator if there was an xsl:namespace-alias. */
-    xsltNsAliasPtr nsAliases;
+    xsltNsAliasPtr nsAliases;    
 };   
 
 #else /* XSLT_REFACTORED */
@@ -1275,6 +1362,7 @@ struct _xsltPrincipalStylesheetData {
     * Global list of information for [xsl:]extension-element-prefixes.
     */
     xsltPointerListPtr extElemNamespaces;
+    xsltEffectiveNsPtr effectiveNs;
 #ifdef XSLT_REFACTORED_XSLT_NSCOMP
     /*
     * Namespace name map to get rid of string comparison of namespace names.
@@ -1562,6 +1650,8 @@ struct _xsltTransformContext {
      * all document text strings are internalized
      */
     int internalized;
+    int nbKeys;
+    int hasTemplKeyPatterns;
 };
 
 /**
@@ -1715,6 +1805,16 @@ XSLTPUBFUN int XSLTCALL
 #endif
 #endif /* XSLT_REFACTORED */
 
+/************************************************************************
+ *                                                                     *
+ *  Transformation-time functions for *internal* use only               *
+ *                                                                     *
+ ************************************************************************/
+XSLTPUBFUN int XSLTCALL
+                       xsltInitCtxtKey         (xsltTransformContextPtr ctxt,
+                                                xsltDocumentPtr doc,
+                                                xsltKeyDefPtr keyd);
+
 #ifdef __cplusplus
 }
 #endif