/** * xsltInitCtxtKey: * @ctxt: an XSLT transformation context * @idoc: the document information (holds key values) * @keyDef: the key definition * * Computes the key tables this key and for the current input document. * * Returns: 0 on success, -1 on error */ int xsltInitCtxtKey(xsltTransformContextPtr ctxt, xsltDocumentPtr idoc, xsltKeyDefPtr keyDef) { int i, len, k; xmlNodeSetPtr matchList = NULL, keylist; xmlXPathObjectPtr matchRes = NULL, useRes = NULL; xmlChar *str = NULL; xsltKeyTablePtr table; xmlNodePtr oldInst, cur; xmlNodePtr oldContextNode; xsltDocumentPtr oldDocInfo; int oldXPPos, oldXPSize; xmlDocPtr oldXPDoc; int oldXPNsNr; xmlNsPtr *oldXPNamespaces; xmlXPathContextPtr xpctxt; #ifdef KEY_INIT_DEBUG fprintf(stderr, "xsltInitCtxtKey %s : %d\n", keyDef->name, ctxt->keyInitLevel); #endif if ((keyDef->comp == NULL) || (keyDef->usecomp == NULL)) return(-1); /* * Detect recursive keys */ if (ctxt->keyInitLevel > ctxt->nbKeys) { #ifdef WITH_XSLT_DEBUG_KEYS XSLT_TRACE(ctxt,XSLT_TRACE_KEYS, xsltGenericDebug(xsltGenericDebugContext, "xsltInitCtxtKey: key definition of %s is recursive\n", keyDef->name)); #endif xsltTransformError(ctxt, NULL, keyDef->inst, "Key definition for %s is recursive\n", keyDef->name); ctxt->state = XSLT_STATE_STOPPED; return(-1); } ctxt->keyInitLevel++; xpctxt = ctxt->xpathCtxt; idoc->nbKeysComputed++; /* * Save context state. */ oldInst = ctxt->inst; oldDocInfo = ctxt->document; oldContextNode = ctxt->node; oldXPDoc = xpctxt->doc; oldXPPos = xpctxt->proximityPosition; oldXPSize = xpctxt->contextSize; oldXPNsNr = xpctxt->nsNr; oldXPNamespaces = xpctxt->namespaces; /* * Set up contexts. */ ctxt->document = idoc; ctxt->node = (xmlNodePtr) idoc->doc; ctxt->inst = keyDef->inst; xpctxt->doc = idoc->doc; xpctxt->node = (xmlNodePtr) idoc->doc; /* TODO : clarify the use of namespaces in keys evaluation */ xpctxt->namespaces = keyDef->nsList; xpctxt->nsNr = keyDef->nsNr; /* * Evaluate the 'match' expression of the xsl:key. * TODO: The 'match' is a *pattern*. */ matchRes = xmlXPathCompiledEval(keyDef->comp, xpctxt); if (matchRes == NULL) { #ifdef WITH_XSLT_DEBUG_KEYS XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, "xsltInitCtxtKey: %s evaluation failed\n", keyDef->match)); #endif xsltTransformError(ctxt, NULL, keyDef->inst, "Failed to evaluate the 'match' expression.\n"); ctxt->state = XSLT_STATE_STOPPED; goto error; } else { if (matchRes->type == XPATH_NODESET) { matchList = matchRes->nodesetval; #ifdef WITH_XSLT_DEBUG_KEYS if (matchList != NULL) XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, "xsltInitCtxtKey: %s evaluates to %d nodes\n", keyDef->match, matchList->nodeNr)); #endif } else { /* * Is not a node set, but must be. */ #ifdef WITH_XSLT_DEBUG_KEYS XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, "xsltInitCtxtKey: %s is not a node set\n", keyDef->match)); #endif xsltTransformError(ctxt, NULL, keyDef->inst, "The 'match' expression did not evaluate to a node set.\n"); ctxt->state = XSLT_STATE_STOPPED; goto error; } } if ((matchList == NULL) || (matchList->nodeNr <= 0)) goto exit; /** * Multiple key definitions for the same name are allowed, so * we must check if the key is already present for this doc */ table = (xsltKeyTablePtr) idoc->keys; while (table != NULL) { if (xmlStrEqual(table->name, keyDef->name) && (((keyDef->nameURI == NULL) && (table->nameURI == NULL)) || ((keyDef->nameURI != NULL) && (table->nameURI != NULL) && (xmlStrEqual(table->nameURI, keyDef->nameURI))))) break; table = table->next; } /** * If the key was not previously defined, create it now and * chain it to the list of keys for the doc */ if (table == NULL) { table = xsltNewKeyTable(keyDef->name, keyDef->nameURI); if (table == NULL) goto error; table->next = idoc->keys; idoc->keys = table; } /* * SPEC XSLT 1.0 (XSLT 2.0 does not clarify the context size!) * "...the use attribute of the xsl:key element is evaluated with x as " the current node and with a node list containing just x as the * current node list" */ xpctxt->contextSize = 1; xpctxt->proximityPosition = 1; for (i = 0; i < matchList->nodeNr; i++) { cur = matchList->nodeTab[i]; if (! IS_XSLT_REAL_NODE(cur)) continue; xpctxt->node = cur; /* * Process the 'use' of the xsl:key. * SPEC XSLT 1.0: * "The use attribute is an expression specifying the values of * the key; the expression is evaluated once for each node that * matches the pattern." */ if (useRes != NULL) xmlXPathFreeObject(useRes); useRes = xmlXPathCompiledEval(keyDef->usecomp, xpctxt); if (useRes == NULL) { xsltTransformError(ctxt, NULL, keyDef->inst, "Failed to evaluate the 'use' expression.\n"); ctxt->state = XSLT_STATE_STOPPED; break; } if (useRes->type == XPATH_NODESET) { if ((useRes->nodesetval != NULL) && (useRes->nodesetval->nodeNr != 0)) { len = useRes->nodesetval->nodeNr; str = xmlXPathCastNodeToString(useRes->nodesetval->nodeTab[0]); } else { continue; } } else { len = 1; if (useRes->type == XPATH_STRING) { /* * Consume the string value. */ str = useRes->stringval; useRes->stringval = NULL; } else { str = xmlXPathCastToString(useRes); } } /* * Process all strings. */ k = 0; while (1) { if (str == NULL) goto next_string; #ifdef WITH_XSLT_DEBUG_KEYS XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, "xsl:key : node associated to ('%s', '%s')\n", keyDef->name, str)); #endif keylist = xmlHashLookup(table->keys, str); if (keylist == NULL) { keylist = xmlXPathNodeSetCreate(cur); if (keylist == NULL) goto error; xmlHashAddEntry(table->keys, str, keylist); } else { /* * TODO: How do we know if this function failed? */ xmlXPathNodeSetAdd(keylist, cur); } switch (cur->type) { case XML_ELEMENT_NODE: case XML_TEXT_NODE: case XML_CDATA_SECTION_NODE: case XML_PI_NODE: case XML_COMMENT_NODE: cur->psvi = keyDef; break; case XML_ATTRIBUTE_NODE: ((xmlAttrPtr) cur)->psvi = keyDef; break; case XML_DOCUMENT_NODE: case XML_HTML_DOCUMENT_NODE: ((xmlDocPtr) cur)->psvi = keyDef; break; default: break; } xmlFree(str); str = NULL; next_string: k++; if (k >= len) break; str = xmlXPathCastNodeToString(useRes->nodesetval->nodeTab[k]); } } exit: error: ctxt->keyInitLevel--; /* * Restore context state. */ xpctxt->doc = oldXPDoc; xpctxt->nsNr = oldXPNsNr; xpctxt->namespaces = oldXPNamespaces; xpctxt->proximityPosition = oldXPPos; xpctxt->contextSize = oldXPSize; ctxt->node = oldContextNode; ctxt->document = oldDocInfo; ctxt->inst = oldInst; if (str) xmlFree(str); if (useRes != NULL) xmlXPathFreeObject(useRes); if (matchRes != NULL) xmlXPathFreeObject(matchRes); return(0); }
/** * xsltDocumentFunction: * @ctxt: the XPath Parser context * @nargs: the number of arguments * * Implement the document() XSLT function * node-set document(object, node-set?) */ void xsltDocumentFunction(xmlXPathParserContextPtr ctxt, int nargs){ xsltDocumentPtr doc; xmlXPathObjectPtr obj, obj2 = NULL; xmlChar *base = NULL, *URI; if ((nargs < 1) || (nargs > 2)) { xsltPrintErrorContext(xsltXPathGetTransformContext(ctxt), NULL, NULL); xsltGenericError(xsltGenericErrorContext, "document() : invalid number of args %d\n", nargs); ctxt->error = XPATH_INVALID_ARITY; return; } if (ctxt->value == NULL) { xsltPrintErrorContext(xsltXPathGetTransformContext(ctxt), NULL, NULL); xsltGenericError(xsltGenericErrorContext, "document() : invalid arg value\n"); ctxt->error = XPATH_INVALID_TYPE; return; } if (nargs == 2) { if (ctxt->value->type != XPATH_NODESET) { xsltPrintErrorContext(xsltXPathGetTransformContext(ctxt), NULL, NULL); xsltGenericError(xsltGenericErrorContext, "document() : invalid arg expecting a nodeset\n"); ctxt->error = XPATH_INVALID_TYPE; return; } obj2 = valuePop(ctxt); } if (ctxt->value->type == XPATH_NODESET) { int i; xmlXPathObjectPtr newobj, ret; obj = valuePop(ctxt); ret = xmlXPathNewNodeSet(NULL); if (obj->nodesetval) { for (i = 0; i < obj->nodesetval->nodeNr; i++) { valuePush(ctxt, xmlXPathNewNodeSet(obj->nodesetval->nodeTab[i])); xmlXPathStringFunction(ctxt, 1); if (nargs == 2) { valuePush(ctxt, xmlXPathObjectCopy(obj2)); } else { valuePush(ctxt, xmlXPathNewNodeSet(obj->nodesetval->nodeTab[i])); } xsltDocumentFunction(ctxt, 2); newobj = valuePop(ctxt); ret->nodesetval = xmlXPathNodeSetMerge(ret->nodesetval, newobj->nodesetval); xmlXPathFreeObject(newobj); } } xmlXPathFreeObject(obj); if (obj2 != NULL) xmlXPathFreeObject(obj2); valuePush(ctxt, ret); return; } /* * Make sure it's converted to a string */ xmlXPathStringFunction(ctxt, 1); if (ctxt->value->type != XPATH_STRING) { xsltPrintErrorContext(xsltXPathGetTransformContext(ctxt), NULL, NULL); xsltGenericError(xsltGenericErrorContext, "document() : invalid arg expecting a string\n"); ctxt->error = XPATH_INVALID_TYPE; if (obj2 != NULL) xmlXPathFreeObject(obj2); return; } obj = valuePop(ctxt); if (obj->stringval == NULL) { valuePush(ctxt, xmlXPathNewNodeSet(NULL)); } else { if ((obj2 != NULL) && (obj2->nodesetval != NULL) && (obj2->nodesetval->nodeNr > 0) && IS_XSLT_REAL_NODE(obj2->nodesetval->nodeTab[0])) { xmlNodePtr target; target = obj2->nodesetval->nodeTab[0]; if (target->type == XML_ATTRIBUTE_NODE) { target = ((xmlAttrPtr) target)->parent; } base = xmlNodeGetBase(target->doc, target); } else { xsltTransformContextPtr tctxt; tctxt = xsltXPathGetTransformContext(ctxt); if ((tctxt != NULL) && (tctxt->inst != NULL)) { base = xmlNodeGetBase(tctxt->inst->doc, tctxt->inst); } else if ((tctxt != NULL) && (tctxt->style != NULL) && (tctxt->style->doc != NULL)) { base = xmlNodeGetBase(tctxt->style->doc, (xmlNodePtr) tctxt->style->doc); } } URI = xmlBuildURI(obj->stringval, base); if (base != NULL) xmlFree(base); if (URI == NULL) { valuePush(ctxt, xmlXPathNewNodeSet(NULL)); } else { xsltTransformContextPtr tctxt; tctxt = xsltXPathGetTransformContext(ctxt); if (tctxt == NULL) { xsltPrintErrorContext(xsltXPathGetTransformContext(ctxt), NULL, NULL); xsltGenericError(xsltGenericErrorContext, "document() : internal error tctxt == NULL\n"); valuePush(ctxt, xmlXPathNewNodeSet(NULL)); } else { if (xmlStrEqual(tctxt->style->doc->URL, URI)) { valuePush(ctxt, xmlXPathNewNodeSet((xmlNodePtr)tctxt->style->doc)); } else { doc = xsltLoadDocument(tctxt, URI); if (doc == NULL) valuePush(ctxt, xmlXPathNewNodeSet(NULL)); else { /* TODO: use XPointer of HTML location for fragment ID */ /* pbm #xxx can lead to location sets, not nodesets :-) */ valuePush(ctxt, xmlXPathNewNodeSet((xmlNodePtr) doc->doc)); } } } xmlFree(URI); } } xmlXPathFreeObject(obj); if (obj2 != NULL) xmlXPathFreeObject(obj2); }
/** * xsltDocumentFunction: * @ctxt: the XPath Parser context * @nargs: the number of arguments * * Implement the document() XSLT function * node-set document(object, node-set?) */ void xsltDocumentFunction(xmlXPathParserContextPtr ctxt, int nargs) { xmlXPathObjectPtr obj, obj2 = NULL; xmlChar *base = NULL, *URI; if ((nargs < 1) || (nargs > 2)) { xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, "document() : invalid number of args %d\n", nargs); ctxt->error = XPATH_INVALID_ARITY; return; } if (ctxt->value == NULL) { xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, "document() : invalid arg value\n"); ctxt->error = XPATH_INVALID_TYPE; return; } if (nargs == 2) { if (ctxt->value->type != XPATH_NODESET) { xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, "document() : invalid arg expecting a nodeset\n"); ctxt->error = XPATH_INVALID_TYPE; return; } obj2 = valuePop(ctxt); } if (ctxt->value->type == XPATH_NODESET) { int i; xmlXPathObjectPtr newobj, ret; obj = valuePop(ctxt); ret = xmlXPathNewNodeSet(NULL); if ((obj != NULL) && obj->nodesetval) { for (i = 0; i < obj->nodesetval->nodeNr; i++) { valuePush(ctxt, xmlXPathNewNodeSet(obj->nodesetval->nodeTab[i])); xmlXPathStringFunction(ctxt, 1); if (nargs == 2) { valuePush(ctxt, xmlXPathObjectCopy(obj2)); } else { valuePush(ctxt, xmlXPathNewNodeSet(obj->nodesetval-> nodeTab[i])); } xsltDocumentFunction(ctxt, 2); newobj = valuePop(ctxt); ret->nodesetval = xmlXPathNodeSetMerge(ret->nodesetval, newobj->nodesetval); xmlXPathFreeObject(newobj); } } if (obj != NULL) xmlXPathFreeObject(obj); if (obj2 != NULL) xmlXPathFreeObject(obj2); valuePush(ctxt, ret); return; } /* * Make sure it's converted to a string */ xmlXPathStringFunction(ctxt, 1); if (ctxt->value->type != XPATH_STRING) { xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, "document() : invalid arg expecting a string\n"); ctxt->error = XPATH_INVALID_TYPE; if (obj2 != NULL) xmlXPathFreeObject(obj2); return; } obj = valuePop(ctxt); if (obj->stringval == NULL) { valuePush(ctxt, xmlXPathNewNodeSet(NULL)); } else { xsltTransformContextPtr tctxt; tctxt = xsltXPathGetTransformContext(ctxt); if ((obj2 != NULL) && (obj2->nodesetval != NULL) && (obj2->nodesetval->nodeNr > 0) && IS_XSLT_REAL_NODE(obj2->nodesetval->nodeTab[0])) { xmlNodePtr target; target = obj2->nodesetval->nodeTab[0]; if ((target->type == XML_ATTRIBUTE_NODE) || (target->type == XML_PI_NODE)) { target = ((xmlAttrPtr) target)->parent; } base = xmlNodeGetBase(target->doc, target); } else { if ((tctxt != NULL) && (tctxt->inst != NULL)) { base = xmlNodeGetBase(tctxt->inst->doc, tctxt->inst); } else if ((tctxt != NULL) && (tctxt->style != NULL) && (tctxt->style->doc != NULL)) { base = xmlNodeGetBase(tctxt->style->doc, (xmlNodePtr) tctxt->style->doc); } } URI = xmlBuildURI(obj->stringval, base); if (base != NULL) xmlFree(base); if (URI == NULL) { if ((tctxt != NULL) && (tctxt->style != NULL) && (tctxt->style->doc != NULL) && (xmlStrEqual(URI, tctxt->style->doc->URL))) { /* This selects the stylesheet's doc itself. */ valuePush(ctxt, xmlXPathNewNodeSet((xmlNodePtr) tctxt->style->doc)); } else { valuePush(ctxt, xmlXPathNewNodeSet(NULL)); } } else { xsltDocumentFunctionLoadDocument( ctxt, URI ); xmlFree(URI); } } xmlXPathFreeObject(obj); if (obj2 != NULL) xmlXPathFreeObject(obj2); }
/** * xsltInitCtxtKey: * @ctxt: an XSLT transformation context * @doc: an XSLT document * @keyd: the key definition * * Computes the key tables this key and for the current input document. */ int xsltInitCtxtKey(xsltTransformContextPtr ctxt, xsltDocumentPtr doc, xsltKeyDefPtr keyd) { int i; xmlNodeSetPtr nodelist = NULL, keylist; xmlXPathObjectPtr res = NULL; xmlChar *str, **list; xsltKeyTablePtr table; int oldPos, oldSize; xmlNodePtr oldInst; xmlNodePtr oldNode; xsltDocumentPtr oldDoc; xmlDocPtr oldXDoc; int oldNsNr; xmlNsPtr *oldNamespaces; doc->nbKeysComputed++; /* * Evaluate the nodelist */ oldXDoc= ctxt->xpathCtxt->doc; oldPos = ctxt->xpathCtxt->proximityPosition; oldSize = ctxt->xpathCtxt->contextSize; oldNsNr = ctxt->xpathCtxt->nsNr; oldNamespaces = ctxt->xpathCtxt->namespaces; oldInst = ctxt->inst; oldDoc = ctxt->document; oldNode = ctxt->node; if (keyd->comp == NULL) goto error; if (keyd->usecomp == NULL) goto error; ctxt->document = doc; ctxt->xpathCtxt->doc = doc->doc; ctxt->xpathCtxt->node = (xmlNodePtr) doc->doc; ctxt->node = (xmlNodePtr) doc->doc; /* TODO : clarify the use of namespaces in keys evaluation */ ctxt->xpathCtxt->namespaces = keyd->nsList; ctxt->xpathCtxt->nsNr = keyd->nsNr; ctxt->inst = keyd->inst; res = xmlXPathCompiledEval(keyd->comp, ctxt->xpathCtxt); ctxt->xpathCtxt->contextSize = oldSize; ctxt->xpathCtxt->proximityPosition = oldPos; ctxt->inst = oldInst; if (res != NULL) { if (res->type == XPATH_NODESET) { nodelist = res->nodesetval; #ifdef WITH_XSLT_DEBUG_KEYS if (nodelist != NULL) XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, "xsltInitCtxtKey: %s evaluates to %d nodes\n", keyd->match, nodelist->nodeNr)); #endif } else { #ifdef WITH_XSLT_DEBUG_KEYS XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, "xsltInitCtxtKey: %s is not a node set\n", keyd->match)); #endif goto error; } } else { #ifdef WITH_XSLT_DEBUG_KEYS XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, "xsltInitCtxtKey: %s evaluation failed\n", keyd->match)); #endif ctxt->state = XSLT_STATE_STOPPED; goto error; } /* * for each node in the list evaluate the key and insert the node */ if ((nodelist == NULL) || (nodelist->nodeNr <= 0)) goto error; /** * Multiple key definitions for the same name are allowed, so * we must check if the key is already present for this doc */ table = (xsltKeyTablePtr) doc->keys; while (table != NULL) { if (xmlStrEqual(table->name, keyd->name) && (((keyd->nameURI == NULL) && (table->nameURI == NULL)) || ((keyd->nameURI != NULL) && (table->nameURI != NULL) && (xmlStrEqual(table->nameURI, keyd->nameURI))))) break; table = table->next; } /** * If the key was not previously defined, create it now and * chain it to the list of keys for the doc */ if (table == NULL) { table = xsltNewKeyTable(keyd->name, keyd->nameURI); if (table == NULL) goto error; table->next = doc->keys; doc->keys = table; } for (i = 0;i < nodelist->nodeNr;i++) { if (IS_XSLT_REAL_NODE(nodelist->nodeTab[i])) { ctxt->node = nodelist->nodeTab[i]; list = xsltEvalXPathKeys(ctxt, keyd->usecomp, keyd); if (list != NULL) { int ix = 0; str = list[ix++]; while (str != NULL) { #ifdef WITH_XSLT_DEBUG_KEYS XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, "xsl:key : node associated to(%s,%s)\n", keyd->name, str)); #endif keylist = xmlHashLookup(table->keys, str); if (keylist == NULL) { keylist = xmlXPathNodeSetCreate(nodelist->nodeTab[i]); xmlHashAddEntry(table->keys, str, keylist); } else { xmlXPathNodeSetAdd(keylist, nodelist->nodeTab[i]); } switch (nodelist->nodeTab[i]->type) { case XML_ELEMENT_NODE: case XML_TEXT_NODE: case XML_CDATA_SECTION_NODE: case XML_PI_NODE: case XML_COMMENT_NODE: nodelist->nodeTab[i]->psvi = keyd; break; case XML_ATTRIBUTE_NODE: { xmlAttrPtr attr = (xmlAttrPtr) nodelist->nodeTab[i]; attr->psvi = keyd; break; } case XML_DOCUMENT_NODE: case XML_HTML_DOCUMENT_NODE: { xmlDocPtr kdoc = (xmlDocPtr) nodelist->nodeTab[i]; kdoc->psvi = keyd; break; } default: break; } xmlFree(str); str = list[ix++]; } xmlFree(list); #ifdef WITH_XSLT_DEBUG_KEYS } else { XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, "xsl:key : use %s failed to return strings\n", keyd->use)); #endif } } } error: ctxt->document = oldDoc; ctxt->xpathCtxt->doc = oldXDoc; ctxt->xpathCtxt->nsNr = oldNsNr; ctxt->xpathCtxt->namespaces = oldNamespaces; ctxt->node = oldNode; if (res != NULL) xmlXPathFreeObject(res); return(0); }