Back to index

lightning-sunbird  0.9+nobinonly
txExecutionState.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) 2002
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Jonas Sicking <jonas@sicking.cc>
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 "txSingleNodeContext.h"
00041 #include "txInstructions.h"
00042 #include "txStylesheet.h"
00043 #include "txVariableMap.h"
00044 #include "txRtfHandler.h"
00045 #include "txXSLTProcessor.h"
00046 #include "TxLog.h"
00047 #include "txURIUtils.h"
00048 #include "txXMLParser.h"
00049 
00050 const PRInt32 txExecutionState::kMaxRecursionDepth = 20000;
00051 
00052 nsresult txLoadedDocumentsHash::init(txXPathNode* aSourceDocument)
00053 {
00054     nsresult rv = Init(8);
00055     NS_ENSURE_SUCCESS(rv, rv);
00056 
00057     mSourceDocument = aSourceDocument;
00058     
00059     nsAutoString baseURI;
00060     txXPathNodeUtils::getBaseURI(*mSourceDocument, baseURI);
00061 
00062     txLoadedDocumentEntry* entry = PutEntry(baseURI);
00063     if (!entry) {
00064         return NS_ERROR_FAILURE;
00065     }
00066 
00067     entry->mDocument = mSourceDocument;
00068 
00069     return NS_OK;
00070 }
00071 
00072 txLoadedDocumentsHash::~txLoadedDocumentsHash()
00073 {
00074     if (!IsInitialized()) {
00075         return;
00076     }
00077 
00078     nsAutoString baseURI;
00079     txXPathNodeUtils::getBaseURI(*mSourceDocument, baseURI);
00080 
00081     txLoadedDocumentEntry* entry = GetEntry(baseURI);
00082     if (entry) {
00083         delete entry->mDocument.forget();
00084     }
00085 }
00086 
00087 txExecutionState::txExecutionState(txStylesheet* aStylesheet,
00088                                    PRBool aDisableLoads)
00089     : mStylesheet(aStylesheet),
00090       mNextInstruction(nsnull),
00091       mLocalVariables(nsnull),
00092       mRecursionDepth(0),
00093       mTemplateRules(nsnull),
00094       mTemplateRulesBufferSize(0),
00095       mTemplateRuleCount(0),
00096       mEvalContext(nsnull),
00097       mInitialEvalContext(nsnull),
00098 //      mRTFDocument(nsnull),
00099       mGlobalParams(nsnull),
00100       mKeyHash(aStylesheet->getKeyMap()),
00101       mDisableLoads(aDisableLoads)
00102 {
00103 }
00104 
00105 txExecutionState::~txExecutionState()
00106 {
00107     delete mResultHandler;
00108     delete mLocalVariables;
00109     delete mEvalContext;
00110 //    delete mRTFDocument;
00111 
00112     PRInt32 i;
00113     for (i = 0; i < mTemplateRuleCount; ++i) {
00114         NS_IF_RELEASE(mTemplateRules[i].mModeLocalName);
00115     }
00116     delete [] mTemplateRules;
00117     
00118     txStackIterator varsIter(&mLocalVarsStack);
00119     while (varsIter.hasNext()) {
00120         delete (txVariableMap*)varsIter.next();
00121     }
00122 
00123     txStackIterator contextIter(&mEvalContextStack);
00124     while (contextIter.hasNext()) {
00125         txIEvalContext* context = (txIEvalContext*)contextIter.next();
00126         if (context != mInitialEvalContext) {
00127             delete context;
00128         }
00129     }
00130 
00131     txStackIterator handlerIter(&mResultHandlerStack);
00132     while (handlerIter.hasNext()) {
00133         delete (txAXMLEventHandler*)handlerIter.next();
00134     }
00135 
00136     txStackIterator paramIter(&mParamStack);
00137     while (paramIter.hasNext()) {
00138         delete (txExpandedNameMap*)paramIter.next();
00139     }
00140 }
00141 
00142 nsresult
00143 txExecutionState::init(const txXPathNode& aNode,
00144                        txExpandedNameMap* aGlobalParams)
00145 {
00146     nsresult rv = NS_OK;
00147 
00148     mGlobalParams = aGlobalParams;
00149 
00150     // Set up initial context
00151     mEvalContext = new txSingleNodeContext(aNode, this);
00152     NS_ENSURE_TRUE(mEvalContext, NS_ERROR_OUT_OF_MEMORY);
00153 
00154     mInitialEvalContext = mEvalContext;
00155 
00156     // Set up output and result-handler
00157     txAXMLEventHandler* handler = 0;
00158     rv = mOutputHandlerFactory->
00159         createHandlerWith(mStylesheet->getOutputFormat(), &handler);
00160     NS_ENSURE_SUCCESS(rv, rv);
00161 
00162     mOutputHandler = handler;
00163     mResultHandler = handler;
00164     mOutputHandler->startDocument();
00165 
00166     // Set up loaded-documents-hash
00167     nsAutoPtr<txXPathNode> document(txXPathNodeUtils::getOwnerDocument(aNode));
00168     NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
00169 
00170     rv = mLoadedDocuments.init(document);
00171     NS_ENSURE_SUCCESS(rv, rv);
00172 
00173     // loaded-documents-hash owns this now
00174     document.forget();
00175 
00176     // Init members
00177     rv = mKeyHash.init();
00178     NS_ENSURE_SUCCESS(rv, rv);
00179     
00180     mRecycler = new txResultRecycler;
00181     NS_ENSURE_TRUE(mRecycler, NS_ERROR_OUT_OF_MEMORY);
00182     
00183     rv = mRecycler->init();
00184     NS_ENSURE_SUCCESS(rv, rv);
00185     
00186     // The actual value here doesn't really matter since noone should use this
00187     // value. But lets put something errorlike in just in case
00188     mGlobalVarPlaceholderValue = new StringResult(NS_LITERAL_STRING("Error"), nsnull);
00189     NS_ENSURE_TRUE(mGlobalVarPlaceholderValue, NS_ERROR_OUT_OF_MEMORY);
00190 
00191     // Initiate first instruction. This has to be done last since findTemplate
00192     // might use us.
00193     txStylesheet::ImportFrame* frame = 0;
00194     txExpandedName nullName;
00195     txInstruction* templ = mStylesheet->findTemplate(aNode, nullName,
00196                                                      this, nsnull, &frame);
00197     rv = pushTemplateRule(frame, nullName, nsnull);
00198     NS_ENSURE_SUCCESS(rv, rv);
00199 
00200     return runTemplate(templ);
00201 }
00202 
00203 nsresult
00204 txExecutionState::end(nsresult aResult)
00205 {
00206     popTemplateRule();
00207     mOutputHandler->endDocument(aResult);
00208     
00209     return NS_OK;
00210 }
00211 
00212 
00213 
00214 nsresult
00215 txExecutionState::getVariable(PRInt32 aNamespace, nsIAtom* aLName,
00216                               txAExprResult*& aResult)
00217 {
00218     nsresult rv = NS_OK;
00219     txExpandedName name(aNamespace, aLName);
00220 
00221     // look for a local variable
00222     if (mLocalVariables) {
00223         mLocalVariables->getVariable(name, &aResult);
00224         if (aResult) {
00225             return NS_OK;
00226         }
00227     }
00228 
00229     // look for an evaluated global variable
00230     mGlobalVariableValues.getVariable(name, &aResult);
00231     if (aResult) {
00232         if (aResult == mGlobalVarPlaceholderValue) {
00233             // XXX ErrorReport: cyclic variable-value
00234             NS_RELEASE(aResult);
00235             return NS_ERROR_XSLT_BAD_RECURSION;
00236         }
00237         return NS_OK;
00238     }
00239 
00240     // Is there perchance a global variable not evaluated yet?
00241     txStylesheet::GlobalVariable* var = mStylesheet->getGlobalVariable(name);
00242     if (!var) {
00243         // XXX ErrorReport: variable doesn't exist in this scope
00244         return NS_ERROR_FAILURE;
00245     }
00246     
00247     NS_ASSERTION(var->mExpr && !var->mFirstInstruction ||
00248                  !var->mExpr && var->mFirstInstruction,
00249                  "global variable should have either instruction or expression");
00250 
00251     // Is this a stylesheet parameter that has a value?
00252     if (var->mIsParam && mGlobalParams) {
00253         txIGlobalParameter* param =
00254             (txIGlobalParameter*)mGlobalParams->get(name);
00255         if (param) {
00256             rv = param->getValue(&aResult);
00257             NS_ENSURE_SUCCESS(rv, rv);
00258 
00259             rv = mGlobalVariableValues.bindVariable(name, aResult);
00260             if (NS_FAILED(rv)) {
00261                 NS_RELEASE(aResult);
00262                 return rv;
00263             }
00264             
00265             return NS_OK;
00266         }
00267     }
00268 
00269     // Insert a placeholdervalue to protect against recursion
00270     rv = mGlobalVariableValues.bindVariable(name, mGlobalVarPlaceholderValue);
00271     NS_ENSURE_SUCCESS(rv, rv);
00272 
00273     // evaluate the global variable
00274     pushEvalContext(mInitialEvalContext);
00275     if (var->mExpr) {
00276         txVariableMap* oldVars = mLocalVariables;
00277         mLocalVariables = nsnull;
00278         rv = var->mExpr->evaluate(getEvalContext(), &aResult);
00279         NS_ENSURE_SUCCESS(rv, rv);
00280 
00281         mLocalVariables = oldVars;
00282     }
00283     else {
00284         nsAutoPtr<txRtfHandler> rtfHandler(new txRtfHandler);
00285         NS_ENSURE_TRUE(rtfHandler, NS_ERROR_OUT_OF_MEMORY);
00286 
00287         rv = pushResultHandler(rtfHandler);
00288         NS_ENSURE_SUCCESS(rv, rv);
00289         
00290         rtfHandler.forget();
00291 
00292         txInstruction* prevInstr = mNextInstruction;
00293         // set return to nsnull to stop execution
00294         mNextInstruction = nsnull;
00295         rv = runTemplate(var->mFirstInstruction);
00296         NS_ENSURE_SUCCESS(rv, rv);
00297 
00298         rv = pushTemplateRule(nsnull, txExpandedName(), nsnull);
00299         NS_ENSURE_SUCCESS(rv, rv);
00300 
00301         rv = txXSLTProcessor::execute(*this);
00302         NS_ENSURE_SUCCESS(rv, rv);
00303 
00304         popTemplateRule();
00305 
00306         mNextInstruction = prevInstr;
00307         rtfHandler = (txRtfHandler*)popResultHandler();
00308         rv = rtfHandler->getAsRTF(&aResult);
00309         NS_ENSURE_SUCCESS(rv, rv);
00310     }
00311     popEvalContext();
00312 
00313     // Remove the placeholder and insert the calculated value
00314     mGlobalVariableValues.removeVariable(name);
00315     rv = mGlobalVariableValues.bindVariable(name, aResult);
00316     if (NS_FAILED(rv)) {
00317         NS_RELEASE(aResult);
00318 
00319         return rv;
00320     }
00321 
00322     return NS_OK;
00323 }
00324 
00325 PRBool
00326 txExecutionState::isStripSpaceAllowed(const txXPathNode& aNode)
00327 {
00328     return mStylesheet->isStripSpaceAllowed(aNode, this);
00329 }
00330 
00331 void*
00332 txExecutionState::getPrivateContext()
00333 {
00334     return this;
00335 }
00336 
00337 txResultRecycler*
00338 txExecutionState::recycler()
00339 {
00340     return mRecycler;
00341 }
00342 
00343 void
00344 txExecutionState::receiveError(const nsAString& aMsg, nsresult aRes)
00345 {
00346     // XXX implement me
00347 }
00348 
00349 nsresult
00350 txExecutionState::pushEvalContext(txIEvalContext* aContext)
00351 {
00352     nsresult rv = mEvalContextStack.push(mEvalContext);
00353     NS_ENSURE_SUCCESS(rv, rv);
00354     
00355     mEvalContext = aContext;
00356     
00357     return NS_OK;
00358 }
00359 
00360 txIEvalContext*
00361 txExecutionState::popEvalContext()
00362 {
00363     txIEvalContext* prev = mEvalContext;
00364     mEvalContext = (txIEvalContext*)mEvalContextStack.pop();
00365     
00366     return prev;
00367 }
00368 
00369 nsresult
00370 txExecutionState::pushString(const nsAString& aStr)
00371 {
00372     if (!mStringStack.AppendString(aStr)) {
00373         return NS_ERROR_OUT_OF_MEMORY;
00374     }
00375     
00376     return NS_OK;
00377 }
00378 
00379 void
00380 txExecutionState::popString(nsAString& aStr)
00381 {
00382     PRInt32 count = mStringStack.Count() - 1;
00383     NS_ASSERTION(count >= 0, "stack is empty");
00384     mStringStack.StringAt(count, aStr);
00385     mStringStack.RemoveStringAt(count);
00386 }
00387 
00388 nsresult
00389 txExecutionState::pushInt(PRInt32 aInt)
00390 {
00391     return mIntStack.push(NS_INT32_TO_PTR(aInt));
00392 }
00393 
00394 PRInt32
00395 txExecutionState::popInt()
00396 {
00397     return NS_PTR_TO_INT32(mIntStack.pop());
00398 }
00399 
00400 nsresult
00401 txExecutionState::pushResultHandler(txAXMLEventHandler* aHandler)
00402 {
00403     nsresult rv = mResultHandlerStack.push(mResultHandler);
00404     NS_ENSURE_SUCCESS(rv, rv);
00405     
00406     mResultHandler = aHandler;
00407 
00408     return NS_OK;
00409 }
00410 
00411 txAXMLEventHandler*
00412 txExecutionState::popResultHandler()
00413 {
00414     txAXMLEventHandler* oldHandler = mResultHandler;
00415     mResultHandler = (txAXMLEventHandler*)mResultHandlerStack.pop();
00416 
00417     return oldHandler;
00418 }
00419 
00420 nsresult
00421 txExecutionState::pushTemplateRule(txStylesheet::ImportFrame* aFrame,
00422                                    const txExpandedName& aMode,
00423                                    txVariableMap* aParams)
00424 {
00425     if (mTemplateRuleCount == mTemplateRulesBufferSize) {
00426         PRInt32 newSize =
00427             mTemplateRulesBufferSize ? mTemplateRulesBufferSize * 2 : 10;
00428         TemplateRule* newRules = new TemplateRule[newSize];
00429         NS_ENSURE_TRUE(newRules, NS_ERROR_OUT_OF_MEMORY);
00430         
00431         memcpy(newRules, mTemplateRules,
00432                mTemplateRuleCount * sizeof(TemplateRule));
00433         delete [] mTemplateRules;
00434         mTemplateRules = newRules;
00435         mTemplateRulesBufferSize = newSize;
00436     }
00437 
00438     mTemplateRules[mTemplateRuleCount].mFrame = aFrame;
00439     mTemplateRules[mTemplateRuleCount].mModeNsId = aMode.mNamespaceID;
00440     mTemplateRules[mTemplateRuleCount].mModeLocalName = aMode.mLocalName;
00441     mTemplateRules[mTemplateRuleCount].mParams = aParams;
00442     NS_IF_ADDREF(mTemplateRules[mTemplateRuleCount].mModeLocalName);
00443     ++mTemplateRuleCount;
00444     
00445     return NS_OK;
00446 }
00447 
00448 void
00449 txExecutionState::popTemplateRule()
00450 {
00451     // decrement outside of RELEASE, that would decrement twice
00452     --mTemplateRuleCount;
00453     NS_IF_RELEASE(mTemplateRules[mTemplateRuleCount].mModeLocalName);
00454 }
00455 
00456 txIEvalContext*
00457 txExecutionState::getEvalContext()
00458 {
00459     return mEvalContext;
00460 }
00461 
00462 const txXPathNode*
00463 txExecutionState::retrieveDocument(const nsAString& aUri)
00464 {
00465     NS_ASSERTION(aUri.FindChar(PRUnichar('#')) == kNotFound,
00466                  "Remove the fragment.");
00467 
00468     if (mDisableLoads) {
00469         return nsnull;
00470     }
00471 
00472     PR_LOG(txLog::xslt, PR_LOG_DEBUG,
00473            ("Retrieve Document %s", NS_LossyConvertUCS2toASCII(aUri).get()));
00474 
00475     // try to get already loaded document
00476     txLoadedDocumentEntry *entry = mLoadedDocuments.PutEntry(aUri);
00477     if (!entry) {
00478         return nsnull;
00479     }
00480 
00481     if (!entry->mDocument) {
00482         // open URI
00483         nsAutoString errMsg;
00484         // XXX we should get the loader from the actual node
00485         // triggering the load, but this will do for the time being
00486         nsresult rv;
00487         rv = txParseDocumentFromURI(aUri, *mLoadedDocuments.mSourceDocument,
00488                                     errMsg,
00489                                     getter_Transfers(entry->mDocument));
00490 
00491         if (NS_FAILED(rv) || !entry->mDocument) {
00492             mLoadedDocuments.RawRemoveEntry(entry);
00493             receiveError(NS_LITERAL_STRING("Couldn't load document '") +
00494                          aUri + NS_LITERAL_STRING("': ") + errMsg, rv);
00495 
00496             return nsnull;
00497         }
00498     }
00499 
00500     return entry->mDocument;
00501 }
00502 
00503 nsresult
00504 txExecutionState::getKeyNodes(const txExpandedName& aKeyName,
00505                               const txXPathNode& aDocument,
00506                               const nsAString& aKeyValue,
00507                               PRBool aIndexIfNotFound,
00508                               txNodeSet** aResult)
00509 {
00510     return mKeyHash.getKeyNodes(aKeyName, aDocument, aKeyValue,
00511                                 aIndexIfNotFound, *this, aResult);
00512 }
00513 
00514 txExecutionState::TemplateRule*
00515 txExecutionState::getCurrentTemplateRule()
00516 {
00517     return mTemplateRules + mTemplateRuleCount - 1;
00518 }
00519 
00520 txInstruction*
00521 txExecutionState::getNextInstruction()
00522 {
00523     txInstruction* instr = mNextInstruction;
00524     if (instr) {
00525         mNextInstruction = instr->mNext;
00526     }
00527     
00528     return instr;
00529 }
00530 
00531 nsresult
00532 txExecutionState::runTemplate(txInstruction* aTemplate)
00533 {
00534     NS_ENSURE_TRUE(++mRecursionDepth < kMaxRecursionDepth,
00535                    NS_ERROR_XSLT_BAD_RECURSION);
00536 
00537     nsresult rv = mLocalVarsStack.push(mLocalVariables);
00538     NS_ENSURE_SUCCESS(rv, rv);
00539 
00540     rv = mReturnStack.push(mNextInstruction);
00541     NS_ENSURE_SUCCESS(rv, rv);
00542     
00543     mLocalVariables = nsnull;
00544     mNextInstruction = aTemplate;
00545     
00546     return NS_OK;
00547 }
00548 
00549 void
00550 txExecutionState::gotoInstruction(txInstruction* aNext)
00551 {
00552     mNextInstruction = aNext;
00553 }
00554 
00555 void
00556 txExecutionState::returnFromTemplate()
00557 {
00558     --mRecursionDepth;
00559     NS_ASSERTION(!mReturnStack.isEmpty() && !mLocalVarsStack.isEmpty(),
00560                  "return or variable stack is empty");
00561     delete mLocalVariables;
00562     mNextInstruction = (txInstruction*)mReturnStack.pop();
00563     mLocalVariables = (txVariableMap*)mLocalVarsStack.pop();
00564 }
00565 
00566 nsresult
00567 txExecutionState::bindVariable(const txExpandedName& aName,
00568                                txAExprResult* aValue)
00569 {
00570     if (!mLocalVariables) {
00571         mLocalVariables = new txVariableMap;
00572         NS_ENSURE_TRUE(mLocalVariables, NS_ERROR_OUT_OF_MEMORY);
00573     }
00574     return mLocalVariables->bindVariable(aName, aValue);
00575 }
00576 
00577 void
00578 txExecutionState::removeVariable(const txExpandedName& aName)
00579 {
00580     mLocalVariables->removeVariable(aName);
00581 }
00582 
00583 nsresult
00584 txExecutionState::pushParamMap(txVariableMap* aParams)
00585 {
00586     nsresult rv = mParamStack.push(mTemplateParams);
00587     NS_ENSURE_SUCCESS(rv, rv);
00588 
00589     mTemplateParams.forget();
00590     mTemplateParams = aParams;
00591     
00592     return NS_OK;
00593 }
00594 
00595 txVariableMap*
00596 txExecutionState::popParamMap()
00597 {
00598     txVariableMap* oldParams = mTemplateParams.forget();
00599     mTemplateParams = (txVariableMap*)mParamStack.pop();
00600 
00601     return oldParams;
00602 }