Implement <xsl:key> and the key() xslt function. Also extend the Map class to enable it to own either it's key-objects or it's value-objects, not just both.

b=65986, r=peterv,pike sr=jst
This commit is contained in:
sicking%bigfoot.com 2005-11-02 07:36:17 +00:00
parent ce63e32faa
commit 5bca538817
5 changed files with 370 additions and 17 deletions

View File

@ -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 \

View File

@ -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) ||

View File

@ -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) ||

View File

@ -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(&params);
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; i<nodeSet->size(); 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 <xsl:key>. Or in the case where several <xsl:key>s
* have the same name one object represents all <xsl:key>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; i<attrs->getLength(); 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; i<res->size(); 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

View File

@ -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 <xsl:key>. Or in the case where several <xsl:key>s
* have the same name one object represents all <xsl:key>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;
};
/**