Back to index

lightning-sunbird  0.9+nobinonly
nsFIXptr.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
00002 */
00003 /* ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is mozilla.org code.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications Corporation.
00020  * Portions created by the Initial Developer are Copyright (C) 2001
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   Heikki Toivonen <heikki@netscape.com> (original author)
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either the GNU General Public License Version 2 or later (the "GPL"), or
00028  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00046 #include "nsIDOMDocument.h"
00047 #include "nsIDOMRange.h"
00048 #include "nsIDOMNode.h"
00049 #include "nsIDOMElement.h"
00050 #include "nsIDOMNodeList.h"
00051 #include "nsCOMPtr.h"
00052 #include "nsIServiceManager.h"
00053 #include "nsFIXptr.h"
00054 #include "nsCRT.h"
00055 #include "nsString.h"
00056 
00057 #include "nsContentCID.h"
00058 static NS_DEFINE_IID(kRangeCID,     NS_RANGE_CID);
00059 
00060 nsFIXptr::nsFIXptr()
00061 {
00062 }
00063 
00064 nsFIXptr::~nsFIXptr()
00065 {
00066 }
00067 
00068 NS_IMPL_ISUPPORTS1(nsFIXptr, nsIFIXptrEvaluator)
00069 
00070 // Get nth child element
00071 static nsresult
00072 GetChild(nsIDOMNode *aParent, PRInt32 aChildNum, nsIDOMNode **aChild)
00073 {
00074   NS_ENSURE_ARG_POINTER(aParent);
00075   NS_ENSURE_ARG_POINTER(aChild);
00076 
00077   *aChild = nsnull;
00078   nsCOMPtr<nsIDOMNodeList> list;
00079   aParent->GetChildNodes(getter_AddRefs(list));
00080   if (!list)
00081     return NS_OK;
00082   
00083   PRUint32 count;
00084   list->GetLength(&count);
00085   PRUint32 i;
00086   PRInt32 curChildNum = 0;
00087   for (i = 0; i < count; i++) {
00088     nsCOMPtr<nsIDOMNode> node;
00089     list->Item(i, getter_AddRefs(node));
00090     if (!node)
00091       break;
00092     PRUint16 nodeType;
00093     node->GetNodeType(&nodeType);
00094     if (nodeType == nsIDOMNode::ELEMENT_NODE) {
00095       curChildNum++;
00096     }
00097     if (curChildNum == aChildNum) {
00098       *aChild = node;
00099       NS_ADDREF(*aChild);
00100       break;
00101     }
00102   }
00103   return NS_OK;
00104 }
00105 
00106 // Get range for char index
00107 static nsresult
00108 GetCharRange(nsIDOMNode *aParent, PRInt32 aCharNum, nsIDOMRange **aRange)
00109 {
00110   NS_ENSURE_ARG_POINTER(aParent);
00111   NS_ENSURE_ARG_POINTER(aRange);
00112 
00113   *aRange = nsnull;
00114   nsCOMPtr<nsIDOMNodeList> list;
00115   aParent->GetChildNodes(getter_AddRefs(list));
00116   if (!list)
00117     return NS_OK;
00118 
00119   PRUint32 count;
00120   list->GetLength(&count);
00121   PRUint32 i;
00122   PRInt32 maxCharNum = 0;
00123   PRInt32 prevCharNum = 0;
00124   for (i = 0; i < count; i++) {
00125     nsCOMPtr<nsIDOMNode> node;
00126     list->Item(i, getter_AddRefs(node));
00127     if (!node)
00128       break;
00129     PRUint16 nodeType;
00130     node->GetNodeType(&nodeType);
00131     if (nodeType & (nsIDOMNode::TEXT_NODE | nsIDOMNode::CDATA_SECTION_NODE)) {
00132       nsAutoString value;
00133       node->GetNodeValue(value);
00134       maxCharNum += value.Length();
00135     }
00136     if (maxCharNum >= aCharNum) {
00137       nsCOMPtr<nsIDOMRange> range(do_CreateInstance(kRangeCID));
00138       if (!range)
00139         return NS_ERROR_OUT_OF_MEMORY;
00140       range->SetStart(node, aCharNum - prevCharNum);
00141       range->SetEnd(node, aCharNum - prevCharNum + 1);
00142       *aRange = range;
00143       NS_ADDREF(*aRange);
00144       break;
00145     }
00146     prevCharNum = maxCharNum;
00147   }
00148   return NS_OK;
00149 }
00150 
00151 // Get node by tumbler
00152 static nsresult
00153 GetTumblerNode(nsIDOMNode *aParent, const nsString &aTumbler,
00154                nsIDOMNode **aNode)
00155 {
00156   NS_ENSURE_ARG_POINTER(aParent);
00157   NS_ENSURE_ARG_POINTER(aNode);
00158 
00159   *aNode = nsnull;
00160   nsAutoString tumbler(aTumbler);
00161   if (!tumbler.IsEmpty() && tumbler[0] == '/')
00162     tumbler.Cut(0, 1);
00163 
00164   nsCOMPtr<nsIDOMNode> node(aParent);
00165   while (!tumbler.IsEmpty() && node) {
00166     PRInt32 sep = tumbler.FindChar('/');
00167     if (sep > 0) {
00168       nsAutoString num;
00169       tumbler.Left(num, sep);
00170       PRInt32 error;
00171       PRInt32 n = num.ToInteger(&error);
00172       if (n <= 0) {
00173         node = nsnull;
00174         break;
00175       }
00176       nsCOMPtr<nsIDOMNode> child;
00177       GetChild(node, n, getter_AddRefs(child));
00178       node = child;
00179     } else {
00180       // Last
00181       PRInt32 error;
00182       PRInt32 n = tumbler.ToInteger(&error);
00183       if (n <= 0) {
00184         node = nsnull;
00185         break;
00186       }
00187       nsCOMPtr<nsIDOMNode> child;
00188       GetChild(node, n, getter_AddRefs(child));
00189       node = child;
00190       break;
00191     }
00192     tumbler.Cut(0, sep + 1);
00193   }
00194   *aNode = node;
00195   NS_IF_ADDREF(*aNode);
00196   return NS_OK;
00197 }
00198 
00199 static nsresult
00200 GetRange(nsIDOMDocument *aDocument, const nsAString& aExpression,
00201          nsIDOMRange **aRange)
00202 {
00203   nsresult rv = NS_OK;
00204   nsCOMPtr<nsIDOMNode> node;
00205   if (nsCRT::IsAsciiAlpha(aExpression.First())) {
00206     // name
00207     nsAutoString id;
00208     const nsAutoString expression(aExpression);
00209     PRInt32 sep = expression.FindCharInSet("/(");
00210     if (sep > 0) {
00211       expression.Left(id,sep);
00212       nsCOMPtr<nsIDOMElement> element;
00213       aDocument->GetElementById(id, getter_AddRefs(element));
00214       node = do_QueryInterface(element);
00215       if (node) {
00216         if (expression[sep] == '/') {
00217           // tumbler
00218           nsAutoString tumbler;
00219           expression.Mid(tumbler, sep, expression.Length());
00220           sep = tumbler.FindChar('(');
00221           if (sep > 0)
00222             tumbler.Truncate(sep);
00223           nsCOMPtr<nsIDOMNode> child;
00224           GetTumblerNode(node, tumbler, getter_AddRefs(child));
00225           node = child;
00226         }
00227         // char
00228         sep = expression.FindChar('(');
00229         if (sep > 0) {
00230           nsAutoString charNum(aExpression);
00231           if (charNum[charNum.Length() -1] == ')') {
00232             charNum.Truncate(charNum.Length() - 1);
00233             charNum.Cut(0, sep + 1);
00234             PRInt32 error;
00235             PRInt32 n = charNum.ToInteger(&error);
00236             if (n < 1)
00237               return NS_OK;
00238             rv = GetCharRange(node, n - 1, aRange);
00239             if (!*aRange) {
00240               node = nsnull;
00241             }
00242           }
00243         }
00244       }
00245     } else {
00246       // just name
00247       nsCOMPtr<nsIDOMElement> element;
00248       aDocument->GetElementById(expression, getter_AddRefs(element));
00249       node = do_QueryInterface(element);
00250     }
00251   } else if (aExpression.First() == '/') {
00252     // Starts with tumbler
00253     node = do_QueryInterface(aDocument);
00254     nsCOMPtr<nsIDOMNode> child;
00255     nsAutoString tumbler(aExpression);
00256     PRInt32 sep = tumbler.FindChar('(');
00257     if (sep > 0)
00258       tumbler.Truncate(sep);
00259     GetTumblerNode(node, tumbler, getter_AddRefs(child));
00260     node = child;
00261     // char
00262     sep = aExpression.FindChar('(');
00263     if (sep > 0) {
00264       nsAutoString charNum(aExpression);
00265       if (charNum[charNum.Length() -1] == ')') {
00266         charNum.Truncate(charNum.Length() - 1);
00267         charNum.Cut(0, sep + 1);
00268         PRInt32 error;
00269         PRInt32 n = charNum.ToInteger(&error);
00270         if (n < 1)
00271           return NS_OK;
00272         rv = GetCharRange(node, n - 1, aRange);
00273         if (!*aRange) {
00274           node = nsnull;
00275         }
00276       }
00277     }
00278   } else {
00279     // Not FIXptr
00280   }
00281 
00282   if (NS_SUCCEEDED(rv) && !*aRange && node) {
00283     nsCOMPtr<nsIDOMRange> range(do_CreateInstance(kRangeCID));  
00284     if (!range)
00285       return NS_ERROR_OUT_OF_MEMORY;
00286     range->SelectNode(node);
00287     *aRange = range;
00288     NS_ADDREF(*aRange);
00289   }
00290 
00291   return rv;
00292 }
00293 
00301 NS_IMETHODIMP
00302 nsFIXptr::Evaluate(nsIDOMDocument *aDocument,
00303                    const nsAString& aExpression,
00304                    nsIDOMRange **aRange)
00305 {
00306   NS_ENSURE_ARG_POINTER(aDocument);
00307   NS_ENSURE_ARG_POINTER(aRange);
00308   *aRange = nsnull;
00309 
00310   nsresult rv;
00311 
00312   PRInt32 split = aExpression.FindChar(',');
00313   if (split >= 0) {
00314     nsCOMPtr<nsIDOMRange> range1, range2;
00315 
00316     const nsDependentSubstring &expr1 = Substring(aExpression, 0, split);
00317     const nsDependentSubstring &expr2 =
00318       Substring(aExpression, split + 1, aExpression.Length() - (split + 1));
00319 
00320     rv = GetRange(aDocument, expr1, getter_AddRefs(range1)); 
00321     if (!range1)
00322       return rv;
00323 
00324     rv = GetRange(aDocument, expr2, getter_AddRefs(range2));
00325     if (!range2)
00326       return rv;
00327 
00328     nsCOMPtr<nsIDOMNode> begin, end;
00329     PRInt32 beginOffset, endOffset;
00330     range1->GetStartContainer(getter_AddRefs(begin));
00331     range1->GetStartOffset(&beginOffset);
00332     range2->GetEndContainer(getter_AddRefs(end));
00333     range2->GetEndOffset(&endOffset);
00334 
00335     nsCOMPtr<nsIDOMRange> range(do_CreateInstance(kRangeCID, &rv));
00336     if (NS_FAILED(rv))
00337       return rv;
00338 
00339     range->SetStart(begin, beginOffset);
00340     range->SetEnd(end, endOffset);
00341     *aRange = range;
00342     NS_ADDREF(*aRange);
00343   } else {
00344     rv = GetRange(aDocument, aExpression, aRange); 
00345   }
00346 
00347   return rv;
00348 }