Back to index

lightning-sunbird  0.9+nobinonly
txMozillaXPathTreeWalker.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  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 2003
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Peter Van der Beken <peterv@propagandism.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 "txXPathTreeWalker.h"
00040 #include "nsIAtom.h"
00041 #include "nsIAttribute.h"
00042 #include "nsIDOM3Node.h"
00043 #include "nsIDOMAttr.h"
00044 #include "nsIDOMDocument.h"
00045 #include "nsIDOMNode.h"
00046 #include "nsIDOMElement.h"
00047 #include "nsIDOMProcessingInstruction.h"
00048 #include "nsINodeInfo.h"
00049 #include "nsITextContent.h"
00050 #include "nsPrintfCString.h"
00051 #include "nsReadableUtils.h"
00052 #include "nsString.h"
00053 #include "nsTextFragment.h"
00054 #include "XMLUtils.h"
00055 #include "TxLog.h"
00056 #include "nsUnicharUtils.h"
00057 
00058 const PRUint32 kUnknownIndex = PRUint32(-1);
00059 
00060 txXPathTreeWalker::txXPathTreeWalker(const txXPathTreeWalker& aOther)
00061     : mPosition(aOther.mPosition),
00062       mCurrentIndex(aOther.mCurrentIndex)
00063 {
00064 }
00065 
00066 txXPathTreeWalker::txXPathTreeWalker(const txXPathNode& aNode)
00067     : mPosition(aNode),
00068       mCurrentIndex(kUnknownIndex)
00069 {
00070 }
00071 
00072 txXPathTreeWalker::~txXPathTreeWalker()
00073 {
00074 }
00075 
00076 PRBool
00077 txXPathTreeWalker::moveToElementById(const nsAString& aID)
00078 {
00079     nsCOMPtr<nsIDOMDocument> document;
00080     if (mPosition.isDocument()) {
00081         document = do_QueryInterface(mPosition.mDocument);
00082     }
00083     else {
00084         document = do_QueryInterface(mPosition.mContent->GetDocument());
00085     }
00086 
00087     if (!document) {
00088         return PR_FALSE;
00089     }
00090 
00091     nsCOMPtr<nsIDOMElement> element;
00092     document->GetElementById(aID, getter_AddRefs(element));
00093     if (!element) {
00094         return PR_FALSE;
00095     }
00096 
00097     nsCOMPtr<nsIContent> content = do_QueryInterface(element);
00098     NS_ENSURE_TRUE(content, PR_FALSE);
00099 
00100     mPosition.mIndex = txXPathNode::eContent;
00101     mPosition.mContent = content;
00102     mCurrentIndex = kUnknownIndex;
00103     mDescendants.Clear();
00104 
00105     return PR_TRUE;
00106 }
00107 
00108 PRBool
00109 txXPathTreeWalker::moveToFirstAttribute()
00110 {
00111     if (!mPosition.isContent()) {
00112         return PR_FALSE;
00113     }
00114 
00115     return moveToValidAttribute(0);
00116 }
00117 
00118 PRBool
00119 txXPathTreeWalker::moveToNextAttribute()
00120 {
00121     // XXX an assertion should be enough here with the current code
00122     if (!mPosition.isAttribute()) {
00123         return PR_FALSE;
00124     }
00125 
00126     return moveToValidAttribute(mPosition.mIndex + 1);
00127 }
00128 
00129 PRBool
00130 txXPathTreeWalker::moveToValidAttribute(PRUint32 aStartIndex)
00131 {
00132     NS_ASSERTION(!mPosition.isDocument(), "documents doesn't have attrs");
00133 
00134     PRUint32 total = mPosition.mContent->GetAttrCount();
00135     if (aStartIndex >= total) {
00136         return PR_FALSE;
00137     }
00138 
00139     PRInt32 namespaceID;
00140     nsCOMPtr<nsIAtom> name, prefix;
00141 
00142     PRUint32 index;
00143     for (index = aStartIndex; index < total; ++index) {
00144         mPosition.mContent->GetAttrNameAt(index, &namespaceID,
00145                                           getter_AddRefs(name),
00146                                           getter_AddRefs(prefix));
00147 
00148         // We need to ignore XMLNS attributes.
00149         if (namespaceID != kNameSpaceID_XMLNS) {
00150             mPosition.mIndex = index;
00151 
00152             return PR_TRUE;
00153         }
00154     }
00155     return PR_FALSE;
00156 }
00157 
00158 PRBool
00159 txXPathTreeWalker::moveToFirstChild()
00160 {
00161     if (mPosition.isAttribute()) {
00162         return PR_FALSE;
00163     }
00164 
00165     if (mPosition.isDocument()) {
00166         nsIContent* child = mPosition.mDocument->GetChildAt(0);
00167         if (!child) {
00168             return PR_FALSE;
00169         }
00170         mPosition.mIndex = txXPathNode::eContent;
00171         mPosition.mContent = child;
00172 
00173         NS_ASSERTION(mCurrentIndex == kUnknownIndex && !mDescendants.Count(),
00174                      "we shouldn't have any position info at the document");
00175         mCurrentIndex = 0;
00176 
00177         return PR_TRUE;
00178     }
00179 
00180     nsIContent* child = mPosition.mContent->GetChildAt(0);
00181     if (!child) {
00182         return PR_FALSE;
00183     }
00184     mPosition.mContent = child;
00185 
00186     NS_ASSERTION(mCurrentIndex != kUnknownIndex || !mDescendants.Count(),
00187                  "Index should be known if parents index are");
00188     if (mCurrentIndex != kUnknownIndex &&
00189         !mDescendants.AppendValue(mCurrentIndex)) {
00190         mDescendants.Clear();
00191     }
00192     mCurrentIndex = 0;
00193 
00194     return PR_TRUE;
00195 }
00196 
00197 PRBool
00198 txXPathTreeWalker::moveToLastChild()
00199 {
00200     if (mPosition.isAttribute()) {
00201         return PR_FALSE;
00202     }
00203 
00204     if (mPosition.isDocument()) {
00205         PRUint32 total = mPosition.mDocument->GetChildCount();
00206         if (!total) {
00207             return PR_FALSE;
00208         }
00209 
00210         mPosition.mIndex = txXPathNode::eContent;
00211         mPosition.mContent = mPosition.mDocument->GetChildAt(total - 1);
00212         NS_ASSERTION(mCurrentIndex == kUnknownIndex && !mDescendants.Count(),
00213                      "we shouldn't have any position info at the document");
00214         mCurrentIndex = total - 1;
00215 
00216         return PR_TRUE;
00217     }
00218 
00219     PRUint32 total = mPosition.mContent->GetChildCount();
00220     if (!total) {
00221         return PR_FALSE;
00222     }
00223     mPosition.mContent = mPosition.mContent->GetChildAt(total - 1);
00224 
00225     NS_ASSERTION(mCurrentIndex != kUnknownIndex || !mDescendants.Count(),
00226                  "Index should be known if parents index are");
00227     if (mCurrentIndex != kUnknownIndex &&
00228         !mDescendants.AppendValue(mCurrentIndex)) {
00229         mDescendants.Clear();
00230     }
00231     mCurrentIndex = total - 1;
00232 
00233     return PR_TRUE;
00234 }
00235 
00236 PRBool
00237 txXPathTreeWalker::moveToNextSibling()
00238 {
00239     if (!mPosition.isContent()) {
00240         return PR_FALSE;
00241     }
00242 
00243     return moveToSibling(1);
00244 }
00245 
00246 PRBool
00247 txXPathTreeWalker::moveToPreviousSibling()
00248 {
00249     if (!mPosition.isContent()) {
00250         return PR_FALSE;
00251     }
00252 
00253     return moveToSibling(-1);
00254 }
00255 
00256 PRBool
00257 txXPathTreeWalker::moveToParent()
00258 {
00259     if (mPosition.isDocument()) {
00260         return PR_FALSE;
00261     }
00262 
00263     if (mPosition.isAttribute()) {
00264         mPosition.mIndex = txXPathNode::eContent;
00265 
00266         return PR_TRUE;
00267     }
00268 
00269     nsIContent *parent = mPosition.mContent->GetParent();
00270     if (parent) {
00271         mPosition.mContent = parent;
00272         PRInt32 count = mDescendants.Count();
00273         if (count) {
00274             mCurrentIndex = mDescendants.ValueAt(--count);
00275             mDescendants.RemoveValueAt(count);
00276         }
00277         else {
00278             mCurrentIndex = kUnknownIndex;
00279         }
00280 
00281         return PR_TRUE;
00282     }
00283 
00284     nsIDocument* document = mPosition.mContent->GetDocument();
00285     if (!document) {
00286         return PR_FALSE;
00287     }
00288 
00289     mPosition.mIndex = txXPathNode::eDocument;
00290     mPosition.mDocument = document;
00291 
00292     return PR_TRUE;
00293 }
00294 
00295 PRBool
00296 txXPathTreeWalker::moveToSibling(PRInt32 aDir)
00297 {
00298     NS_ASSERTION(mPosition.isContent(),
00299                  "moveToSibling should only be called for content");
00300 
00301     nsIDocument* document;
00302     nsIContent* parent = mPosition.mContent->GetParent();
00303     if (!parent) {
00304         document = mPosition.mContent->GetDocument();
00305         if (!document) {
00306             return PR_FALSE;
00307         }
00308     }
00309     if (mCurrentIndex == kUnknownIndex) {
00310         mCurrentIndex = parent ? parent->IndexOf(mPosition.mContent)
00311                                : document->IndexOf(mPosition.mContent);
00312     }
00313 
00314     // if mCurrentIndex is 0 we rely on GetChildAt returning null for an
00315     // index of PRUint32(-1).
00316     PRUint32 newIndex = mCurrentIndex + aDir;
00317     nsIContent* newChild = parent ? parent->GetChildAt(newIndex) :
00318                                     document->GetChildAt(newIndex);
00319     if (!newChild) {
00320         return PR_FALSE;
00321     }
00322 
00323     mPosition.mContent = newChild;
00324     mCurrentIndex = newIndex;
00325 
00326     return PR_TRUE;
00327 }
00328 
00329 txXPathNode::txXPathNode(const txXPathNode& aNode) : mIndex(aNode.mIndex)
00330 {
00331     // Hopefully it's ok to access mContent through mDocument.
00332     mDocument = aNode.mDocument;
00333 }
00334 
00335 PRBool
00336 txXPathNode::operator==(const txXPathNode& aNode) const
00337 {
00338     if (mIndex != aNode.mIndex) {
00339         return PR_FALSE;
00340     }
00341 
00342     // Hopefully it's ok to access mContent through mDocument.
00343     return (mDocument == aNode.mDocument);
00344 }
00345 
00346 /* static */
00347 PRBool
00348 txXPathNodeUtils::getAttr(const txXPathNode& aNode, nsIAtom* aLocalName,
00349                           PRInt32 aNSID, nsAString& aValue)
00350 {
00351     if (aNode.isDocument() || aNode.isAttribute()) {
00352         return PR_FALSE;
00353     }
00354 
00355     nsresult rv = aNode.mContent->GetAttr(aNSID, aLocalName, aValue);
00356     return NS_SUCCEEDED(rv) && rv != NS_CONTENT_ATTR_NOT_THERE;
00357 }
00358 
00359 /* static */
00360 already_AddRefed<nsIAtom>
00361 txXPathNodeUtils::getLocalName(const txXPathNode& aNode)
00362 {
00363     if (aNode.isDocument()) {
00364         return nsnull;
00365     }
00366 
00367     if (aNode.isContent()) {
00368         if (aNode.mContent->IsContentOfType(nsIContent::eELEMENT)) {
00369             nsIAtom* localName = aNode.mContent->Tag();
00370             NS_ADDREF(localName);
00371 
00372             return localName;
00373         }
00374 
00375         if (aNode.mContent->IsContentOfType(nsIContent::ePROCESSING_INSTRUCTION)) {
00376             nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aNode.mContent);
00377             nsAutoString target;
00378             node->GetNodeName(target);
00379 
00380             return NS_NewAtom(target);
00381         }
00382 
00383         return nsnull;
00384     }
00385 
00386     nsIAtom* localName;
00387     nsCOMPtr<nsIAtom> prefix;
00388     PRInt32 namespaceID;
00389     aNode.mContent->GetAttrNameAt(aNode.mIndex, &namespaceID, &localName,
00390                                   getter_AddRefs(prefix));
00391 
00392     return localName;
00393 }
00394 
00395 /* static */
00396 void
00397 txXPathNodeUtils::getLocalName(const txXPathNode& aNode, nsAString& aLocalName)
00398 {
00399     if (aNode.isDocument()) {
00400         aLocalName.Truncate();
00401 
00402         return;
00403     }
00404 
00405     if (aNode.isContent()) {
00406         nsINodeInfo* nodeInfo = aNode.mContent->GetNodeInfo();
00407         if (nodeInfo) {
00408             nodeInfo->GetLocalName(aLocalName);
00409 
00410             // Check for html
00411             if (nodeInfo->NamespaceEquals(kNameSpaceID_None) &&
00412                 aNode.mContent->IsContentOfType(nsIContent::eHTML)) {
00413                 ToUpperCase(aLocalName);
00414             }
00415 
00416             return;
00417         }
00418 
00419         if (aNode.mContent->IsContentOfType(nsIContent::ePROCESSING_INSTRUCTION)) {
00420             // PIs don't have a nodeinfo but do have a name
00421             nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aNode.mContent);
00422             node->GetNodeName(aLocalName);
00423 
00424             return;
00425         }
00426 
00427         aLocalName.Truncate();
00428 
00429         return;
00430     }
00431 
00432     nsCOMPtr<nsIAtom> prefix, localName;
00433     PRInt32 namespaceID;
00434     aNode.mContent->GetAttrNameAt(aNode.mIndex, &namespaceID,
00435                                   getter_AddRefs(localName),
00436                                   getter_AddRefs(prefix));
00437     localName->ToString(aLocalName);
00438 
00439     // Check for html
00440     if (aNode.mContent->GetNodeInfo()->NamespaceEquals(kNameSpaceID_None) &&
00441         aNode.mContent->IsContentOfType(nsIContent::eHTML)) {
00442         ToUpperCase(aLocalName);
00443     }
00444 }
00445 
00446 /* static */
00447 void
00448 txXPathNodeUtils::getNodeName(const txXPathNode& aNode, nsAString& aName)
00449 {
00450     if (aNode.isDocument()) {
00451         aName.Truncate();
00452 
00453         return;
00454     }
00455 
00456     if (aNode.isContent()) {
00457         nsINodeInfo* nodeInfo = aNode.mContent->GetNodeInfo();
00458         if (nodeInfo) {
00459             nodeInfo->GetQualifiedName(aName);
00460 
00461             // Check for html
00462             if (nodeInfo->NamespaceEquals(kNameSpaceID_None) &&
00463                 aNode.mContent->IsContentOfType(nsIContent::eHTML)) {
00464                 ToUpperCase(aName);
00465             }
00466 
00467             return;
00468         }
00469 
00470         if (aNode.mContent->IsContentOfType(nsIContent::ePROCESSING_INSTRUCTION)) {
00471             // PIs don't have a nodeinfo but do have a name
00472             nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aNode.mContent);
00473             node->GetNodeName(aName);
00474 
00475             return;
00476         }
00477 
00478         aName.Truncate();
00479 
00480         return;
00481     }
00482 
00483     nsCOMPtr<nsIAtom> name, prefix;
00484     PRInt32 namespaceID;
00485     aNode.mContent->GetAttrNameAt(aNode.mIndex, &namespaceID,
00486                                   getter_AddRefs(name),
00487                                   getter_AddRefs(prefix));
00488 
00489 
00490     if (prefix) {
00491         prefix->ToString(aName);
00492         aName.Append(PRUnichar(':'));
00493     }
00494     else {
00495         aName.Truncate();
00496     }
00497 
00498     const char* attrName;
00499     name->GetUTF8String(&attrName);
00500     AppendUTF8toUTF16(attrName, aName);
00501 
00502     // Check for html
00503     if (aNode.mContent->GetNodeInfo()->NamespaceEquals(kNameSpaceID_None) &&
00504         aNode.mContent->IsContentOfType(nsIContent::eHTML)) {
00505         ToUpperCase(aName);
00506     }
00507 }
00508 
00509 /* static */
00510 PRInt32
00511 txXPathNodeUtils::getNamespaceID(const txXPathNode& aNode)
00512 {
00513     if (aNode.isDocument()) {
00514         return kNameSpaceID_None;
00515     }
00516 
00517     if (aNode.isContent()) {
00518         nsINodeInfo* ni = aNode.mContent->GetNodeInfo();
00519 
00520         return ni ? ni->NamespaceID() : kNameSpaceID_None;
00521     }
00522 
00523     nsCOMPtr<nsIAtom> name, prefix;
00524     PRInt32 namespaceID;
00525     aNode.mContent->GetAttrNameAt(aNode.mIndex, &namespaceID,
00526                                   getter_AddRefs(name),
00527                                   getter_AddRefs(prefix));
00528     return namespaceID;
00529 }
00530 
00531 /* static */
00532 void
00533 txXPathNodeUtils::getNamespaceURI(const txXPathNode& aNode, nsAString& aURI)
00534 {
00535     gTxNameSpaceManager->GetNameSpaceURI(getNamespaceID(aNode), aURI);
00536 }
00537 
00538 /* static */
00539 PRUint16
00540 txXPathNodeUtils::getNodeType(const txXPathNode& aNode)
00541 {
00542     if (aNode.isDocument()) {
00543         return txXPathNodeType::DOCUMENT_NODE;
00544     }
00545 
00546     if (aNode.isContent()) {
00547         PRUint16 type;
00548         nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aNode.mContent);
00549         node->GetNodeType(&type);
00550 
00551         return type;
00552     }
00553 
00554     return txXPathNodeType::ATTRIBUTE_NODE;
00555 }
00556 
00557 static void appendTextContent(nsIContent* aElement, nsAString& aResult)
00558 {
00559     nsIContent* content = aElement->GetChildAt(0);
00560     PRUint32 i = 0;
00561     while (content) {
00562         if (content->IsContentOfType(nsIContent::eELEMENT)) {
00563             appendTextContent(content, aResult);
00564         }
00565         else if (content->IsContentOfType(nsIContent::eTEXT)) {
00566             nsCOMPtr<nsITextContent> textContent = do_QueryInterface(content);
00567             textContent->AppendTextTo(aResult);
00568         }
00569         content = aElement->GetChildAt(++i);
00570     }
00571 }
00572 
00573 /* static */
00574 void
00575 txXPathNodeUtils::appendNodeValue(const txXPathNode& aNode, nsAString& aResult)
00576 {
00577     if (aNode.isAttribute()) {
00578         nsCOMPtr<nsIAtom> name, prefix;
00579         PRInt32 namespaceID;
00580         aNode.mContent->GetAttrNameAt(aNode.mIndex, &namespaceID,
00581                                       getter_AddRefs(name),
00582                                       getter_AddRefs(prefix));
00583 
00584         nsAutoString result;
00585         aNode.mContent->GetAttr(namespaceID, name, result);
00586         aResult.Append(result);
00587 
00588         return;
00589     }
00590 
00591     if (aNode.isDocument()) {
00592         nsIContent* content = aNode.mDocument->GetRootContent();
00593         if (content) {
00594             appendTextContent(content, aResult);
00595         }
00596 
00597         return;
00598     }
00599 
00600     if (aNode.mContent->IsContentOfType(nsIContent::eELEMENT)) {
00601         appendTextContent(aNode.mContent, aResult);
00602 
00603         return;
00604     }
00605 
00606     if (aNode.mContent->IsContentOfType(nsIContent::ePROCESSING_INSTRUCTION)) {
00607         nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aNode.mContent);
00608 
00609         nsAutoString result;
00610         node->GetNodeValue(result);
00611         aResult.Append(result);
00612 
00613         return;
00614     }
00615 
00616     nsCOMPtr<nsITextContent> textContent = do_QueryInterface(aNode.mContent);
00617     if (!textContent) {
00618         NS_ERROR("Unexpected nodetype.");
00619 
00620         return;
00621     }
00622 
00623     textContent->AppendTextTo(aResult);
00624 }
00625 
00626 /* static */
00627 PRBool
00628 txXPathNodeUtils::isWhitespace(const txXPathNode& aNode)
00629 {
00630     NS_ASSERTION(aNode.isContent(), "Wrong type!");
00631 
00632     nsCOMPtr<nsITextContent> textCont = do_QueryInterface(aNode.mContent);
00633     if (!textCont) {
00634         return PR_TRUE;
00635     }
00636     return textCont->IsOnlyWhitespace();
00637 }
00638 
00639 /* static */
00640 txXPathNode*
00641 txXPathNodeUtils::getDocument(const txXPathNode& aNode)
00642 {
00643     if (aNode.isDocument()) {
00644         return new txXPathNode(aNode);
00645     }
00646 
00647     nsIDocument* document = aNode.mContent->GetDocument();
00648     return document ? new txXPathNode(document) : nsnull;
00649 }
00650 
00651 /* static */
00652 txXPathNode*
00653 txXPathNodeUtils::getOwnerDocument(const txXPathNode& aNode)
00654 {
00655     if (aNode.isDocument()) {
00656         return new txXPathNode(aNode);
00657     }
00658 
00659     nsIDocument* document = aNode.mContent->GetDocument();
00660     if (!document) {
00661         nsINodeInfo* ni = aNode.mContent->GetNodeInfo();
00662         if (ni) {
00663           document = ni->GetDocument();
00664         }
00665     }
00666 
00667     return document ? new txXPathNode(document) : nsnull;
00668 }
00669 
00670 #ifndef HAVE_64BIT_OS
00671 #define kFmtSize 13
00672 #define kFmtSizeAttr 24
00673 const char gPrintfFmt[] = "id0x%08p";
00674 const char gPrintfFmtAttr[] = "id0x%08p-%010i";
00675 #else
00676 #define kFmtSize 21
00677 #define kFmtSizeAttr 32
00678 const char gPrintfFmt[] = "id0x%016p";
00679 const char gPrintfFmtAttr[] = "id0x%016p-%010i";
00680 #endif
00681 
00682 /* static */
00683 nsresult
00684 txXPathNodeUtils::getXSLTId(const txXPathNode& aNode,
00685                             nsAString& aResult)
00686 {
00687     if (aNode.isDocument()) {
00688         CopyASCIItoUTF16(nsPrintfCString(kFmtSize, gPrintfFmt,
00689                                          aNode.mDocument), aResult);
00690 
00691         return NS_OK;
00692     }
00693 
00694     if (aNode.isContent()) {
00695         CopyASCIItoUTF16(nsPrintfCString(kFmtSize, gPrintfFmt, aNode.mContent),
00696                          aResult);
00697 
00698         return NS_OK;
00699     }
00700 
00701     CopyASCIItoUTF16(nsPrintfCString(kFmtSizeAttr, gPrintfFmtAttr, aNode.mContent,
00702                                      aNode.mIndex), aResult);
00703 
00704     return NS_OK;
00705 }
00706 
00707 /* static */
00708 void
00709 txXPathNodeUtils::getBaseURI(const txXPathNode& aNode, nsAString& aURI)
00710 {
00711     nsCOMPtr<nsIDOM3Node> node;
00712     if (aNode.isDocument()) {
00713         node = do_QueryInterface(aNode.mDocument);
00714     }
00715     else {
00716         node = do_QueryInterface(aNode.mContent);
00717     }
00718 
00719     if (node) {
00720         node->GetBaseURI(aURI);
00721     }
00722     else {
00723         aURI.Truncate();
00724     }
00725 }
00726 
00727 /* static */
00728 PRIntn
00729 txXPathNodeUtils::comparePosition(const txXPathNode& aNode,
00730                                   const txXPathNode& aOtherNode)
00731 {
00732     // First check for equal nodes or attribute-nodes on the same element.
00733     if (aNode.mContent == aOtherNode.mContent) {
00734         if (aNode.mIndex == aOtherNode.mIndex) {
00735             return 0;
00736         }
00737 
00738         if (aNode.isContent() || (!aOtherNode.isContent() &&
00739                                   aNode.mIndex < aOtherNode.mIndex)) {
00740             return -1;
00741         }
00742 
00743         return 1;
00744     }
00745 
00746     // Get document for both nodes.
00747     nsIDocument* document = aNode.isDocument() ?
00748                             aNode.mDocument :
00749                             aNode.mContent->GetDocument();
00750 
00751     nsIDocument* otherDocument = aOtherNode.isDocument() ?
00752                                  aOtherNode.mDocument :
00753                                  aOtherNode.mContent->GetDocument();
00754 
00755     // If the nodes have different ownerdocuments, compare the document
00756     // pointers.
00757     if (document && otherDocument && document != otherDocument) {
00758         return document > otherDocument ? 1 : -1;
00759     }
00760 
00761     // Every node comes after its ownerdocument.
00762     if (aNode.isDocument()) {
00763         return -1;
00764     }
00765 
00766     if (aOtherNode.isDocument()) {
00767         return 1;
00768     }
00769 
00770     // Get parents up the tree.
00771     nsAutoVoidArray parents, otherParents;
00772     nsIContent* content = aNode.mContent;
00773     nsIContent* otherContent = aOtherNode.mContent;
00774     nsIContent* parent, *otherParent;
00775     PRInt32 index, otherIndex;
00776     while (content && otherContent) {
00777         parent = content->GetParent();
00778         otherParent = otherContent->GetParent();
00779 
00780         // Hopefully this is a common case.
00781         if (parent == otherParent) {
00782             if (parent) {
00783                 index = (PRUint32)parent->IndexOf(content);
00784                 otherIndex = (PRUint32)parent->IndexOf(otherContent);
00785             }
00786             else {
00787                 if (!document) {
00788                     if (!otherDocument) {
00789                         // Neither aNode nor aOtherNode are not in a document,
00790                         // compare their top ancestors.
00791                         return content > otherContent ? 1 : -1;
00792                     }
00793 
00794                     // aNode is not in the tree, compare its top ancestor with
00795                     // aOtherNode's document.
00796                     return (void*)content > (void*)otherDocument ? 1 : -1;
00797                 }
00798                 else if (!otherDocument) {
00799                     // aOtherNode is not in a document, compare its top
00800                     // ancestor with aNode's document.
00801                     return (void*)document > (void*)otherContent ? 1 : -1;
00802                 }
00803 
00804                 // Both nodes are in the same document.
00805                 index = (PRUint32)document->IndexOf(content);
00806                 otherIndex = (PRUint32)document->IndexOf(otherContent);
00807             }
00808 
00809             return index < otherIndex ? -1 : 1;
00810         }
00811 
00812         parents.AppendElement(content);
00813         otherParents.AppendElement(otherContent);
00814         content = parent;
00815         otherContent = otherParent;
00816     }
00817 
00818     while (content) {
00819         parents.AppendElement(content);
00820         content = content->GetParent();
00821     }
00822     while (otherContent) {
00823         otherParents.AppendElement(otherContent);
00824         otherContent = otherContent->GetParent();
00825     }
00826 
00827     if (!document) {
00828         if (!otherDocument) {
00829             // Neither aNode nor aOtherNode are not in a document, compare
00830             // their top ancestors.
00831             return parents.ElementAt(parents.Count() - 1) >
00832                    otherParents.ElementAt(otherParents.Count() - 1) ? 1 : -1;
00833         }
00834 
00835         // aNode is not in the tree, compare its top ancestor with aOtherNode's
00836         // document.
00837         return parents.ElementAt(parents.Count() - 1) > otherDocument ? 1 : -1;
00838     }
00839     else if (!otherDocument) {
00840         // aOtherNode is not in a document, compare its top
00841         // ancestor with aNode's document.
00842         return document >
00843                otherParents.ElementAt(otherParents.Count() - 1) ? 1 : -1;
00844     }
00845 
00846     // Walk back down along the parent-chains until we find where they split.
00847     PRInt32 total = parents.Count() - 1;
00848     PRInt32 otherTotal = otherParents.Count() - 1;
00849     NS_ASSERTION(total != otherTotal, "Can't have same number of parents");
00850 
00851     PRInt32 lastIndex = PR_MIN(total, otherTotal);
00852     PRInt32 i;
00853     parent = nsnull;
00854     for (i = 0; i <= lastIndex; ++i) {
00855         content = NS_STATIC_CAST(nsIContent*, parents.ElementAt(total - i));
00856         otherContent = NS_STATIC_CAST(nsIContent*,
00857                                       otherParents.ElementAt(otherTotal - i));
00858         if (content != otherContent) {
00859             if (parent) {
00860                 index = (PRUint32)parent->IndexOf(content);
00861                 otherIndex = (PRUint32)parent->IndexOf(otherContent);
00862             }
00863             else {
00864                 index = (PRUint32)document->IndexOf(content);
00865                 otherIndex = (PRUint32)document->IndexOf(otherContent);
00866             }
00867             NS_ASSERTION(index != otherIndex && index >= 0 && otherIndex >= 0,
00868                          "invalid index in compareTreePosition");
00869             return index < otherIndex ? -1 : 1;
00870         }
00871 
00872         parent = content;
00873     }
00874 
00875     // One node is a descendant of the other. The one with the shortest
00876     // parent-chain is first in the document.
00877     return total < otherTotal ? -1 : 1;
00878 }
00879 
00880 /* static */
00881 txXPathNode*
00882 txXPathNativeNode::createXPathNode(nsIDOMNode* aNode)
00883 {
00884     PRUint16 nodeType;
00885     aNode->GetNodeType(&nodeType);
00886 
00887     if (nodeType == nsIDOMNode::ATTRIBUTE_NODE) {
00888         nsCOMPtr<nsIAttribute> attr = do_QueryInterface(aNode);
00889         NS_ASSERTION(attr, "doesn't implement nsIAttribute");
00890 
00891         nsINodeInfo *nodeInfo = attr->NodeInfo();
00892         nsIContent *parent = attr->GetContent();
00893         if (!parent) {
00894             return nsnull;
00895         }
00896 
00897         nsCOMPtr<nsIAtom> attName, attPrefix;
00898         PRInt32 attNS;
00899 
00900         PRUint32 i, total = parent->GetAttrCount();
00901         for (i = 0; i < total; ++i) {
00902             parent->GetAttrNameAt(i, &attNS, getter_AddRefs(attName),
00903                                   getter_AddRefs(attPrefix));
00904             if (nodeInfo->Equals(attName, attNS)) {
00905                 return new txXPathNode(parent, i);
00906             }
00907         }
00908 
00909         NS_ERROR("Couldn't find the attribute in its parent!");
00910 
00911         return nsnull;
00912     }
00913 
00914     if (nodeType == nsIDOMNode::DOCUMENT_NODE) {
00915         nsCOMPtr<nsIDocument> document = do_QueryInterface(aNode);
00916         return new txXPathNode(document);
00917     }
00918 
00919     nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
00920     return new txXPathNode(content);
00921 }
00922 
00923 /* static */
00924 txXPathNode*
00925 txXPathNativeNode::createXPathNode(nsIDOMDocument* aDocument)
00926 {
00927     nsCOMPtr<nsIDocument> document = do_QueryInterface(aDocument);
00928     return new txXPathNode(document);
00929 }
00930 
00931 /* static */
00932 nsresult
00933 txXPathNativeNode::getNode(const txXPathNode& aNode, nsIDOMNode** aResult)
00934 {
00935     if (aNode.isDocument()) {
00936         return CallQueryInterface(aNode.mDocument, aResult);
00937     }
00938 
00939     if (aNode.isContent()) {
00940         return CallQueryInterface(aNode.mContent, aResult);
00941     }
00942 
00943     PRInt32 namespaceID;
00944     nsCOMPtr<nsIAtom> name, prefix;
00945     aNode.mContent->GetAttrNameAt(aNode.mIndex, &namespaceID,
00946                                   getter_AddRefs(name),
00947                                   getter_AddRefs(prefix));
00948 
00949     nsAutoString namespaceURI, localname;
00950     gTxNameSpaceManager->GetNameSpaceURI(namespaceID, namespaceURI);
00951     name->ToString(localname);
00952 
00953     nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aNode.mContent);
00954     nsCOMPtr<nsIDOMAttr> attr;
00955     element->GetAttributeNodeNS(namespaceURI, localname,
00956                                 getter_AddRefs(attr));
00957 
00958     return CallQueryInterface(attr, aResult);
00959 }
00960 
00961 /* static */
00962 nsIContent*
00963 txXPathNativeNode::getContent(const txXPathNode& aNode)
00964 {
00965     NS_ASSERTION(aNode.isContent(),
00966                  "Only call getContent on nsIContent wrappers!");
00967     return aNode.mContent;
00968 }
00969 
00970 /* static */
00971 nsIDocument*
00972 txXPathNativeNode::getDocument(const txXPathNode& aNode)
00973 {
00974     NS_ASSERTION(aNode.isDocument(),
00975                  "Only call getDocument on nsIDocument wrappers!");
00976     return aNode.mDocument;
00977 }