283 lines
8.0 KiB
C++
283 lines
8.0 KiB
C++
/* -*- 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 "ProcessorState.h"
|
|
#include "txAtoms.h"
|
|
#include "txSingleNodeContext.h"
|
|
#include "XMLDOMUtils.h"
|
|
#include "XSLTFunctions.h"
|
|
|
|
/*
|
|
* txKeyFunctionCall
|
|
* A representation of the XSLT additional function: key()
|
|
*/
|
|
|
|
/*
|
|
* Creates a new key function call
|
|
*/
|
|
txKeyFunctionCall::txKeyFunctionCall(ProcessorState* aPs,
|
|
Node* aQNameResolveNode)
|
|
: mProcessorState(aPs),
|
|
mQNameResolveNode(aQNameResolveNode)
|
|
{
|
|
}
|
|
|
|
/*
|
|
* 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(txIEvalContext* aContext)
|
|
{
|
|
if (!aContext || !requireParams(2, 2, aContext))
|
|
return new StringResult(NS_LITERAL_STRING("error"));
|
|
|
|
NodeSet* res = new NodeSet;
|
|
if (!res) {
|
|
// ErrorReport: out of memory
|
|
return 0;
|
|
}
|
|
|
|
txListIterator iter(¶ms);
|
|
String keyQName;
|
|
evaluateToString((Expr*)iter.next(), aContext, keyQName);
|
|
Expr* param = (Expr*) iter.next();
|
|
|
|
txExpandedName keyName;
|
|
txXSLKey* key = 0;
|
|
nsresult rv = keyName.init(keyQName, mQNameResolveNode, MB_FALSE);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
key = mProcessorState->getKey(keyName);
|
|
}
|
|
|
|
if (!key) {
|
|
String err(NS_LITERAL_STRING("No key with that name in: "));
|
|
toString(err);
|
|
aContext->receiveError(err, NS_ERROR_INVALID_ARG);
|
|
return res;
|
|
}
|
|
|
|
ExprResult* exprResult = param->evaluate(aContext);
|
|
if (!exprResult)
|
|
return res;
|
|
|
|
Document* contextDoc;
|
|
Node* contextNode = aContext->getContextNode();
|
|
if (contextNode->getNodeType() == Node::DOCUMENT_NODE)
|
|
contextDoc = (Document*)contextNode;
|
|
else
|
|
contextDoc = contextNode->getOwnerDocument();
|
|
|
|
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);
|
|
res->add(key->getNodes(val, contextDoc));
|
|
}
|
|
}
|
|
else {
|
|
String val;
|
|
exprResult->stringValue(val);
|
|
res->append(key->getNodes(val, contextDoc));
|
|
}
|
|
delete exprResult;
|
|
return res;
|
|
|
|
} // evaluate
|
|
|
|
nsresult txKeyFunctionCall::getNameAtom(txAtom** aAtom)
|
|
{
|
|
*aAtom = txXSLTAtoms::key;
|
|
TX_ADDREF_ATOM(*aAtom);
|
|
return NS_OK;
|
|
}
|
|
|
|
/*
|
|
* 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()
|
|
{
|
|
txListIterator iter(&mKeys);
|
|
Key* key;
|
|
while ((key = (Key*)iter.next())) {
|
|
delete key->matchPattern;
|
|
delete key->useExpr;
|
|
delete key;
|
|
}
|
|
} // ~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)
|
|
{
|
|
NS_ASSERTION(aDoc, "missing document");
|
|
if (!aDoc)
|
|
return &mEmptyNodeset;
|
|
|
|
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 aMatch match-pattern
|
|
* @param aUse use-expression
|
|
* @return MB_FALSE if an error occured, MB_TRUE otherwise
|
|
*/
|
|
MBool txXSLKey::addKey(txPattern* aMatch, Expr* aUse)
|
|
{
|
|
if (!aMatch || !aUse)
|
|
return MB_FALSE;
|
|
|
|
Key* key = new Key;
|
|
if (!key)
|
|
return MB_FALSE;
|
|
|
|
key->matchPattern = aMatch;
|
|
key->useExpr = aUse;
|
|
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 (PRUint32 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;
|
|
|
|
txListIterator iter(&mKeys);
|
|
while (iter.hasNext())
|
|
{
|
|
Key* key=(Key*)iter.next();
|
|
if (key->matchPattern->matches(aNode, mProcessorState)) {
|
|
txSingleNodeContext evalContext(aNode, mProcessorState);
|
|
txIEvalContext* prevCon =
|
|
mProcessorState->setEvalContext(&evalContext);
|
|
ExprResult* exprResult = key->useExpr->evaluate(&evalContext);
|
|
mProcessorState->setEvalContext(prevCon);
|
|
if (exprResult->getResultType() == ExprResult::NODESET) {
|
|
NodeSet* res = (NodeSet*)exprResult;
|
|
for (int i=0; i<res->size(); i++) {
|
|
val.Truncate();
|
|
XMLDOMUtils::getNodeValue(res->get(i), val);
|
|
|
|
nodeSet = (NodeSet*)aMap->get(val);
|
|
if (!nodeSet) {
|
|
nodeSet = new NodeSet;
|
|
if (!nodeSet)
|
|
return;
|
|
aMap->put(val, nodeSet);
|
|
}
|
|
nodeSet->append(aNode);
|
|
}
|
|
}
|
|
else {
|
|
exprResult->stringValue(val);
|
|
nodeSet = (NodeSet*)aMap->get(val);
|
|
if (!nodeSet) {
|
|
nodeSet = new NodeSet;
|
|
if (!nodeSet)
|
|
return;
|
|
aMap->put(val, nodeSet);
|
|
}
|
|
nodeSet->append(aNode);
|
|
}
|
|
delete exprResult;
|
|
}
|
|
}
|
|
} // testNode
|