diff --git a/content/xslt/src/main/Makefile.in b/content/xslt/src/main/Makefile.in index 92c658d95230..f7709326f9d7 100644 --- a/content/xslt/src/main/Makefile.in +++ b/content/xslt/src/main/Makefile.in @@ -117,6 +117,7 @@ OBJS =../base/ArrayList.o \ ../xslt/functions/ElementAvailableFnCall.o \ ../xslt/functions/FunctionAvailableFnCall.o \ ../xslt/functions/GenerateIdFunctionCall.o \ + ../xslt/functions/txKeyFunctionCall.o \ ../xslt/functions/SystemPropertyFunctionCall.o \ ../xslt/util/NodeSorter.o \ ../xslt/util/NodeStack.o \ diff --git a/content/xslt/src/xslt/txElementAvailableFnCall.cpp b/content/xslt/src/xslt/txElementAvailableFnCall.cpp index 41a22d2cbc90..1f495a462f24 100644 --- a/content/xslt/src/xslt/txElementAvailableFnCall.cpp +++ b/content/xslt/src/xslt/txElementAvailableFnCall.cpp @@ -81,6 +81,7 @@ ExprResult* ElementAvailableFunctionCall::evaluate(Node* context, ContextState* localName.isEqual(IF) || localName.isEqual(IMPORT) || localName.isEqual(INCLUDE) || + localName.isEqual(KEY) || localName.isEqual(MESSAGE) || localName.isEqual(NUMBER) || localName.isEqual(OTHERWISE) || diff --git a/content/xslt/src/xslt/txFunctionAvailableFnCall.cpp b/content/xslt/src/xslt/txFunctionAvailableFnCall.cpp index c3d3e854492d..1e8bd237dde4 100644 --- a/content/xslt/src/xslt/txFunctionAvailableFnCall.cpp +++ b/content/xslt/src/xslt/txFunctionAvailableFnCall.cpp @@ -91,9 +91,9 @@ ExprResult* FunctionAvailableFunctionCall::evaluate(Node* context, ContextState* property.isEqual(XPathNames::CEILING_FN) || property.isEqual(XPathNames::FLOOR_FN) || property.isEqual(DOCUMENT_FN) || - // property.isEqual(KEY_FN) || + property.isEqual(KEY_FN) || // property.isEqual(FORMAT_NUMBER_FN) || - // property.isEqual(CURRENT_FN) || + property.isEqual(CURRENT_FN) || // property.isEqual(UNPARSED_ENTITY_URI_FN) || property.isEqual(GENERATE_ID_FN) || property.isEqual(SYSTEM_PROPERTY_FN) || diff --git a/content/xslt/src/xslt/txKeyFunctionCall.cpp b/content/xslt/src/xslt/txKeyFunctionCall.cpp new file mode 100644 index 000000000000..b3da55d58ba3 --- /dev/null +++ b/content/xslt/src/xslt/txKeyFunctionCall.cpp @@ -0,0 +1,257 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Initial Developer of the Original Code is Jonas Sicking. + * Portions created by Jonas Sicking are Copyright (C) 2001, Jonas Sicking. + * All rights reserved. + * + * Contributor(s): + * Jonas Sicking, sicking@bigfoot.com + * -- original author. + */ + +#include "XSLTFunctions.h" +#include "Names.h" +#include "XMLDOMUtils.h" + +/* + * txKeyFunctionCall + * A representation of the XSLT additional function: key() + */ + +/* + * Creates a new key function call + */ +txKeyFunctionCall::txKeyFunctionCall(ProcessorState* aPs) : + FunctionCall(KEY_FN) +{ + mProcessorState = aPs; +} + +/* + * Evaluates a key() xslt-function call. First argument is name of key + * to use, second argument is value to look up. + * @param aContext the context node for evaluation of this Expr + * @param aCs the ContextState containing the stack information needed + * for evaluation + * @return the result of the evaluation + */ +ExprResult* txKeyFunctionCall::evaluate(Node* aContext, ContextState* aCs) +{ + NodeSet* res = new NodeSet; + if (!res) { + // ErrorReport: out of memory + return NULL; + } + + if (!requireParams(2, 2, aCs)) + return res; + + ListIterator iter(¶ms); + String keyName; + evaluateToString((Expr*)iter.next(), aContext, aCs, keyName); + Expr* param = (Expr*) iter.next(); + + txXSLKey* key = mProcessorState->getKey(keyName); + if (!key) { + String err("No key with that name in: "); + toString(err); + aCs->recieveError(err); + return res; + } + + ExprResult* exprResult = param->evaluate(aContext, aCs); + if (!exprResult) + return res; + + if (exprResult->getResultType() == ExprResult::NODESET) { + NodeSet* nodeSet = (NodeSet*) exprResult; + for (int i=0; isize(); i++) { + String val; + XMLDOMUtils::getNodeValue(nodeSet->get(i), &val); + key->getNodes(val,aContext->getOwnerDocument())->copyInto(*res); + } + } + else { + String val; + exprResult->stringValue(val); + key->getNodes(val,aContext->getOwnerDocument())->copyInto(*res); + } + delete exprResult; + return res; + +} // evaluate + +/* + * Class representing an . Or in the case where several s + * have the same name one object represents all s with that name + */ + +txXSLKey::txXSLKey(ProcessorState* aPs) +{ + mProcessorState = aPs; + mMaps.setOwnership(Map::eOwnsItems); +} // txXSLKey + +txXSLKey::~txXSLKey() +{ + ListIterator iter(&mKeys); + while (iter.hasNext()) { + iter.next(); + delete (Key*)iter.remove(); + } +} // ~txXSLKey + +/* + * Returns a NodeSet containing all nodes within the specified document + * that have the value keyValue. The document is indexed in case it + * hasn't been searched previously. The returned nodeset is owned by + * the txXSLKey object + * @param aKeyValue Value to search for + * @param aDoc Document to search in + * @return a NodeSet* containing all nodes in doc matching with value + * keyValue + */ +const NodeSet* txXSLKey::getNodes(String& aKeyValue, Document* aDoc) +{ + NamedMap* map = (NamedMap*)mMaps.get(aDoc); + if (!map) { + map = addDocument(aDoc); + if (!map) + return &mEmptyNodeset; + } + + NodeSet* nodes = (NodeSet*)map->get(aKeyValue); + if (!nodes) + return &mEmptyNodeset; + + return nodes; +} // getNodes + +/* + * Adds a match/use pair. Returns MB_FALSE if matchString or useString + * can't be parsed. + * @param aMatchString String to be parsed as match-pattern + * @param aUseString String to be parsed as use-expression + * @return MB_FALSE if matchString or useString can't be parsed + * MB_TRUE otherwise + */ +MBool txXSLKey::addKey(const String& aMatchString, const String& aUseString) +{ + Key* key = new Key; + if (!key) + return MB_FALSE; + + key->matchPattern = mProcessorState->getPatternExpr(aMatchString); + key->useExpr = mProcessorState->getExpr(aUseString); + if (!key->matchPattern || !key->useExpr) { + delete key; + return MB_FALSE; + } + mKeys.add(key); + return MB_TRUE; +} // addKey + +/* + * Indexes a document and adds it to the set of indexed documents + * @param aDoc Document to index and add + * @returns a NamedMap* containing the index + */ +NamedMap* txXSLKey::addDocument(Document* aDoc) +{ + NamedMap* map = new NamedMap; + if (!map) + return NULL; + map->setObjectDeletion(MB_TRUE); + mMaps.put(aDoc, map); + indexTree(aDoc, map); + return map; +} // addDocument + +/* + * Recursively searches a node, its attributes and its subtree for + * nodes matching any of the keys match-patterns. + * @param aNode node to search + * @param aMap index to add search result in + */ +void txXSLKey::indexTree(Node* aNode, NamedMap* aMap) +{ + testNode(aNode, aMap); + + // check if the nodes attributes matches + NamedNodeMap* attrs = aNode->getAttributes(); + if (attrs) { + for (UInt32 i=0; igetLength(); i++) { + testNode(attrs->item(i), aMap); + } + } + + Node* child = aNode->getFirstChild(); + while (child) { + indexTree(child, aMap); + child = child->getNextSibling(); + } +} // indexTree + +/* + * Tests one node if it matches any of the keys match-patterns. If + * the node matches its values are added to the index. + * @param aNode node to test + * @param aMap index to add values to + */ +void txXSLKey::testNode(Node* aNode, NamedMap* aMap) +{ + String val; + NodeSet *nodeSet; + + ListIterator iter(&mKeys); + while (iter.hasNext()) + { + Key* key=(Key*)iter.next(); + if (key->matchPattern->matches(aNode, 0, mProcessorState)) { + NodeSet contextNodeSet; + contextNodeSet.add(aNode); + mProcessorState->getNodeSetStack()->push(&contextNodeSet); + mProcessorState->pushCurrentNode(aNode); + ExprResult* exprResult = key->useExpr->evaluate(aNode, mProcessorState); + mProcessorState->popCurrentNode(); + mProcessorState->getNodeSetStack()->pop(); + if (exprResult->getResultType() == ExprResult::NODESET) { + NodeSet* res = (NodeSet*)exprResult; + for (int i=0; isize(); i++) { + val.clear(); + XMLDOMUtils::getNodeValue(res->get(i), &val); + + nodeSet = (NodeSet*)aMap->get(val); + if (!nodeSet) { + nodeSet = new NodeSet; + if (!nodeSet) + return; + aMap->put(val, nodeSet); + } + nodeSet->add(aNode); + } + } + else { + exprResult->stringValue(val); + nodeSet = (NodeSet*)aMap->get(val); + if (!nodeSet) { + nodeSet = new NodeSet; + if (!nodeSet) + return; + aMap->put(val, nodeSet); + } + nodeSet->add(aNode); + } + delete exprResult; + } + } +} // testNode diff --git a/content/xslt/src/xslt/txXSLTFunctions.h b/content/xslt/src/xslt/txXSLTFunctions.h index 7bc5ba537f3d..4d4635e72b16 100644 --- a/content/xslt/src/xslt/txXSLTFunctions.h +++ b/content/xslt/src/xslt/txXSLTFunctions.h @@ -23,7 +23,10 @@ * Olivier Gerardin, * -- added document() function definition * - * $Id: txXSLTFunctions.h,v 1.6 2005/11/02 07:33:59 peterv%netscape.com Exp $ + * Jonas Sicking, + * -- added txXSLKey class + * + * $Id: txXSLTFunctions.h,v 1.7 2005/11/02 07:34:00 sicking%bigfoot.com Exp $ */ #ifndef TRANSFRMX_XSLT_FUNCTIONS_H @@ -34,6 +37,8 @@ #include "DOMHelper.h" #include "TxString.h" #include "ProcessorState.h" +#include "Map.h" +#include "List.h" /** * The definition for the XSLT document() function @@ -63,29 +68,118 @@ private: ProcessorState* processorState; }; -/** +/* * The definition for the XSLT key() function -**/ -class KeyFunctionCall : public FunctionCall { + */ +class txKeyFunctionCall : public FunctionCall { public: - /** + /* * Creates a new key() function call - **/ - KeyFunctionCall(); + */ + txKeyFunctionCall(ProcessorState* aPs); - /** - * Evaluates this Expr based on the given context node and processor state - * @param context the context node for evaluation of this Expr - * @param cs the ContextState containing the stack information needed - * for evaluation + /* + * Evaluates a key() xslt-functioncall. First argument is name of key + * to use, second argument is value to look up. + * @param aContext the context node for evaluation of this Expr + * @param aCs the ContextState containing the stack information needed + * for evaluation * @return the result of the evaluation - * @see FunctionCall.h - **/ - virtual ExprResult* evaluate(Node* context, ContextState* cs); + */ + virtual ExprResult* evaluate(Node* aContext, ContextState* aCs); private: + ProcessorState* mProcessorState; +}; + +/* + * Class representing an . Or in the case where several s + * have the same name one object represents all s with that name + */ +class txXSLKey : public TxObject { + +public: + txXSLKey(ProcessorState* aPs); + ~txXSLKey(); + + /* + * Returns a NodeSet containing all nodes within the specified document + * that have the value keyValue. The document is indexed in case it + * hasn't been searched previously. The returned nodeset is owned by + * the txXSLKey object + * @param aKeyValue Value to search for + * @param aDoc Document to search in + * @return a NodeSet* containing all nodes in doc matching with value + * keyValue + */ + const NodeSet* getNodes(String& aKeyValue, Document* aDoc); + + /* + * Adds a match/use pair. Returns MB_FALSE if matchString or useString + * can't be parsed. + * @param aMatchString String to be parsed as match-pattern + * @param aUseString String to be parsed as use-expression + * @return MB_FALSE if matchString or useString can't be parsed + * MB_TRUE otherwise + */ + MBool addKey(const String& aMatchString, const String& aUseString); + +private: + /* + * Indexes a document and adds it to the set of indexed documents + * @param aDoc Document to index and add + * @returns a NamedMap* containing the index + */ + NamedMap* addDocument(Document* aDoc); + + /* + * Recursively searches a node, its attributes and its subtree for + * nodes matching any of the keys match-patterns. + * @param aNode node to search + * @param aMap index to add search result in + */ + void indexTree(Node* aNode, NamedMap* aMap); + + /* + * Tests one node if it matches any of the keys match-patterns. If + * the node matches its values are added to the index. + * @param aNode node to test + * @param aMap index to add values to + */ + void testNode(Node* aNode, NamedMap* aMap); + + /* + * represents one match/use pair + */ + struct Key { + Pattern* matchPattern; + Expr* useExpr; + }; + + /* + * List of all match/use pairs + */ + List mKeys; + + /* + * Map containing all indexes (keyed on document). Every index is a + * NamedMap. Every NamedMap contains NodeLists with the nodes for + * a certain value + */ + Map mMaps; + + /* + * ProcessorState used to parse the match-patterns and + * use-expressions + */ + ProcessorState* mProcessorState; + + /* + * Used to return empty nodeset + */ + NodeSet mEmptyNodeset; }; /**