1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Peter Van der Beken.
19 : * Portions created by the Initial Developer are Copyright (C) 2003
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Peter Van der Beken <peterv@propagandism.org>
24 : *
25 : *
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.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : #include "mozilla/Util.h"
41 :
42 : #include "nsIAtom.h"
43 : #include "nsGkAtoms.h"
44 : #include "txExecutionState.h"
45 : #include "txExpr.h"
46 : #include "txIXPathContext.h"
47 : #include "txNodeSet.h"
48 : #include "txOutputFormat.h"
49 : #include "txRtfHandler.h"
50 : #include "txXPathTreeWalker.h"
51 : #include "nsPrintfCString.h"
52 : #include "nsComponentManagerUtils.h"
53 : #include "nsContentCID.h"
54 : #include "nsContentCreatorFunctions.h"
55 : #include "nsIContent.h"
56 : #include "nsIDOMDocumentFragment.h"
57 : #include "txMozillaXMLOutput.h"
58 :
59 : using namespace mozilla;
60 :
61 : class txStylesheetCompilerState;
62 :
63 : // ------------------------------------------------------------------
64 : // Utility functions
65 : // ------------------------------------------------------------------
66 :
67 : static nsresult
68 0 : convertRtfToNode(txIEvalContext *aContext, txResultTreeFragment *aRtf)
69 : {
70 : txExecutionState* es =
71 0 : static_cast<txExecutionState*>(aContext->getPrivateContext());
72 0 : if (!es) {
73 0 : NS_ERROR("Need txExecutionState!");
74 :
75 0 : return NS_ERROR_UNEXPECTED;
76 : }
77 :
78 0 : const txXPathNode& document = es->getSourceDocument();
79 :
80 0 : nsIDocument *doc = txXPathNativeNode::getDocument(document);
81 0 : nsCOMPtr<nsIDOMDocumentFragment> domFragment;
82 0 : nsresult rv = NS_NewDocumentFragment(getter_AddRefs(domFragment),
83 0 : doc->NodeInfoManager());
84 0 : NS_ENSURE_SUCCESS(rv, rv);
85 :
86 0 : txOutputFormat format;
87 0 : txMozillaXMLOutput mozHandler(&format, domFragment, true);
88 :
89 0 : rv = aRtf->flushToHandler(&mozHandler);
90 0 : NS_ENSURE_SUCCESS(rv, rv);
91 :
92 0 : rv = mozHandler.closePrevious(true);
93 0 : NS_ENSURE_SUCCESS(rv, rv);
94 :
95 : // The txResultTreeFragment will own this.
96 : const txXPathNode* node = txXPathNativeNode::createXPathNode(domFragment,
97 0 : true);
98 0 : NS_ENSURE_TRUE(node, NS_ERROR_OUT_OF_MEMORY);
99 :
100 0 : aRtf->setNode(node);
101 :
102 0 : return NS_OK;
103 : }
104 :
105 : static nsresult
106 0 : createTextNode(txIEvalContext *aContext, nsString& aValue,
107 : txXPathNode* *aResult)
108 : {
109 : txExecutionState* es =
110 0 : static_cast<txExecutionState*>(aContext->getPrivateContext());
111 0 : if (!es) {
112 0 : NS_ERROR("Need txExecutionState!");
113 :
114 0 : return NS_ERROR_UNEXPECTED;
115 : }
116 :
117 0 : const txXPathNode& document = es->getSourceDocument();
118 :
119 0 : nsIDocument *doc = txXPathNativeNode::getDocument(document);
120 0 : nsCOMPtr<nsIContent> text;
121 0 : nsresult rv = NS_NewTextNode(getter_AddRefs(text), doc->NodeInfoManager());
122 0 : NS_ENSURE_SUCCESS(rv, rv);
123 :
124 0 : rv = text->SetText(aValue, false);
125 0 : NS_ENSURE_SUCCESS(rv, rv);
126 :
127 0 : *aResult = txXPathNativeNode::createXPathNode(text, true);
128 0 : NS_ENSURE_TRUE(*aResult, NS_ERROR_OUT_OF_MEMORY);
129 :
130 0 : return NS_OK;
131 : }
132 :
133 : static nsresult
134 0 : createDocFragment(txIEvalContext *aContext, nsIContent** aResult)
135 : {
136 : txExecutionState* es =
137 0 : static_cast<txExecutionState*>(aContext->getPrivateContext());
138 0 : if (!es) {
139 0 : NS_ERROR("Need txExecutionState!");
140 :
141 0 : return NS_ERROR_UNEXPECTED;
142 : }
143 :
144 0 : const txXPathNode& document = es->getSourceDocument();
145 0 : nsIDocument *doc = txXPathNativeNode::getDocument(document);
146 0 : nsCOMPtr<nsIDOMDocumentFragment> domFragment;
147 0 : nsresult rv = NS_NewDocumentFragment(getter_AddRefs(domFragment),
148 0 : doc->NodeInfoManager());
149 0 : NS_ENSURE_SUCCESS(rv, rv);
150 :
151 0 : return CallQueryInterface(domFragment, aResult);
152 : }
153 :
154 : static nsresult
155 0 : createAndAddToResult(nsIAtom* aName, const nsSubstring& aValue,
156 : txNodeSet* aResultSet, nsIContent* aResultHolder)
157 : {
158 0 : NS_ASSERTION(aResultHolder->IsNodeOfType(nsINode::eDOCUMENT_FRAGMENT) &&
159 : aResultHolder->OwnerDoc(),
160 : "invalid result-holder");
161 :
162 0 : nsIDocument* doc = aResultHolder->OwnerDoc();
163 0 : nsCOMPtr<nsIContent> elem;
164 0 : nsresult rv = doc->CreateElem(nsDependentAtomString(aName),
165 : nsnull, kNameSpaceID_None,
166 0 : getter_AddRefs(elem));
167 0 : NS_ENSURE_SUCCESS(rv, rv);
168 :
169 0 : nsCOMPtr<nsIContent> text;
170 0 : rv = NS_NewTextNode(getter_AddRefs(text), doc->NodeInfoManager());
171 0 : NS_ENSURE_SUCCESS(rv, rv);
172 :
173 0 : rv = text->SetText(aValue, false);
174 0 : NS_ENSURE_SUCCESS(rv, rv);
175 :
176 0 : rv = elem->AppendChildTo(text, false);
177 0 : NS_ENSURE_SUCCESS(rv, rv);
178 :
179 0 : rv = aResultHolder->AppendChildTo(elem, false);
180 0 : NS_ENSURE_SUCCESS(rv, rv);
181 :
182 : nsAutoPtr<txXPathNode> xpathNode(
183 0 : txXPathNativeNode::createXPathNode(elem, true));
184 0 : NS_ENSURE_TRUE(xpathNode, NS_ERROR_OUT_OF_MEMORY);
185 :
186 0 : aResultSet->append(*xpathNode);
187 :
188 0 : return NS_OK;
189 : }
190 :
191 : // Need to update this array if types are added to the ResultType enum in
192 : // txAExprResult.
193 : static const char * const sTypes[] = {
194 : "node-set",
195 : "boolean",
196 : "number",
197 : "string",
198 : "RTF"
199 : };
200 :
201 : // ------------------------------------------------------------------
202 : // Function implementations
203 : // ------------------------------------------------------------------
204 :
205 : struct txEXSLTFunctionDescriptor
206 : {
207 : PRInt8 mMinParams;
208 : PRInt8 mMaxParams;
209 : Expr::ResultType mReturnType;
210 : nsIAtom** mName;
211 : PRInt32 mNamespaceID;
212 : const char* mNamespaceURI;
213 : };
214 :
215 : static const char kEXSLTCommonNS[] = "http://exslt.org/common";
216 : static const char kEXSLTSetsNS[] = "http://exslt.org/sets";
217 : static const char kEXSLTStringsNS[] = "http://exslt.org/strings";
218 : static const char kEXSLTMathNS[] = "http://exslt.org/math";
219 : static const char kEXSLTDatesAndTimesNS[] = "http://exslt.org/dates-and-times";
220 :
221 : // The order of this table must be the same as the
222 : // txEXSLTFunctionCall::eType enum
223 : static txEXSLTFunctionDescriptor descriptTable[] =
224 : {
225 : { 1, 1, Expr::NODESET_RESULT, &nsGkAtoms::nodeSet, 0, kEXSLTCommonNS }, // NODE_SET
226 : { 1, 1, Expr::STRING_RESULT, &nsGkAtoms::objectType, 0, kEXSLTCommonNS }, // OBJECT_TYPE
227 : { 2, 2, Expr::NODESET_RESULT, &nsGkAtoms::difference, 0, kEXSLTSetsNS }, // DIFFERENCE
228 : { 1, 1, Expr::NODESET_RESULT, &nsGkAtoms::distinct, 0, kEXSLTSetsNS }, // DISTINCT
229 : { 2, 2, Expr::BOOLEAN_RESULT, &nsGkAtoms::hasSameNode, 0, kEXSLTSetsNS }, // HAS_SAME_NODE
230 : { 2, 2, Expr::NODESET_RESULT, &nsGkAtoms::intersection, 0, kEXSLTSetsNS }, // INTERSECTION
231 : { 2, 2, Expr::NODESET_RESULT, &nsGkAtoms::leading, 0, kEXSLTSetsNS }, // LEADING
232 : { 2, 2, Expr::NODESET_RESULT, &nsGkAtoms::trailing, 0, kEXSLTSetsNS }, // TRAILING
233 : { 1, 1, Expr::STRING_RESULT, &nsGkAtoms::concat, 0, kEXSLTStringsNS }, // CONCAT
234 : { 1, 2, Expr::STRING_RESULT, &nsGkAtoms::split, 0, kEXSLTStringsNS }, // SPLIT
235 : { 1, 2, Expr::STRING_RESULT, &nsGkAtoms::tokenize, 0, kEXSLTStringsNS }, // TOKENIZE
236 : { 1, 1, Expr::NUMBER_RESULT, &nsGkAtoms::max, 0, kEXSLTMathNS }, // MAX
237 : { 1, 1, Expr::NUMBER_RESULT, &nsGkAtoms::min, 0, kEXSLTMathNS }, // MIN
238 : { 1, 1, Expr::NODESET_RESULT, &nsGkAtoms::highest, 0, kEXSLTMathNS }, // HIGHEST
239 : { 1, 1, Expr::NODESET_RESULT, &nsGkAtoms::lowest, 0, kEXSLTMathNS }, // LOWEST
240 : { 0, 0, Expr::STRING_RESULT, &nsGkAtoms::dateTime, 0, kEXSLTDatesAndTimesNS }, // DATE_TIME
241 :
242 : };
243 :
244 : class txEXSLTFunctionCall : public FunctionCall
245 0 : {
246 : public:
247 : // The order of this enum must be the same as the descriptTable
248 : // table above
249 : enum eType {
250 : // Set functions
251 : NODE_SET,
252 : OBJECT_TYPE,
253 : DIFFERENCE,
254 : DISTINCT,
255 : HAS_SAME_NODE,
256 : INTERSECTION,
257 : LEADING,
258 : TRAILING,
259 : CONCAT,
260 : SPLIT,
261 : TOKENIZE,
262 : MAX,
263 : MIN,
264 : HIGHEST,
265 : LOWEST,
266 : DATE_TIME
267 : };
268 :
269 0 : txEXSLTFunctionCall(eType aType)
270 0 : : mType(aType)
271 : {
272 0 : }
273 :
274 : TX_DECL_FUNCTION
275 :
276 : private:
277 : eType mType;
278 : };
279 :
280 : nsresult
281 0 : txEXSLTFunctionCall::evaluate(txIEvalContext *aContext,
282 : txAExprResult **aResult)
283 : {
284 0 : *aResult = nsnull;
285 0 : if (!requireParams(descriptTable[mType].mMinParams,
286 : descriptTable[mType].mMaxParams,
287 0 : aContext)) {
288 0 : return NS_ERROR_XPATH_BAD_ARGUMENT_COUNT;
289 : }
290 :
291 0 : nsresult rv = NS_OK;
292 0 : switch (mType) {
293 : case NODE_SET:
294 : {
295 0 : nsRefPtr<txAExprResult> exprResult;
296 0 : rv = mParams[0]->evaluate(aContext, getter_AddRefs(exprResult));
297 0 : NS_ENSURE_SUCCESS(rv, rv);
298 :
299 0 : if (exprResult->getResultType() == txAExprResult::NODESET) {
300 0 : exprResult.swap(*aResult);
301 : }
302 : else {
303 0 : nsRefPtr<txNodeSet> resultSet;
304 0 : rv = aContext->recycler()->
305 0 : getNodeSet(getter_AddRefs(resultSet));
306 0 : NS_ENSURE_SUCCESS(rv, rv);
307 :
308 0 : if (exprResult->getResultType() ==
309 : txAExprResult::RESULT_TREE_FRAGMENT) {
310 : txResultTreeFragment *rtf =
311 : static_cast<txResultTreeFragment*>
312 0 : (exprResult.get());
313 :
314 0 : const txXPathNode *node = rtf->getNode();
315 0 : if (!node) {
316 0 : rv = convertRtfToNode(aContext, rtf);
317 0 : NS_ENSURE_SUCCESS(rv, rv);
318 :
319 0 : node = rtf->getNode();
320 : }
321 :
322 0 : resultSet->append(*node);
323 : }
324 : else {
325 0 : nsAutoString value;
326 0 : exprResult->stringValue(value);
327 :
328 0 : nsAutoPtr<txXPathNode> node;
329 : rv = createTextNode(aContext, value,
330 0 : getter_Transfers(node));
331 0 : NS_ENSURE_SUCCESS(rv, rv);
332 :
333 0 : resultSet->append(*node);
334 : }
335 :
336 0 : NS_ADDREF(*aResult = resultSet);
337 : }
338 :
339 0 : return NS_OK;
340 : }
341 : case OBJECT_TYPE:
342 : {
343 0 : nsRefPtr<txAExprResult> exprResult;
344 0 : nsresult rv = mParams[0]->evaluate(aContext,
345 0 : getter_AddRefs(exprResult));
346 0 : NS_ENSURE_SUCCESS(rv, rv);
347 :
348 0 : nsRefPtr<StringResult> strRes;
349 0 : rv = aContext->recycler()->getStringResult(getter_AddRefs(strRes));
350 0 : NS_ENSURE_SUCCESS(rv, rv);
351 :
352 0 : AppendASCIItoUTF16(sTypes[exprResult->getResultType()],
353 0 : strRes->mValue);
354 :
355 0 : NS_ADDREF(*aResult = strRes);
356 :
357 0 : return NS_OK;
358 : }
359 : case DIFFERENCE:
360 : case INTERSECTION:
361 : {
362 0 : nsRefPtr<txNodeSet> nodes1;
363 0 : rv = evaluateToNodeSet(mParams[0], aContext,
364 0 : getter_AddRefs(nodes1));
365 0 : NS_ENSURE_SUCCESS(rv, rv);
366 :
367 0 : nsRefPtr<txNodeSet> nodes2;
368 0 : rv = evaluateToNodeSet(mParams[1], aContext,
369 0 : getter_AddRefs(nodes2));
370 0 : NS_ENSURE_SUCCESS(rv, rv);
371 :
372 0 : nsRefPtr<txNodeSet> resultSet;
373 0 : rv = aContext->recycler()->getNodeSet(getter_AddRefs(resultSet));
374 0 : NS_ENSURE_SUCCESS(rv, rv);
375 :
376 0 : bool insertOnFound = mType == INTERSECTION;
377 :
378 0 : PRInt32 searchPos = 0;
379 0 : PRInt32 i, len = nodes1->size();
380 0 : for (i = 0; i < len; ++i) {
381 0 : const txXPathNode& node = nodes1->get(i);
382 0 : PRInt32 foundPos = nodes2->indexOf(node, searchPos);
383 0 : if (foundPos >= 0) {
384 0 : searchPos = foundPos + 1;
385 : }
386 :
387 0 : if ((foundPos >= 0) == insertOnFound) {
388 0 : rv = resultSet->append(node);
389 0 : NS_ENSURE_SUCCESS(rv, rv);
390 : }
391 : }
392 :
393 0 : NS_ADDREF(*aResult = resultSet);
394 :
395 0 : return NS_OK;
396 : }
397 : case DISTINCT:
398 : {
399 0 : nsRefPtr<txNodeSet> nodes;
400 0 : rv = evaluateToNodeSet(mParams[0], aContext,
401 0 : getter_AddRefs(nodes));
402 0 : NS_ENSURE_SUCCESS(rv, rv);
403 :
404 0 : nsRefPtr<txNodeSet> resultSet;
405 0 : rv = aContext->recycler()->getNodeSet(getter_AddRefs(resultSet));
406 0 : NS_ENSURE_SUCCESS(rv, rv);
407 :
408 0 : nsTHashtable<nsStringHashKey> hash;
409 0 : if (!hash.Init()) {
410 0 : return NS_ERROR_OUT_OF_MEMORY;
411 : }
412 :
413 0 : PRInt32 i, len = nodes->size();
414 0 : for (i = 0; i < len; ++i) {
415 0 : nsAutoString str;
416 0 : const txXPathNode& node = nodes->get(i);
417 0 : txXPathNodeUtils::appendNodeValue(node, str);
418 0 : if (!hash.GetEntry(str)) {
419 0 : if (!hash.PutEntry(str)) {
420 0 : return NS_ERROR_OUT_OF_MEMORY;
421 : }
422 0 : rv = resultSet->append(node);
423 0 : NS_ENSURE_SUCCESS(rv, rv);
424 : }
425 : }
426 :
427 0 : NS_ADDREF(*aResult = resultSet);
428 :
429 0 : return NS_OK;
430 : }
431 : case HAS_SAME_NODE:
432 : {
433 0 : nsRefPtr<txNodeSet> nodes1;
434 0 : rv = evaluateToNodeSet(mParams[0], aContext,
435 0 : getter_AddRefs(nodes1));
436 0 : NS_ENSURE_SUCCESS(rv, rv);
437 :
438 0 : nsRefPtr<txNodeSet> nodes2;
439 0 : rv = evaluateToNodeSet(mParams[1], aContext,
440 0 : getter_AddRefs(nodes2));
441 0 : NS_ENSURE_SUCCESS(rv, rv);
442 :
443 0 : bool found = false;
444 0 : PRInt32 i, len = nodes1->size();
445 0 : for (i = 0; i < len; ++i) {
446 0 : if (nodes2->contains(nodes1->get(i))) {
447 0 : found = true;
448 0 : break;
449 : }
450 : }
451 :
452 0 : aContext->recycler()->getBoolResult(found, aResult);
453 :
454 0 : return NS_OK;
455 : }
456 : case LEADING:
457 : case TRAILING:
458 : {
459 0 : nsRefPtr<txNodeSet> nodes1;
460 0 : rv = evaluateToNodeSet(mParams[0], aContext,
461 0 : getter_AddRefs(nodes1));
462 0 : NS_ENSURE_SUCCESS(rv, rv);
463 :
464 0 : nsRefPtr<txNodeSet> nodes2;
465 0 : rv = evaluateToNodeSet(mParams[1], aContext,
466 0 : getter_AddRefs(nodes2));
467 0 : NS_ENSURE_SUCCESS(rv, rv);
468 :
469 0 : if (nodes2->isEmpty()) {
470 0 : *aResult = nodes1;
471 0 : NS_ADDREF(*aResult);
472 :
473 0 : return NS_OK;
474 : }
475 :
476 0 : nsRefPtr<txNodeSet> resultSet;
477 0 : rv = aContext->recycler()->getNodeSet(getter_AddRefs(resultSet));
478 0 : NS_ENSURE_SUCCESS(rv, rv);
479 :
480 0 : PRInt32 end = nodes1->indexOf(nodes2->get(0));
481 0 : if (end >= 0) {
482 0 : PRInt32 i = 0;
483 0 : if (mType == TRAILING) {
484 0 : i = end + 1;
485 0 : end = nodes1->size();
486 : }
487 0 : for (; i < end; ++i) {
488 0 : rv = resultSet->append(nodes1->get(i));
489 0 : NS_ENSURE_SUCCESS(rv, rv);
490 : }
491 : }
492 :
493 0 : NS_ADDREF(*aResult = resultSet);
494 :
495 0 : return NS_OK;
496 : }
497 : case CONCAT:
498 : {
499 0 : nsRefPtr<txNodeSet> nodes;
500 0 : rv = evaluateToNodeSet(mParams[0], aContext,
501 0 : getter_AddRefs(nodes));
502 0 : NS_ENSURE_SUCCESS(rv, rv);
503 :
504 0 : nsAutoString str;
505 0 : PRInt32 i, len = nodes->size();
506 0 : for (i = 0; i < len; ++i) {
507 0 : txXPathNodeUtils::appendNodeValue(nodes->get(i), str);
508 : }
509 :
510 0 : return aContext->recycler()->getStringResult(str, aResult);
511 : }
512 : case SPLIT:
513 : case TOKENIZE:
514 : {
515 : // Evaluate parameters
516 0 : nsAutoString string;
517 0 : rv = mParams[0]->evaluateToString(aContext, string);
518 0 : NS_ENSURE_SUCCESS(rv, rv);
519 :
520 0 : nsAutoString pattern;
521 0 : if (mParams.Length() == 2) {
522 0 : rv = mParams[1]->evaluateToString(aContext, pattern);
523 0 : NS_ENSURE_SUCCESS(rv, rv);
524 : }
525 0 : else if (mType == SPLIT) {
526 0 : pattern.AssignLiteral(" ");
527 : }
528 : else {
529 0 : pattern.AssignLiteral("\t\r\n ");
530 : }
531 :
532 : // Set up holders for the result
533 0 : nsCOMPtr<nsIContent> docFrag;
534 0 : rv = createDocFragment(aContext, getter_AddRefs(docFrag));
535 0 : NS_ENSURE_SUCCESS(rv, rv);
536 :
537 0 : nsRefPtr<txNodeSet> resultSet;
538 0 : rv = aContext->recycler()->getNodeSet(getter_AddRefs(resultSet));
539 0 : NS_ENSURE_SUCCESS(rv, rv);
540 :
541 : PRUint32 tailIndex;
542 :
543 : // Start splitting
544 0 : if (pattern.IsEmpty()) {
545 0 : nsString::const_char_iterator start = string.BeginReading();
546 0 : nsString::const_char_iterator end = string.EndReading();
547 0 : for (; start < end; ++start) {
548 : rv = createAndAddToResult(nsGkAtoms::token,
549 0 : Substring(start, start + 1),
550 0 : resultSet, docFrag);
551 0 : NS_ENSURE_SUCCESS(rv, rv);
552 : }
553 :
554 0 : tailIndex = string.Length();
555 : }
556 0 : else if (mType == SPLIT) {
557 0 : nsAString::const_iterator strStart, strEnd;
558 0 : string.BeginReading(strStart);
559 0 : string.EndReading(strEnd);
560 0 : nsAString::const_iterator start = strStart, end = strEnd;
561 :
562 0 : while (FindInReadable(pattern, start, end)) {
563 0 : if (start != strStart) {
564 : rv = createAndAddToResult(nsGkAtoms::token,
565 0 : Substring(strStart, start),
566 0 : resultSet, docFrag);
567 0 : NS_ENSURE_SUCCESS(rv, rv);
568 : }
569 0 : strStart = start = end;
570 0 : end = strEnd;
571 : }
572 :
573 0 : tailIndex = strStart.get() - string.get();
574 : }
575 : else {
576 0 : PRInt32 found, start = 0;
577 0 : while ((found = string.FindCharInSet(pattern, start)) !=
578 : kNotFound) {
579 0 : if (found != start) {
580 : rv = createAndAddToResult(nsGkAtoms::token,
581 : Substring(string, start,
582 0 : found - start),
583 0 : resultSet, docFrag);
584 0 : NS_ENSURE_SUCCESS(rv, rv);
585 : }
586 0 : start = found + 1;
587 : }
588 :
589 0 : tailIndex = start;
590 : }
591 :
592 : // Add tail if needed
593 0 : if (tailIndex != (PRUint32)string.Length()) {
594 : rv = createAndAddToResult(nsGkAtoms::token,
595 0 : Substring(string, tailIndex),
596 0 : resultSet, docFrag);
597 0 : NS_ENSURE_SUCCESS(rv, rv);
598 : }
599 :
600 0 : NS_ADDREF(*aResult = resultSet);
601 :
602 0 : return NS_OK;
603 : }
604 : case MAX:
605 : case MIN:
606 : {
607 0 : nsRefPtr<txNodeSet> nodes;
608 0 : rv = evaluateToNodeSet(mParams[0], aContext,
609 0 : getter_AddRefs(nodes));
610 0 : NS_ENSURE_SUCCESS(rv, rv);
611 :
612 0 : if (nodes->isEmpty()) {
613 0 : return aContext->recycler()->
614 0 : getNumberResult(txDouble::NaN, aResult);
615 : }
616 :
617 0 : bool findMax = mType == MAX;
618 :
619 : double res = findMax ? txDouble::NEGATIVE_INFINITY :
620 0 : txDouble::POSITIVE_INFINITY;
621 0 : PRInt32 i, len = nodes->size();
622 0 : for (i = 0; i < len; ++i) {
623 0 : nsAutoString str;
624 0 : txXPathNodeUtils::appendNodeValue(nodes->get(i), str);
625 0 : double val = txDouble::toDouble(str);
626 0 : if (txDouble::isNaN(val)) {
627 0 : res = txDouble::NaN;
628 : break;
629 : }
630 :
631 0 : if (findMax ? (val > res) : (val < res)) {
632 0 : res = val;
633 : }
634 : }
635 :
636 0 : return aContext->recycler()->getNumberResult(res, aResult);
637 : }
638 : case HIGHEST:
639 : case LOWEST:
640 : {
641 0 : nsRefPtr<txNodeSet> nodes;
642 0 : rv = evaluateToNodeSet(mParams[0], aContext,
643 0 : getter_AddRefs(nodes));
644 0 : NS_ENSURE_SUCCESS(rv, rv);
645 :
646 0 : if (nodes->isEmpty()) {
647 0 : NS_ADDREF(*aResult = nodes);
648 :
649 0 : return NS_OK;
650 : }
651 :
652 0 : nsRefPtr<txNodeSet> resultSet;
653 0 : rv = aContext->recycler()->getNodeSet(getter_AddRefs(resultSet));
654 0 : NS_ENSURE_SUCCESS(rv, rv);
655 :
656 0 : bool findMax = mType == HIGHEST;
657 : double res = findMax ? txDouble::NEGATIVE_INFINITY :
658 0 : txDouble::POSITIVE_INFINITY;
659 0 : PRInt32 i, len = nodes->size();
660 0 : for (i = 0; i < len; ++i) {
661 0 : nsAutoString str;
662 0 : const txXPathNode& node = nodes->get(i);
663 0 : txXPathNodeUtils::appendNodeValue(node, str);
664 0 : double val = txDouble::toDouble(str);
665 0 : if (txDouble::isNaN(val)) {
666 0 : resultSet->clear();
667 : break;
668 : }
669 0 : if (findMax ? (val > res) : (val < res)) {
670 0 : resultSet->clear();
671 0 : res = val;
672 : }
673 :
674 0 : if (res == val) {
675 0 : rv = resultSet->append(node);
676 0 : NS_ENSURE_SUCCESS(rv, rv);
677 : }
678 : }
679 :
680 0 : NS_ADDREF(*aResult = resultSet);
681 :
682 0 : return NS_OK;
683 : }
684 : case DATE_TIME:
685 : {
686 : // http://exslt.org/date/functions/date-time/
687 : // format: YYYY-MM-DDTTHH:MM:SS.sss+00:00
688 0 : char formatstr[] = "%04hd-%02ld-%02ldT%02ld:%02ld:%02ld.%03ld%c%02ld:%02ld";
689 0 : const size_t max = sizeof("YYYY-MM-DDTHH:MM:SS.sss+00:00");
690 :
691 : PRExplodedTime prtime;
692 0 : PR_ExplodeTime(PR_Now(), PR_LocalTimeParameters, &prtime);
693 :
694 : PRInt32 offset = (prtime.tm_params.tp_gmt_offset +
695 0 : prtime.tm_params.tp_dst_offset) / 60;
696 :
697 0 : bool isneg = offset < 0;
698 0 : if (isneg) offset = -offset;
699 :
700 : StringResult* strRes;
701 0 : rv = aContext->recycler()->getStringResult(&strRes);
702 0 : NS_ENSURE_SUCCESS(rv, rv);
703 :
704 : CopyASCIItoUTF16(nsPrintfCString(max, formatstr,
705 : prtime.tm_year, prtime.tm_month + 1, prtime.tm_mday,
706 : prtime.tm_hour, prtime.tm_min, prtime.tm_sec,
707 : prtime.tm_usec / 10000,
708 0 : isneg ? '-' : '+', offset / 60, offset % 60), strRes->mValue);
709 :
710 0 : *aResult = strRes;
711 :
712 0 : return NS_OK;
713 : }
714 : }
715 :
716 : aContext->receiveError(NS_LITERAL_STRING("Internal error"),
717 : NS_ERROR_UNEXPECTED);
718 : return NS_ERROR_UNEXPECTED;
719 : }
720 :
721 : Expr::ResultType
722 0 : txEXSLTFunctionCall::getReturnType()
723 : {
724 0 : return descriptTable[mType].mReturnType;
725 : }
726 :
727 : bool
728 0 : txEXSLTFunctionCall::isSensitiveTo(ContextSensitivity aContext)
729 : {
730 0 : if (mType == NODE_SET || mType == SPLIT || mType == TOKENIZE) {
731 0 : return (aContext & PRIVATE_CONTEXT) || argsSensitiveTo(aContext);
732 : }
733 0 : return argsSensitiveTo(aContext);
734 : }
735 :
736 : #ifdef TX_TO_STRING
737 : nsresult
738 0 : txEXSLTFunctionCall::getNameAtom(nsIAtom **aAtom)
739 : {
740 0 : NS_ADDREF(*aAtom = *descriptTable[mType].mName);
741 0 : return NS_OK;
742 : }
743 : #endif
744 :
745 : extern nsresult
746 0 : TX_ConstructEXSLTFunction(nsIAtom *aName,
747 : PRInt32 aNamespaceID,
748 : txStylesheetCompilerState* aState,
749 : FunctionCall **aResult)
750 : {
751 : PRUint32 i;
752 0 : for (i = 0; i < ArrayLength(descriptTable); ++i) {
753 0 : txEXSLTFunctionDescriptor& desc = descriptTable[i];
754 0 : if (aName == *desc.mName && aNamespaceID == desc.mNamespaceID) {
755 : *aResult = new txEXSLTFunctionCall(
756 0 : static_cast<txEXSLTFunctionCall::eType>(i));
757 :
758 0 : return *aResult ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
759 : }
760 : }
761 :
762 0 : return NS_ERROR_XPATH_UNKNOWN_FUNCTION;
763 : }
764 :
765 : extern bool
766 1404 : TX_InitEXSLTFunction()
767 : {
768 : PRUint32 i;
769 23868 : for (i = 0; i < ArrayLength(descriptTable); ++i) {
770 22464 : txEXSLTFunctionDescriptor& desc = descriptTable[i];
771 44928 : NS_ConvertASCIItoUTF16 namespaceURI(desc.mNamespaceURI);
772 : desc.mNamespaceID =
773 22464 : txNamespaceManager::getNamespaceID(namespaceURI);
774 :
775 22464 : if (desc.mNamespaceID == kNameSpaceID_Unknown) {
776 0 : return false;
777 : }
778 : }
779 :
780 1404 : return true;
781 : }
|