1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 #include "mozilla/HashFunctions.h"
10 #include "nsReadableUtils.h"
11 #include "txExecutionState.h"
13 #include "txNamespaceMap.h"
14 #include "txSingleNodeContext.h"
15 #include "txXSLTFunctions.h"
16 #include "txXSLTPatterns.h"
18 using namespace mozilla
;
22 * A representation of the XSLT additional function: key()
26 * Creates a new key function call
28 txKeyFunctionCall::txKeyFunctionCall(txNamespaceMap
* aMappings
)
29 : mMappings(aMappings
) {}
32 * Evaluates a key() xslt-function call. First argument is name of key
33 * to use, second argument is value to look up.
34 * @param aContext the context node for evaluation of this Expr
35 * @param aCs the ContextState containing the stack information needed
37 * @return the result of the evaluation
39 nsresult
txKeyFunctionCall::evaluate(txIEvalContext
* aContext
,
40 txAExprResult
** aResult
) {
41 if (!aContext
|| !requireParams(2, 2, aContext
))
42 return NS_ERROR_XPATH_BAD_ARGUMENT_COUNT
;
44 txExecutionState
* es
=
45 static_cast<txExecutionState
*>(aContext
->getPrivateContext());
47 nsAutoString keyQName
;
48 nsresult rv
= mParams
[0]->evaluateToString(aContext
, keyQName
);
49 NS_ENSURE_SUCCESS(rv
, rv
);
51 txExpandedName keyName
;
52 rv
= keyName
.init(keyQName
, mMappings
, false);
53 NS_ENSURE_SUCCESS(rv
, rv
);
55 RefPtr
<txAExprResult
> exprResult
;
56 rv
= mParams
[1]->evaluate(aContext
, getter_AddRefs(exprResult
));
57 NS_ENSURE_SUCCESS(rv
, rv
);
59 txXPathTreeWalker
walker(aContext
->getContextNode());
62 RefPtr
<txNodeSet
> res
;
64 if (exprResult
->getResultType() == txAExprResult::NODESET
&&
66 static_cast<txNodeSet
*>(static_cast<txAExprResult
*>(exprResult
)))
68 rv
= aContext
->recycler()->getNodeSet(getter_AddRefs(res
));
69 NS_ENSURE_SUCCESS(rv
, rv
);
72 for (i
= 0; i
< nodeSet
->size(); ++i
) {
74 txXPathNodeUtils::appendNodeValue(nodeSet
->get(i
), val
);
76 RefPtr
<txNodeSet
> nodes
;
77 rv
= es
->getKeyNodes(keyName
, walker
.getCurrentPosition(), val
, i
== 0,
78 getter_AddRefs(nodes
));
79 NS_ENSURE_SUCCESS(rv
, rv
);
85 exprResult
->stringValue(val
);
86 rv
= es
->getKeyNodes(keyName
, walker
.getCurrentPosition(), val
, true,
88 NS_ENSURE_SUCCESS(rv
, rv
);
97 Expr::ResultType
txKeyFunctionCall::getReturnType() { return NODESET_RESULT
; }
99 bool txKeyFunctionCall::isSensitiveTo(ContextSensitivity aContext
) {
100 return (aContext
& NODE_CONTEXT
) || argsSensitiveTo(aContext
);
104 void txKeyFunctionCall::appendName(nsAString
& aDest
) {
105 aDest
.Append(nsGkAtoms::key
->GetUTF16String());
113 bool txKeyValueHashEntry::KeyEquals(KeyTypePointer aKey
) const {
114 return mKey
.mKeyName
== aKey
->mKeyName
&&
115 mKey
.mRootIdentifier
== aKey
->mRootIdentifier
&&
116 mKey
.mKeyValue
.Equals(aKey
->mKeyValue
);
119 PLDHashNumber
txKeyValueHashEntry::HashKey(KeyTypePointer aKey
) {
120 const txKeyValueHashKey
* key
= static_cast<const txKeyValueHashKey
*>(aKey
);
122 return AddToHash(HashString(key
->mKeyValue
), key
->mKeyName
.mNamespaceID
,
123 key
->mRootIdentifier
, key
->mKeyName
.mLocalName
.get());
126 bool txIndexedKeyHashEntry::KeyEquals(KeyTypePointer aKey
) const {
127 return mKey
.mKeyName
== aKey
->mKeyName
&&
128 mKey
.mRootIdentifier
== aKey
->mRootIdentifier
;
131 PLDHashNumber
txIndexedKeyHashEntry::HashKey(KeyTypePointer aKey
) {
132 const txIndexedKeyHashKey
* key
=
133 static_cast<const txIndexedKeyHashKey
*>(aKey
);
134 return HashGeneric(key
->mKeyName
.mNamespaceID
, key
->mRootIdentifier
,
135 key
->mKeyName
.mLocalName
.get());
139 * Class managing XSLT-keys
142 nsresult
txKeyHash::getKeyNodes(const txExpandedName
& aKeyName
,
143 const txXPathNode
& aRoot
,
144 const nsAString
& aKeyValue
,
145 bool aIndexIfNotFound
, txExecutionState
& aEs
,
146 txNodeSet
** aResult
) {
149 int32_t identifier
= txXPathNodeUtils::getUniqueIdentifier(aRoot
);
151 txKeyValueHashKey
valueKey(aKeyName
, identifier
, aKeyValue
);
152 txKeyValueHashEntry
* valueEntry
= mKeyValues
.GetEntry(valueKey
);
154 *aResult
= valueEntry
->mNodeSet
;
160 // We didn't find a value. This could either mean that that key has no
161 // nodes with that value or that the key hasn't been indexed using this
164 if (!aIndexIfNotFound
) {
165 // If aIndexIfNotFound is set then the caller knows this key is
166 // indexed, so don't bother investigating.
167 *aResult
= mEmptyNodeSet
;
173 txIndexedKeyHashKey
indexKey(aKeyName
, identifier
);
174 txIndexedKeyHashEntry
* indexEntry
= mIndexedKeys
.PutEntry(indexKey
);
175 NS_ENSURE_TRUE(indexEntry
, NS_ERROR_OUT_OF_MEMORY
);
177 if (indexEntry
->mIndexed
) {
178 // The key was indexed and apparently didn't contain this value so
179 // return the empty nodeset.
180 *aResult
= mEmptyNodeSet
;
186 // The key needs to be indexed.
187 txXSLKey
* xslKey
= mKeys
.get(aKeyName
);
189 // The key didn't exist, so bail.
190 return NS_ERROR_INVALID_ARG
;
193 nsresult rv
= xslKey
->indexSubtreeRoot(aRoot
, mKeyValues
, aEs
);
194 NS_ENSURE_SUCCESS(rv
, rv
);
196 indexEntry
->mIndexed
= true;
198 // Now that the key is indexed we can get its value.
199 valueEntry
= mKeyValues
.GetEntry(valueKey
);
201 *aResult
= valueEntry
->mNodeSet
;
204 *aResult
= mEmptyNodeSet
;
211 nsresult
txKeyHash::init() {
212 mEmptyNodeSet
= new txNodeSet(nullptr);
218 * Adds a match/use pair.
219 * @param aMatch match-pattern
220 * @param aUse use-expression
221 * @return false if an error occurred, true otherwise
223 bool txXSLKey::addKey(UniquePtr
<txPattern
>&& aMatch
, UniquePtr
<Expr
>&& aUse
) {
224 if (!aMatch
|| !aUse
) return false;
226 Key
* key
= mKeys
.AppendElement();
227 if (!key
) return false;
229 key
->matchPattern
= std::move(aMatch
);
230 key
->useExpr
= std::move(aUse
);
236 * Indexes a document and adds it to the hash of key values
237 * @param aRoot Subtree root to index and add
238 * @param aKeyValueHash Hash to add values to
239 * @param aEs txExecutionState to use for XPath evaluation
241 nsresult
txXSLKey::indexSubtreeRoot(const txXPathNode
& aRoot
,
242 txKeyValueHash
& aKeyValueHash
,
243 txExecutionState
& aEs
) {
244 txKeyValueHashKey
key(mName
, txXPathNodeUtils::getUniqueIdentifier(aRoot
),
246 return indexTree(aRoot
, key
, aKeyValueHash
, aEs
);
250 * Recursively searches a node, its attributes and its subtree for
251 * nodes matching any of the keys match-patterns.
252 * @param aNode Node to search
253 * @param aKey Key to use when adding into the hash
254 * @param aKeyValueHash Hash to add values to
255 * @param aEs txExecutionState to use for XPath evaluation
257 nsresult
txXSLKey::indexTree(const txXPathNode
& aNode
, txKeyValueHashKey
& aKey
,
258 txKeyValueHash
& aKeyValueHash
,
259 txExecutionState
& aEs
) {
260 nsresult rv
= testNode(aNode
, aKey
, aKeyValueHash
, aEs
);
261 NS_ENSURE_SUCCESS(rv
, rv
);
263 // check if the node's attributes match
264 txXPathTreeWalker
walker(aNode
);
265 if (walker
.moveToFirstAttribute()) {
267 rv
= testNode(walker
.getCurrentPosition(), aKey
, aKeyValueHash
, aEs
);
268 NS_ENSURE_SUCCESS(rv
, rv
);
269 } while (walker
.moveToNextAttribute());
270 walker
.moveToParent();
273 // check if the node's descendants match
274 if (walker
.moveToFirstChild()) {
276 rv
= indexTree(walker
.getCurrentPosition(), aKey
, aKeyValueHash
, aEs
);
277 NS_ENSURE_SUCCESS(rv
, rv
);
278 } while (walker
.moveToNextSibling());
285 * Tests one node if it matches any of the keys match-patterns. If
286 * the node matches its values are added to the index.
287 * @param aNode Node to test
288 * @param aKey Key to use when adding into the hash
289 * @param aKeyValueHash Hash to add values to
290 * @param aEs txExecutionState to use for XPath evaluation
292 nsresult
txXSLKey::testNode(const txXPathNode
& aNode
, txKeyValueHashKey
& aKey
,
293 txKeyValueHash
& aKeyValueHash
,
294 txExecutionState
& aEs
) {
296 uint32_t currKey
, numKeys
= mKeys
.Length();
297 for (currKey
= 0; currKey
< numKeys
; ++currKey
) {
299 nsresult rv
= mKeys
[currKey
].matchPattern
->matches(aNode
, &aEs
, matched
);
300 NS_ENSURE_SUCCESS(rv
, rv
);
303 aEs
.pushEvalContext(new txSingleNodeContext(aNode
, &aEs
));
305 RefPtr
<txAExprResult
> exprResult
;
306 nsresult rv
= mKeys
[currKey
].useExpr
->evaluate(
307 aEs
.getEvalContext(), getter_AddRefs(exprResult
));
309 delete aEs
.popEvalContext();
310 NS_ENSURE_SUCCESS(rv
, rv
);
312 if (exprResult
->getResultType() == txAExprResult::NODESET
) {
314 static_cast<txNodeSet
*>(static_cast<txAExprResult
*>(exprResult
));
316 for (i
= 0; i
< res
->size(); ++i
) {
318 txXPathNodeUtils::appendNodeValue(res
->get(i
), val
);
320 aKey
.mKeyValue
.Assign(val
);
321 txKeyValueHashEntry
* entry
= aKeyValueHash
.PutEntry(aKey
);
322 NS_ENSURE_TRUE(entry
&& entry
->mNodeSet
, NS_ERROR_OUT_OF_MEMORY
);
324 if (entry
->mNodeSet
->isEmpty() ||
325 entry
->mNodeSet
->get(entry
->mNodeSet
->size() - 1) != aNode
) {
326 entry
->mNodeSet
->append(aNode
);
330 exprResult
->stringValue(val
);
332 aKey
.mKeyValue
.Assign(val
);
333 txKeyValueHashEntry
* entry
= aKeyValueHash
.PutEntry(aKey
);
334 NS_ENSURE_TRUE(entry
&& entry
->mNodeSet
, NS_ERROR_OUT_OF_MEMORY
);
336 if (entry
->mNodeSet
->isEmpty() ||
337 entry
->mNodeSet
->get(entry
->mNodeSet
->size() - 1) != aNode
) {
338 entry
->mNodeSet
->append(aNode
);