Back to index

lightning-sunbird  0.9+nobinonly
txKeyFunctionCall.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is TransforMiiX XSLT processor code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Jonas Sicking.
00019  * Portions created by the Initial Developer are Copyright (C) 2001
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Jonas Sicking <sicking@bigfoot.com> (Original Author)
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either the GNU General Public License Version 2 or later (the "GPL"), or
00027  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "txExecutionState.h"
00040 #include "txAtoms.h"
00041 #include "txSingleNodeContext.h"
00042 #include "XSLTFunctions.h"
00043 #include "nsReadableUtils.h"
00044 #include "txKey.h"
00045 #include "txXSLTPatterns.h"
00046 #include "txNamespaceMap.h"
00047 
00048 /*
00049  * txKeyFunctionCall
00050  * A representation of the XSLT additional function: key()
00051  */
00052 
00053 /*
00054  * Creates a new key function call
00055  */
00056 txKeyFunctionCall::txKeyFunctionCall(txNamespaceMap* aMappings)
00057     : mMappings(aMappings)
00058 {
00059 }
00060 
00061 /*
00062  * Evaluates a key() xslt-function call. First argument is name of key
00063  * to use, second argument is value to look up.
00064  * @param aContext the context node for evaluation of this Expr
00065  * @param aCs      the ContextState containing the stack information needed
00066  *                 for evaluation
00067  * @return the result of the evaluation
00068  */
00069 nsresult
00070 txKeyFunctionCall::evaluate(txIEvalContext* aContext, txAExprResult** aResult)
00071 {
00072     if (!aContext || !requireParams(2, 2, aContext))
00073         return NS_ERROR_XPATH_BAD_ARGUMENT_COUNT;
00074 
00075     txExecutionState* es =
00076         NS_STATIC_CAST(txExecutionState*, aContext->getPrivateContext());
00077 
00078     txListIterator iter(&params);
00079     nsAutoString keyQName;
00080     evaluateToString((Expr*)iter.next(), aContext, keyQName);
00081 
00082     txExpandedName keyName;
00083     nsresult rv = keyName.init(keyQName, mMappings, PR_FALSE);
00084     NS_ENSURE_SUCCESS(rv, rv);
00085 
00086     nsRefPtr<txAExprResult> exprResult;
00087     rv = ((Expr*)iter.next())->evaluate(aContext, getter_AddRefs(exprResult));
00088     NS_ENSURE_SUCCESS(rv, rv);
00089 
00090     nsAutoPtr<txXPathNode> contextDoc(txXPathNodeUtils::getOwnerDocument(aContext->getContextNode()));
00091     NS_ENSURE_TRUE(contextDoc, NS_ERROR_FAILURE);
00092 
00093     nsRefPtr<txNodeSet> res;
00094     txNodeSet* nodeSet;
00095     if (exprResult->getResultType() == txAExprResult::NODESET &&
00096         (nodeSet = NS_STATIC_CAST(txNodeSet*,
00097                                   NS_STATIC_CAST(txAExprResult*,
00098                                                  exprResult)))->size() > 1) {
00099         rv = aContext->recycler()->getNodeSet(getter_AddRefs(res));
00100         NS_ENSURE_SUCCESS(rv, rv);
00101 
00102         PRInt32 i;
00103         for (i = 0; i < nodeSet->size(); ++i) {
00104             nsAutoString val;
00105             txXPathNodeUtils::appendNodeValue(nodeSet->get(i), val);
00106 
00107             nsRefPtr<txNodeSet> nodes;
00108             rv = es->getKeyNodes(keyName, *contextDoc, val, i == 0,
00109                                  getter_AddRefs(nodes));
00110             NS_ENSURE_SUCCESS(rv, rv);
00111 
00112             res->add(*nodes);
00113         }
00114     }
00115     else {
00116         nsAutoString val;
00117         exprResult->stringValue(val);
00118         rv = es->getKeyNodes(keyName, *contextDoc, val, PR_TRUE,
00119                              getter_AddRefs(res));
00120         NS_ENSURE_SUCCESS(rv, rv);
00121     }
00122 
00123     *aResult = res;
00124     NS_ADDREF(*aResult);
00125 
00126     return NS_OK;
00127 }
00128 
00129 #ifdef TX_TO_STRING
00130 nsresult
00131 txKeyFunctionCall::getNameAtom(nsIAtom** aAtom)
00132 {
00133     *aAtom = txXSLTAtoms::key;
00134     NS_ADDREF(*aAtom);
00135     return NS_OK;
00136 }
00137 #endif
00138 
00143 DHASH_WRAPPER(txKeyValueHash, txKeyValueHashEntry, txKeyValueHashKey&)
00144 DHASH_WRAPPER(txIndexedKeyHash, txIndexedKeyHashEntry, txIndexedKeyHashKey&)
00145 
00146 const void*
00147 txKeyValueHashEntry::GetKey()
00148 {
00149     return &mKey;
00150 }
00151 
00152 PRBool
00153 txKeyValueHashEntry::MatchEntry(const void* aKey) const
00154 {
00155     const txKeyValueHashKey* key =
00156         NS_STATIC_CAST(const txKeyValueHashKey*, aKey);
00157 
00158     return mKey.mKeyName == key->mKeyName &&
00159            mKey.mDocumentIdentifier == key->mDocumentIdentifier &&
00160            mKey.mKeyValue.Equals(key->mKeyValue);
00161 }
00162 
00163 PLDHashNumber
00164 txKeyValueHashEntry::HashKey(const void* aKey)
00165 {
00166     const txKeyValueHashKey* key =
00167         NS_STATIC_CAST(const txKeyValueHashKey*, aKey);
00168 
00169     return key->mKeyName.mNamespaceID ^
00170            NS_PTR_TO_INT32(key->mKeyName.mLocalName.get()) ^
00171            key->mDocumentIdentifier ^
00172            HashString(key->mKeyValue);
00173 }
00174 
00175 const void*
00176 txIndexedKeyHashEntry::GetKey()
00177 {
00178     return &mKey;
00179 }
00180 
00181 PRBool
00182 txIndexedKeyHashEntry::MatchEntry(const void* aKey) const
00183 {
00184     const txIndexedKeyHashKey* key =
00185         NS_STATIC_CAST(const txIndexedKeyHashKey*, aKey);
00186 
00187     return mKey.mKeyName == key->mKeyName &&
00188            mKey.mDocumentIdentifier == key->mDocumentIdentifier;
00189 }
00190 
00191 PLDHashNumber
00192 txIndexedKeyHashEntry::HashKey(const void* aKey)
00193 {
00194     const txIndexedKeyHashKey* key =
00195         NS_STATIC_CAST(const txIndexedKeyHashKey*, aKey);
00196 
00197     return key->mKeyName.mNamespaceID ^
00198            NS_PTR_TO_INT32(key->mKeyName.mLocalName.get()) ^
00199            key->mDocumentIdentifier;
00200 }
00201 
00202 /*
00203  * Class managing XSLT-keys
00204  */
00205 
00206 nsresult
00207 txKeyHash::getKeyNodes(const txExpandedName& aKeyName,
00208                        const txXPathNode& aDocument,
00209                        const nsAString& aKeyValue,
00210                        PRBool aIndexIfNotFound,
00211                        txExecutionState& aEs,
00212                        txNodeSet** aResult)
00213 {
00214     NS_ENSURE_TRUE(mKeyValues.mHashTable.ops && mIndexedKeys.mHashTable.ops,
00215                    NS_ERROR_OUT_OF_MEMORY);
00216 
00217     *aResult = nsnull;
00218 
00219     PRInt32 identifier = txXPathNodeUtils::getUniqueIdentifier(aDocument);
00220 
00221     txKeyValueHashKey valueKey(aKeyName, identifier, aKeyValue);
00222     txKeyValueHashEntry* valueEntry = mKeyValues.GetEntry(valueKey);
00223     if (valueEntry) {
00224         *aResult = valueEntry->mNodeSet;
00225         NS_ADDREF(*aResult);
00226 
00227         return NS_OK;
00228     }
00229 
00230     // We didn't find a value. This could either mean that that key has no
00231     // nodes with that value or that the key hasn't been indexed using this
00232     // document.
00233 
00234     if (!aIndexIfNotFound) {
00235         // If aIndexIfNotFound is set then the caller knows this key is
00236         // indexed, so don't bother investigating.
00237         *aResult = mEmptyNodeSet;
00238         NS_ADDREF(*aResult);
00239 
00240         return NS_OK;
00241     }
00242 
00243     txIndexedKeyHashKey indexKey(aKeyName, identifier);
00244     txIndexedKeyHashEntry* indexEntry = mIndexedKeys.AddEntry(indexKey);
00245     NS_ENSURE_TRUE(indexEntry, NS_ERROR_OUT_OF_MEMORY);
00246 
00247     if (indexEntry->mIndexed) {
00248         // The key was indexed and apparently didn't contain this value so
00249         // return the empty nodeset.
00250         *aResult = mEmptyNodeSet;
00251         NS_ADDREF(*aResult);
00252 
00253         return NS_OK;
00254     }
00255 
00256     // The key needs to be indexed.
00257     txXSLKey* xslKey = (txXSLKey*)mKeys.get(aKeyName);
00258     if (!xslKey) {
00259         // The key didn't exist, so bail.
00260         return NS_ERROR_INVALID_ARG;
00261     }
00262 
00263     nsresult rv = xslKey->indexDocument(aDocument, mKeyValues, aEs);
00264     NS_ENSURE_SUCCESS(rv, rv);
00265     
00266     indexEntry->mIndexed = PR_TRUE;
00267 
00268     // Now that the key is indexed we can get its value.
00269     valueEntry = mKeyValues.GetEntry(valueKey);
00270     if (valueEntry) {
00271         *aResult = valueEntry->mNodeSet;
00272         NS_ADDREF(*aResult);
00273     }
00274     else {
00275         *aResult = mEmptyNodeSet;
00276         NS_ADDREF(*aResult);
00277     }
00278 
00279     return NS_OK;
00280 }
00281 
00282 nsresult
00283 txKeyHash::init()
00284 {
00285     nsresult rv = mKeyValues.Init(8);
00286     NS_ENSURE_SUCCESS(rv, rv);
00287 
00288     rv = mIndexedKeys.Init(1);
00289     NS_ENSURE_SUCCESS(rv, rv);
00290     
00291     mEmptyNodeSet = new txNodeSet(nsnull);
00292     NS_ENSURE_TRUE(mEmptyNodeSet, NS_ERROR_OUT_OF_MEMORY);
00293     
00294     return NS_OK;
00295 }
00296 
00301 txXSLKey::~txXSLKey()
00302 {
00303     txListIterator iter(&mKeys);
00304     Key* key;
00305     while ((key = (Key*)iter.next())) {
00306         delete key;
00307     }
00308 }
00309 
00316 PRBool txXSLKey::addKey(nsAutoPtr<txPattern> aMatch, nsAutoPtr<Expr> aUse)
00317 {
00318     if (!aMatch || !aUse)
00319         return PR_FALSE;
00320 
00321     nsAutoPtr<Key> key(new Key);
00322     if (!key)
00323         return PR_FALSE;
00324 
00325     key->matchPattern = aMatch;
00326     key->useExpr = aUse;
00327     nsresult rv = mKeys.add(key);
00328     NS_ENSURE_SUCCESS(rv, PR_FALSE);
00329     
00330     key.forget();
00331 
00332     return PR_TRUE;
00333 }
00334 
00341 nsresult txXSLKey::indexDocument(const txXPathNode& aDocument,
00342                                  txKeyValueHash& aKeyValueHash,
00343                                  txExecutionState& aEs)
00344 {
00345     txKeyValueHashKey key(mName,
00346                           txXPathNodeUtils::getUniqueIdentifier(aDocument),
00347                           EmptyString());
00348     return indexTree(aDocument, key, aKeyValueHash, aEs);
00349 }
00350 
00359 nsresult txXSLKey::indexTree(const txXPathNode& aNode,
00360                              txKeyValueHashKey& aKey,
00361                              txKeyValueHash& aKeyValueHash,
00362                              txExecutionState& aEs)
00363 {
00364     nsresult rv = testNode(aNode, aKey, aKeyValueHash, aEs);
00365     NS_ENSURE_SUCCESS(rv, rv);
00366 
00367     // check if the node's attributes match
00368     txXPathTreeWalker walker(aNode);
00369     if (walker.moveToFirstAttribute()) {
00370         do {
00371             rv = testNode(walker.getCurrentPosition(), aKey, aKeyValueHash,
00372                           aEs);
00373             NS_ENSURE_SUCCESS(rv, rv);
00374         } while (walker.moveToNextAttribute());
00375         walker.moveToParent();
00376     }
00377 
00378     // check if the node's descendants match
00379     if (walker.moveToFirstChild()) {
00380         do {
00381             rv = indexTree(walker.getCurrentPosition(), aKey, aKeyValueHash,
00382                            aEs);
00383             NS_ENSURE_SUCCESS(rv, rv);
00384         } while (walker.moveToNextSibling());
00385     }
00386 
00387     return NS_OK;
00388 }
00389 
00398 nsresult txXSLKey::testNode(const txXPathNode& aNode,
00399                             txKeyValueHashKey& aKey,
00400                             txKeyValueHash& aKeyValueHash,
00401                             txExecutionState& aEs)
00402 {
00403     nsAutoString val;
00404     txListIterator iter(&mKeys);
00405     while (iter.hasNext())
00406     {
00407         Key* key = (Key*)iter.next();
00408         if (key->matchPattern->matches(aNode, &aEs)) {
00409             txSingleNodeContext evalContext(aNode, &aEs);
00410             nsresult rv = aEs.pushEvalContext(&evalContext);
00411             NS_ENSURE_SUCCESS(rv, rv);
00412 
00413             nsRefPtr<txAExprResult> exprResult;
00414             rv = key->useExpr->evaluate(&evalContext,
00415                                         getter_AddRefs(exprResult));
00416             NS_ENSURE_SUCCESS(rv, rv);
00417 
00418             aEs.popEvalContext();
00419 
00420             if (exprResult->getResultType() == txAExprResult::NODESET) {
00421                 txNodeSet* res = NS_STATIC_CAST(txNodeSet*,
00422                                                 NS_STATIC_CAST(txAExprResult*,
00423                                                                exprResult));
00424                 PRInt32 i;
00425                 for (i = 0; i < res->size(); ++i) {
00426                     val.Truncate();
00427                     txXPathNodeUtils::appendNodeValue(res->get(i), val);
00428 
00429                     aKey.mKeyValue.Assign(val);
00430                     txKeyValueHashEntry* entry = aKeyValueHash.AddEntry(aKey);
00431                     NS_ENSURE_TRUE(entry && entry->mNodeSet,
00432                                    NS_ERROR_OUT_OF_MEMORY);
00433 
00434                     if (entry->mNodeSet->isEmpty() ||
00435                         entry->mNodeSet->get(entry->mNodeSet->size() - 1) !=
00436                         aNode) {
00437                         entry->mNodeSet->append(aNode);
00438                     }
00439                 }
00440             }
00441             else {
00442                 exprResult->stringValue(val);
00443 
00444                 aKey.mKeyValue.Assign(val);
00445                 txKeyValueHashEntry* entry = aKeyValueHash.AddEntry(aKey);
00446                 NS_ENSURE_TRUE(entry && entry->mNodeSet,
00447                                NS_ERROR_OUT_OF_MEMORY);
00448 
00449                 if (entry->mNodeSet->isEmpty() ||
00450                     entry->mNodeSet->get(entry->mNodeSet->size() - 1) !=
00451                     aNode) {
00452                     entry->mNodeSet->append(aNode);
00453                 }
00454             }
00455         }
00456     }
00457     
00458     return NS_OK;
00459 }