Back to index

lightning-sunbird  0.9+nobinonly
txXSLTPatterns.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  * Axel Hecht.
00019  * Portions created by the Initial Developer are Copyright (C) 2002
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Axel Hecht <axel@pike.org>
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 "nsReadableUtils.h"
00040 #include "txExecutionState.h"
00041 #include "txXSLTPatterns.h"
00042 #include "txNodeSetContext.h"
00043 #include "txForwardContext.h"
00044 #include "XMLUtils.h"
00045 #include "XSLTFunctions.h"
00046 #ifndef TX_EXE
00047 #include "nsIContent.h"
00048 #endif
00049 
00050 /*
00051  * txPattern
00052  *
00053  * Base class of all patterns
00054  * Implements only a default getSimplePatterns
00055  */
00056 nsresult txPattern::getSimplePatterns(txList& aList)
00057 {
00058     aList.add(this);
00059     return NS_OK;
00060 }
00061 
00062 txPattern::~txPattern()
00063 {
00064 }
00065 
00066 
00067 /*
00068  * txUnionPattern
00069  *
00070  * This pattern is returned by the parser for "foo | bar" constructs.
00071  * |xsl:template|s should use the simple patterns
00072  */
00073 
00074 /*
00075  * Destructor, deletes all LocationPathPatterns
00076  */
00077 txUnionPattern::~txUnionPattern()
00078 {
00079     txListIterator iter(&mLocPathPatterns);
00080     while (iter.hasNext()) {
00081         delete (txPattern*)iter.next();
00082     }
00083 }
00084 
00085 nsresult txUnionPattern::addPattern(txPattern* aPattern)
00086 {
00087     if (!aPattern)
00088         return NS_ERROR_NULL_POINTER;
00089     mLocPathPatterns.add(aPattern);
00090     return NS_OK;
00091 }
00092 
00093 /*
00094  * Returns the default priority of this Pattern.
00095  * UnionPatterns don't like this.
00096  * This should be called on the simple patterns.
00097  */
00098 double txUnionPattern::getDefaultPriority()
00099 {
00100     NS_ASSERTION(0, "Don't call getDefaultPriority on txUnionPattern");
00101     return Double::NaN;
00102 }
00103 
00104 /*
00105  * Determines whether this Pattern matches the given node within
00106  * the given context
00107  * This should be called on the simple patterns for xsl:template,
00108  * but is fine for xsl:key and xsl:number
00109  */
00110 MBool txUnionPattern::matches(const txXPathNode& aNode, txIMatchContext* aContext)
00111 {
00112     txListIterator iter(&mLocPathPatterns);
00113     while (iter.hasNext()) {
00114         txPattern* p = (txPattern*)iter.next();
00115         if (p->matches(aNode, aContext)) {
00116             return MB_TRUE;
00117         }
00118     }
00119     return MB_FALSE;
00120 }
00121 
00122 nsresult txUnionPattern::getSimplePatterns(txList& aList)
00123 {
00124     txListIterator iter(&mLocPathPatterns);
00125     while (iter.hasNext()) {
00126         aList.add(iter.next());
00127         iter.remove();
00128     }
00129     return NS_OK;
00130 }
00131 
00132 #ifdef TX_TO_STRING
00133 void
00134 txUnionPattern::toString(nsAString& aDest)
00135 {
00136 #ifdef DEBUG
00137     aDest.AppendLiteral("txUnionPattern{");
00138 #endif
00139     txListIterator iter(&mLocPathPatterns);
00140     if (iter.hasNext())
00141         ((txPattern*)iter.next())->toString(aDest);
00142     while (iter.hasNext()) {
00143         aDest.AppendLiteral(" | ");
00144         ((txPattern*)iter.next())->toString(aDest);
00145     }
00146 #ifdef DEBUG
00147     aDest.Append(PRUnichar('}'));
00148 #endif
00149 }
00150 #endif
00151 
00152 
00153 /*
00154  * LocationPathPattern
00155  *
00156  * a list of step patterns, can start with id or key
00157  * (dealt with by the parser)
00158  */
00159 
00160 /*
00161  * Destructor, deletes all PathPatterns
00162  */
00163 txLocPathPattern::~txLocPathPattern()
00164 {
00165     txListIterator iter(&mSteps);
00166     while (iter.hasNext()) {
00167          delete (Step*)iter.next();
00168     }
00169 }
00170 
00171 nsresult txLocPathPattern::addStep(txPattern* aPattern, MBool isChild)
00172 {
00173     if (!aPattern)
00174         return NS_ERROR_NULL_POINTER;
00175     Step* step = new Step(aPattern, isChild);
00176     if (!step)
00177         return NS_ERROR_OUT_OF_MEMORY;
00178     mSteps.add(step);
00179     return NS_OK;
00180 }
00181 
00182 MBool txLocPathPattern::matches(const txXPathNode& aNode, txIMatchContext* aContext)
00183 {
00184     NS_ASSERTION(mSteps.getLength(), "Internal error");
00185 
00186     /*
00187      * The idea is to split up a path into blocks separated by descendant
00188      * operators. For example "foo/bar//baz/bop//ying/yang" is split up into
00189      * three blocks. The "ying/yang" block is handled by the first while-loop
00190      * and the "foo/bar" and "baz/bop" blocks are handled by the second
00191      * while-loop.
00192      * A block is considered matched when we find a list of ancestors that
00193      * match the block. If there are more than one list of ancestors that
00194      * match a block we only need to find the one furthermost down in the
00195      * tree.
00196      */
00197 
00198     txListIterator iter(&mSteps);
00199     iter.resetToEnd();
00200 
00201     Step* step;
00202     step = (Step*)iter.previous();
00203     if (!step->pattern->matches(aNode, aContext))
00204         return MB_FALSE;
00205 
00206     txXPathTreeWalker walker(aNode);
00207     PRBool hasParent = walker.moveToParent();
00208 
00209     while (step->isChild) {
00210         step = (Step*)iter.previous();
00211         if (!step)
00212             return MB_TRUE; // all steps matched
00213         if (!hasParent || !step->pattern->matches(walker.getCurrentPosition(), aContext))
00214             return MB_FALSE; // no more ancestors or no match
00215 
00216         hasParent = walker.moveToParent();
00217     }
00218 
00219     // We have at least one // path separator
00220     txXPathTreeWalker blockWalker(walker);
00221     txListIterator blockIter(iter);
00222 
00223     while ((step = (Step*)iter.previous())) {
00224         if (!hasParent)
00225             return MB_FALSE; // There are more steps in the current block 
00226                              // than ancestors of the tested node
00227 
00228         if (!step->pattern->matches(walker.getCurrentPosition(), aContext)) {
00229             // Didn't match. We restart at beginning of block using a new
00230             // start node
00231             iter = blockIter;
00232             hasParent = blockWalker.moveToParent();
00233             walker.moveTo(blockWalker);
00234         }
00235         else {
00236             hasParent = walker.moveToParent();
00237             if (!step->isChild) {
00238                 // We've matched an entire block. Set new start iter and start node
00239                 blockIter = iter;
00240                 blockWalker.moveTo(walker);
00241             }
00242         }
00243     }
00244 
00245     return MB_TRUE;
00246 } // txLocPathPattern::matches
00247 
00248 double txLocPathPattern::getDefaultPriority()
00249 {
00250     if (mSteps.getLength() > 1) {
00251         return 0.5;
00252     }
00253 
00254     return ((Step*)mSteps.get(0))->pattern->getDefaultPriority();
00255 }
00256 
00257 #ifdef TX_TO_STRING
00258 void
00259 txLocPathPattern::toString(nsAString& aDest)
00260 {
00261     txListIterator iter(&mSteps);
00262 #ifdef DEBUG
00263     aDest.AppendLiteral("txLocPathPattern{");
00264 #endif
00265     Step* step;
00266     step = (Step*)iter.next();
00267     if (step) {
00268         step->pattern->toString(aDest);
00269     }
00270     while ((step = (Step*)iter.next())) {
00271         if (step->isChild)
00272             aDest.Append(PRUnichar('/'));
00273         else
00274             aDest.AppendLiteral("//");
00275         step->pattern->toString(aDest);
00276     }
00277 #ifdef DEBUG
00278     aDest.Append(PRUnichar('}'));
00279 #endif
00280 }
00281 #endif
00282 
00283 /*
00284  * txRootPattern
00285  *
00286  * a txPattern matching the document node, or '/'
00287  */
00288 
00289 txRootPattern::~txRootPattern()
00290 {
00291 }
00292 
00293 MBool txRootPattern::matches(const txXPathNode& aNode, txIMatchContext* aContext)
00294 {
00295     return txXPathNodeUtils::isRoot(aNode);
00296 }
00297 
00298 double txRootPattern::getDefaultPriority()
00299 {
00300     return 0.5;
00301 }
00302 
00303 #ifdef TX_TO_STRING
00304 void
00305 txRootPattern::toString(nsAString& aDest)
00306 {
00307 #ifdef DEBUG
00308     aDest.AppendLiteral("txRootPattern{");
00309 #endif
00310     if (mSerialize)
00311         aDest.Append(PRUnichar('/'));
00312 #ifdef DEBUG
00313     aDest.Append(PRUnichar('}'));
00314 #endif
00315 }
00316 #endif
00317 
00318 /*
00319  * txIdPattern
00320  *
00321  * txIdPattern matches if the given node has a ID attribute with one
00322  * of the space delimited values.
00323  * This looks like the id() function, but may only have LITERALs as
00324  * argument.
00325  */
00326 txIdPattern::txIdPattern(const nsAString& aString)
00327 {
00328     nsAString::const_iterator pos, begin, end;
00329     aString.BeginReading(begin);
00330     aString.EndReading(end);
00331     pos = begin;
00332     while (pos != end) {
00333         while (pos != end && XMLUtils::isWhitespace(*pos))
00334             ++pos;
00335         begin = pos;
00336         while (pos != end && !XMLUtils::isWhitespace(*pos))
00337             ++pos;
00338         // this can fail, XXX move to a Init(aString) method
00339         mIds.AppendString(Substring(begin, pos));
00340     }
00341 }
00342 
00343 txIdPattern::~txIdPattern()
00344 {
00345 }
00346 
00347 MBool txIdPattern::matches(const txXPathNode& aNode, txIMatchContext* aContext)
00348 {
00349     if (!txXPathNodeUtils::isElement(aNode)) {
00350         return PR_FALSE;
00351     }
00352 
00353     // Get a ID attribute, if there is
00354     nsAutoString value;
00355 #ifdef TX_EXE
00356     Element* elem;
00357     nsresult rv = txXPathNativeNode::getElement(aNode, &elem);
00358     NS_ASSERTION(NS_SUCCEEDED(rv), "So why claim it's an element above?");
00359     if (!elem->getIDValue(value)) {
00360         return PR_FALSE;
00361     }
00362 #else
00363     nsIContent* content = txXPathNativeNode::getContent(aNode);
00364     NS_ASSERTION(content, "a Element without nsIContent");
00365     if (!content) {
00366         return MB_FALSE;
00367     }
00368 
00369     nsIAtom* idAttr = content->GetIDAttributeName();
00370     if (!idAttr) {
00371         return MB_FALSE; // no ID for this element defined, can't match
00372     }
00373     nsresult rv = content->GetAttr(kNameSpaceID_None, idAttr, value);
00374     if (rv != NS_CONTENT_ATTR_HAS_VALUE) {
00375         return MB_FALSE; // no ID attribute given
00376     }
00377 #endif // TX_EXE
00378     return mIds.IndexOf(value) > -1;
00379 }
00380 
00381 double txIdPattern::getDefaultPriority()
00382 {
00383     return 0.5;
00384 }
00385 
00386 #ifdef TX_TO_STRING
00387 void
00388 txIdPattern::toString(nsAString& aDest)
00389 {
00390 #ifdef DEBUG
00391     aDest.AppendLiteral("txIdPattern{");
00392 #endif
00393     aDest.AppendLiteral("id('");
00394     PRUint32 k, count = mIds.Count() - 1;
00395     for (k = 0; k < count; ++k) {
00396         aDest.Append(*mIds[k]);
00397         aDest.Append(PRUnichar(' '));
00398     }
00399     aDest.Append(*mIds[count]);
00400     aDest.Append(NS_LITERAL_STRING("')"));
00401 #ifdef DEBUG
00402     aDest.Append(PRUnichar('}'));
00403 #endif
00404 }
00405 #endif
00406 
00407 /*
00408  * txKeyPattern
00409  *
00410  * txKeyPattern matches if the given node is in the evalation of 
00411  * the key() function
00412  * This resembles the key() function, but may only have LITERALs as
00413  * argument.
00414  */
00415 
00416 txKeyPattern::~txKeyPattern()
00417 {
00418 }
00419 
00420 MBool txKeyPattern::matches(const txXPathNode& aNode, txIMatchContext* aContext)
00421 {
00422     txExecutionState* es = (txExecutionState*)aContext->getPrivateContext();
00423     nsAutoPtr<txXPathNode> contextDoc(txXPathNodeUtils::getOwnerDocument(aNode));
00424     NS_ENSURE_TRUE(contextDoc, PR_FALSE);
00425 
00426     nsRefPtr<txNodeSet> nodes;
00427     nsresult rv = es->getKeyNodes(mName, *contextDoc, mValue, PR_TRUE,
00428                                   getter_AddRefs(nodes));
00429     NS_ENSURE_SUCCESS(rv, PR_FALSE);
00430 
00431     return nodes->contains(aNode);
00432 }
00433 
00434 double txKeyPattern::getDefaultPriority()
00435 {
00436     return 0.5;
00437 }
00438 
00439 #ifdef TX_TO_STRING
00440 void
00441 txKeyPattern::toString(nsAString& aDest)
00442 {
00443 #ifdef DEBUG
00444     aDest.AppendLiteral("txKeyPattern{");
00445 #endif
00446     aDest.AppendLiteral("key('");
00447     nsAutoString tmp;
00448     if (mPrefix) {
00449         mPrefix->ToString(tmp);
00450         aDest.Append(tmp);
00451         aDest.Append(PRUnichar(':'));
00452     }
00453     mName.mLocalName->ToString(tmp);
00454     aDest.Append(tmp);
00455     aDest.AppendLiteral(", ");
00456     aDest.Append(mValue);
00457     aDest.Append(NS_LITERAL_STRING("')"));
00458 #ifdef DEBUG
00459     aDest.Append(PRUnichar('}'));
00460 #endif
00461 }
00462 #endif
00463 
00464 /*
00465  * txStepPattern
00466  *
00467  * a txPattern to hold the NodeTest and the Predicates of a StepPattern
00468  */
00469 
00470 txStepPattern::~txStepPattern()
00471 {
00472     delete mNodeTest;
00473 }
00474 
00475 MBool txStepPattern::matches(const txXPathNode& aNode, txIMatchContext* aContext)
00476 {
00477     NS_ASSERTION(mNodeTest, "Internal error");
00478 
00479     if (!mNodeTest->matches(aNode, aContext))
00480         return MB_FALSE;
00481 
00482     txXPathTreeWalker walker(aNode);
00483     if ((!mIsAttr &&
00484          txXPathNodeUtils::isAttribute(walker.getCurrentPosition())) ||
00485         !walker.moveToParent()) {
00486         return MB_FALSE;
00487     }
00488     if (isEmpty()) {
00489         return MB_TRUE;
00490     }
00491 
00492     /*
00493      * Evaluate Predicates
00494      *
00495      * Copy all siblings/attributes matching mNodeTest to nodes
00496      * Up to the last Predicate do
00497      *  Foreach node in nodes
00498      *   evaluate Predicate with node as context node
00499      *   if the result is a number, check the context position,
00500      *    otherwise convert to bool
00501      *   if result is true, copy node to newNodes
00502      *  if aNode is not member of newNodes, return MB_FALSE
00503      *  nodes = newNodes
00504      *
00505      * For the last Predicate, evaluate Predicate with aNode as
00506      *  context node, if the result is a number, check the position,
00507      *  otherwise return the result converted to boolean
00508      */
00509 
00510     // Create the context node set for evaluating the predicates
00511     nsRefPtr<txNodeSet> nodes;
00512     nsresult rv = aContext->recycler()->getNodeSet(getter_AddRefs(nodes));
00513     NS_ENSURE_SUCCESS(rv, rv);
00514 
00515     PRBool hasNext = mIsAttr ? walker.moveToFirstAttribute() :
00516                                walker.moveToFirstChild();
00517     while (hasNext) {
00518         if (mNodeTest->matches(walker.getCurrentPosition(), aContext)) {
00519             nodes->append(walker.getCurrentPosition());
00520         }
00521         hasNext = mIsAttr ? walker.moveToNextAttribute() :
00522                             walker.moveToNextSibling();
00523     }
00524 
00525     txListIterator iter(&predicates);
00526     Expr* predicate = (Expr*)iter.next();
00527     nsRefPtr<txNodeSet> newNodes;
00528     rv = aContext->recycler()->getNodeSet(getter_AddRefs(newNodes));
00529     NS_ENSURE_SUCCESS(rv, rv);
00530 
00531     while (iter.hasNext()) {
00532         newNodes->clear();
00533         MBool contextIsInPredicate = MB_FALSE;
00534         txNodeSetContext predContext(nodes, aContext);
00535         while (predContext.hasNext()) {
00536             predContext.next();
00537             nsRefPtr<txAExprResult> exprResult;
00538             rv = predicate->evaluate(&predContext, getter_AddRefs(exprResult));
00539             NS_ENSURE_SUCCESS(rv, PR_FALSE);
00540 
00541             switch(exprResult->getResultType()) {
00542                 case txAExprResult::NUMBER:
00543                     // handle default, [position() == numberValue()]
00544                     if ((double)predContext.position() ==
00545                         exprResult->numberValue()) {
00546                         const txXPathNode& tmp = predContext.getContextNode();
00547                         if (tmp == aNode)
00548                             contextIsInPredicate = MB_TRUE;
00549                         newNodes->append(tmp);
00550                     }
00551                     break;
00552                 default:
00553                     if (exprResult->booleanValue()) {
00554                         const txXPathNode& tmp = predContext.getContextNode();
00555                         if (tmp == aNode)
00556                             contextIsInPredicate = MB_TRUE;
00557                         newNodes->append(tmp);
00558                     }
00559                     break;
00560             }
00561         }
00562         // Move new NodeSet to the current one
00563         nodes->clear();
00564         nodes->append(*newNodes);
00565         if (!contextIsInPredicate) {
00566             return MB_FALSE;
00567         }
00568         predicate = (Expr*)iter.next();
00569     }
00570     txForwardContext evalContext(aContext, aNode, nodes);
00571     nsRefPtr<txAExprResult> exprResult;
00572     rv = predicate->evaluate(&evalContext, getter_AddRefs(exprResult));
00573     NS_ENSURE_SUCCESS(rv, PR_FALSE);
00574 
00575     if (exprResult->getResultType() == txAExprResult::NUMBER)
00576         // handle default, [position() == numberValue()]
00577         return ((double)evalContext.position() == exprResult->numberValue());
00578 
00579     return exprResult->booleanValue();
00580 } // matches
00581 
00582 double txStepPattern::getDefaultPriority()
00583 {
00584     if (isEmpty())
00585         return mNodeTest->getDefaultPriority();
00586     return 0.5;
00587 }
00588 
00589 #ifdef TX_TO_STRING
00590 void
00591 txStepPattern::toString(nsAString& aDest)
00592 {
00593 #ifdef DEBUG
00594     aDest.AppendLiteral("txStepPattern{");
00595 #endif
00596     if (mIsAttr)
00597         aDest.Append(PRUnichar('@'));
00598     if (mNodeTest)
00599         mNodeTest->toString(aDest);
00600 
00601     PredicateList::toString(aDest);
00602 #ifdef DEBUG
00603     aDest.Append(PRUnichar('}'));
00604 #endif
00605 }
00606 #endif