extra: import at 3.0.1 beta 1
[mozilla-extra.git] / extensions / xmlextras / pointers / src / nsXPointer.cpp
blob81706298e8d140129c83e8acd50f02d40dca002a
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 */
3 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
16 * The Original Code is mozilla.org code.
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 2003
21 * the Initial Developer. All Rights Reserved.
23 * Contributor(s):
24 * Heikki Toivonen <heikki@netscape.com> (original author)
26 * Alternatively, the contents of this file may be used under the terms of
27 * either the GNU General Public License Version 2 or later (the "GPL"), or
28 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
40 /**
41 * Implementation for the XPointer family of specifications, practically the
42 * XPointer Processor. The processor can call optional modules that implement
43 * some XPointer schemes that were not implemented in this file. Please note
44 * that implementation of the xmlns scheme is left to the optional scheme
45 * implementations - all the information they need will be passed in.
47 * The framework:
48 * http://www.w3.org/TR/xptr-framework/
49 * The element scheme:
50 * http://www.w3.org/TR/xptr-element/
52 * Additionally this module implements 'fixptr' scheme for the FIXptr
53 * W3C proposal:
54 * http://lists.w3.org/Archives/Public/www-xml-linking-comments/2001AprJun/att-0074/01-NOTE-FIXptr-20010425.htm
57 // TODO:
58 // - xpointer scheme
61 #include "nsIDOMNode.h"
62 #include "nsIDOMNodeList.h"
63 #include "nsIDOMElement.h"
64 #include "nsIDOMDocument.h"
65 #include "nsIDOMClassInfo.h"
66 #include "nsCOMPtr.h"
67 #include "nsXPointer.h"
68 #include "nsIModifyableXPointer.h"
69 #include "nsISupports.h"
70 #include "nsISupportsUtils.h"
71 #include "nsIXPointer.h"
72 #include "nsFIXptr.h"
73 #include "nsIServiceManager.h"
74 #include "nsReadableUtils.h"
76 #include "nsContentCID.h"
77 static NS_DEFINE_IID(kRangeCID, NS_RANGE_CID);
79 nsXPointerResult::nsXPointerResult()
83 nsXPointerResult::~nsXPointerResult()
87 NS_INTERFACE_MAP_BEGIN(nsXPointerResult)
88 NS_INTERFACE_MAP_ENTRY(nsIXPointerResult)
89 NS_INTERFACE_MAP_ENTRY(nsIModifyableXPointerResult)
90 NS_INTERFACE_MAP_ENTRY(nsISupports)
91 NS_INTERFACE_MAP_ENTRY_EXTERNAL_DOM_CLASSINFO(XPointerResult)
92 NS_INTERFACE_MAP_END
94 NS_IMPL_ADDREF(nsXPointerResult)
95 NS_IMPL_RELEASE(nsXPointerResult)
97 NS_IMETHODIMP
98 nsXPointerResult::AppendRange(nsIDOMRange* aRange)
100 NS_ENSURE_ARG(aRange);
102 if (!mArray.AppendObject(aRange)) {
103 return NS_ERROR_OUT_OF_MEMORY;
106 return NS_OK;
109 NS_IMETHODIMP
110 nsXPointerResult::Item(PRUint32 aIndex, nsIDOMRange** aReturn)
112 NS_ENSURE_ARG_POINTER(aReturn);
114 if (aIndex >= (PRUint32)mArray.Count()) {
115 return NS_ERROR_FAILURE;
118 *aReturn = mArray.ObjectAt(aIndex);
119 NS_IF_ADDREF(*aReturn);
121 return NS_OK;
124 NS_IMETHODIMP
125 nsXPointerResult::GetLength(PRUint32* aLength)
127 NS_ENSURE_ARG_POINTER(aLength);
129 *aLength = mArray.Count();
131 return NS_OK;
134 nsresult NS_NewXPointerResult(nsIXPointerResult **aResult)
136 NS_ENSURE_ARG_POINTER(aResult);
138 *aResult = new nsXPointerResult();
139 if (!*aResult) {
140 return NS_ERROR_OUT_OF_MEMORY;
143 NS_ADDREF(*aResult);
145 return NS_OK;
148 static nsresult NS_NewXPointerResult(nsIDOMRange *aRange,
149 nsIXPointerResult **aResult)
151 NS_ENSURE_ARG(aRange);
152 NS_ENSURE_ARG_POINTER(aResult);
154 nsCOMPtr<nsXPointerResult> result(new nsXPointerResult());
155 if (!result) {
156 return NS_ERROR_OUT_OF_MEMORY;
159 nsresult rv = result->AppendRange(aRange);
160 if (NS_FAILED(rv)) {
161 return rv;
164 *aResult = result.get();
165 NS_ADDREF(*aResult);
167 return NS_OK;
170 static nsresult NS_NewXPointerResult(nsIDOMNode *aNode,
171 nsIXPointerResult **aResult)
173 NS_ENSURE_ARG(aNode);
174 NS_ENSURE_ARG_POINTER(aResult);
176 nsCOMPtr<nsIDOMRange> range(do_CreateInstance(kRangeCID));
177 if (!range) {
178 return NS_ERROR_OUT_OF_MEMORY;
181 nsresult rv = range->SelectNode(aNode);
182 if (NS_FAILED(rv)) {
183 return rv;
186 return NS_NewXPointerResult(range, aResult);
190 // nsXPointerSchemeContext
192 class nsXPointerSchemeContext : public nsIXPointerSchemeContext
194 public:
195 nsXPointerSchemeContext() {}
196 virtual ~nsXPointerSchemeContext() {}
198 NS_DECL_ISUPPORTS
199 NS_DECL_NSIXPOINTERSCHEMECONTEXT
201 nsresult Append(const nsAString &aScheme, const nsAString &aData);
203 private:
204 nsStringArray mSchemes;
205 nsStringArray mDatas;
208 NS_IMPL_ISUPPORTS1(nsXPointerSchemeContext, nsIXPointerSchemeContext)
210 NS_IMETHODIMP
211 nsXPointerSchemeContext::GetCount(PRUint32 *aCount)
213 NS_ENSURE_ARG_POINTER(aCount);
215 *aCount = mSchemes.Count();
217 return NS_OK;
221 nsresult
222 nsXPointerSchemeContext::Append(const nsAString &aScheme,
223 const nsAString &aData)
225 if (!mSchemes.AppendString(aScheme)) {
226 return NS_ERROR_OUT_OF_MEMORY;
229 if (!mDatas.AppendString(aData)) {
230 // Keep mDatas and mSchemes in sync
231 mSchemes.RemoveStringAt(mSchemes.Count() - 1);
232 return NS_ERROR_OUT_OF_MEMORY;
235 return NS_OK;
238 NS_IMETHODIMP
239 nsXPointerSchemeContext::GetSchemeData(PRUint32 aIndex,
240 nsAString &aScheme,
241 nsAString &aData)
243 if (aIndex >= (PRUint32)mSchemes.Count()) {
244 aScheme.Truncate();
245 aData.Truncate();
247 return NS_ERROR_FAILURE;
250 mSchemes.StringAt(aIndex, aScheme);
251 mDatas.StringAt(aIndex, aData);
253 return NS_OK;
256 // XPointer
258 nsXPointer::nsXPointer()
262 nsXPointer::~nsXPointer()
266 NS_IMPL_ISUPPORTS1(nsXPointer, nsIXPointerEvaluator)
268 static nsresult GetNextSchemeNameAndData(nsString& aExpression,
269 nsString &aScheme,
270 nsString& aData)
272 aScheme.Truncate();
273 aData.Truncate();
275 PRInt32 lp = aExpression.FindChar('(');
276 if (lp < 1) {
277 return NS_ERROR_FAILURE; // format |scheme + '(' [ + data + ] + ')'| required
280 PRInt32 i = lp + 1, len = aExpression.Length();
281 if (i >= len) {
282 return NS_ERROR_FAILURE; // format |scheme + '(' [ + data + ] + ')'| required
285 aScheme = Substring(aExpression, 0, lp);
286 aScheme.CompressWhitespace(PR_TRUE, PR_FALSE);
287 if (aScheme.FindCharInSet(" \t\r\n") > 0) {
288 return NS_ERROR_FAILURE; // scheme name can't contain ws (we'd really need to check a lot more...)
291 // XXX perf: Switch to string iterators
292 PRBool escapeOn = PR_FALSE;
293 PRInt32 balance = 1;
294 for (; i < len; ++i) {
295 // Circumflex is the escape character that can precede ^, ( and ) only
296 if (aExpression[i] == '^') {
297 if (!escapeOn) {
298 escapeOn = PR_TRUE;
299 continue;
301 } else if (escapeOn) {
302 if ((aExpression[i] != '(') && (aExpression[i] != ')')) {
303 return NS_ERROR_FAILURE; // illegal use of ^
305 } else if (aExpression[i] == '(') {
306 ++balance;
307 } else if (aExpression[i] == ')') {
308 if (--balance == 0) {
309 aExpression.Cut(0, i + 1);
310 break;
314 aData.Append(aExpression[i]);
315 escapeOn = PR_FALSE;
318 if (balance != 0) {
319 return NS_ERROR_FAILURE; // format |scheme + '(' [ + data + ] + ')'| required
322 return NS_OK;
325 NS_IMETHODIMP
326 nsXPointer::Evaluate(nsIDOMDocument *aDocument,
327 const nsAString& aExpression,
328 nsIXPointerResult **aResult)
330 NS_ENSURE_ARG_POINTER(aDocument);
331 NS_ENSURE_ARG_POINTER(aResult);
332 *aResult = nsnull;
334 nsresult rv = NS_OK;
336 if (aExpression.FindChar('(') < 0) {
337 // Must be shorthand, i.e. plain id
338 nsCOMPtr<nsIDOMElement> element;
339 aDocument->GetElementById(aExpression, getter_AddRefs(element));
340 if (element) {
341 rv = NS_NewXPointerResult(element, aResult);
343 return rv;
346 nsAutoString expression(aExpression), scheme, data;
348 NS_NAMED_LITERAL_STRING(element, "element");
349 NS_NAMED_LITERAL_STRING(fixptr, "fixptr");
350 NS_NAMED_LITERAL_CSTRING(baseSchemeProgID, NS_XPOINTER_SCHEME_PROCESSOR_BASE);
351 nsCOMPtr<nsXPointerSchemeContext> contextSchemeDataArray(new nsXPointerSchemeContext());
352 if (!contextSchemeDataArray) {
353 return NS_ERROR_OUT_OF_MEMORY;
356 // Keep trying the schemes from left to right until one finds a subresource
357 while (!expression.IsEmpty()) {
358 rv = GetNextSchemeNameAndData(expression, scheme, data);
359 if (NS_FAILED(rv))
360 break;
362 // Built in schemes
363 if (scheme.Equals(element)) {
364 // We implement element scheme by using the FIXptr processor.
365 // Check there are no parenthesis (legal in FIXptr data).
366 if (data.FindChar('(') < 0) {
367 nsCOMPtr<nsIDOMRange> range;
368 nsCOMPtr<nsIFIXptrEvaluator> e(new nsFIXptr());
369 if (!e)
370 return NS_ERROR_OUT_OF_MEMORY;
371 rv = e->Evaluate(aDocument, data, getter_AddRefs(range));
372 if (NS_FAILED(rv))
373 break;
374 if (range) {
375 return NS_NewXPointerResult(range, aResult);
378 } else if (scheme.Equals(fixptr)) {
379 nsCOMPtr<nsIDOMRange> range;
380 nsCOMPtr<nsIFIXptrEvaluator> e(new nsFIXptr());
381 if (!e)
382 return NS_ERROR_OUT_OF_MEMORY;
383 rv = e->Evaluate(aDocument, data, getter_AddRefs(range));
384 if (NS_FAILED(rv))
385 break;
386 if (range) {
387 return NS_NewXPointerResult(range, aResult);
389 } else {
390 // Add-on schemes
391 nsCAutoString progid(baseSchemeProgID);
392 AppendUTF16toUTF8(scheme, progid);
393 nsCOMPtr<nsIXPointerSchemeProcessor> p(do_CreateInstance(progid.get()));
394 if (p) {
395 rv = p->Evaluate(aDocument, contextSchemeDataArray, data, aResult);
396 if (NS_FAILED(rv))
397 break;
398 if (*aResult)
399 return NS_OK;
403 rv = contextSchemeDataArray->Append(scheme, data);
404 if (NS_FAILED(rv))
405 break;
409 return rv;