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 TransforMiiX XSLT processor code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * The MITRE Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 1999
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Keith Visco <kvisco@ziplink.net> (Original Author)
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either the GNU General Public License Version 2 or later (the "GPL"), or
27 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #include "mozilla/Util.h"
40 :
41 : #include "txExpr.h"
42 : #include "nsAutoPtr.h"
43 : #include "txNodeSet.h"
44 : #include "nsGkAtoms.h"
45 : #include "txIXPathContext.h"
46 : #include "nsWhitespaceTokenizer.h"
47 : #include "txXPathTreeWalker.h"
48 : #include <math.h>
49 : #include "txStringUtils.h"
50 : #include "txXMLUtils.h"
51 :
52 : using namespace mozilla;
53 :
54 : struct txCoreFunctionDescriptor
55 : {
56 : PRInt8 mMinParams;
57 : PRInt8 mMaxParams;
58 : Expr::ResultType mReturnType;
59 : nsIAtom** mName;
60 : };
61 :
62 : // This must be ordered in the same order as txCoreFunctionCall::eType.
63 : // If you change one, change the other.
64 : static const txCoreFunctionDescriptor descriptTable[] =
65 : {
66 : { 1, 1, Expr::NUMBER_RESULT, &nsGkAtoms::count }, // COUNT
67 : { 1, 1, Expr::NODESET_RESULT, &nsGkAtoms::id }, // ID
68 : { 0, 0, Expr::NUMBER_RESULT, &nsGkAtoms::last }, // LAST
69 : { 0, 1, Expr::STRING_RESULT, &nsGkAtoms::localName }, // LOCAL_NAME
70 : { 0, 1, Expr::STRING_RESULT, &nsGkAtoms::namespaceUri }, // NAMESPACE_URI
71 : { 0, 1, Expr::STRING_RESULT, &nsGkAtoms::name }, // NAME
72 : { 0, 0, Expr::NUMBER_RESULT, &nsGkAtoms::position }, // POSITION
73 :
74 : { 2, -1, Expr::STRING_RESULT, &nsGkAtoms::concat }, // CONCAT
75 : { 2, 2, Expr::BOOLEAN_RESULT, &nsGkAtoms::contains }, // CONTAINS
76 : { 0, 1, Expr::STRING_RESULT, &nsGkAtoms::normalizeSpace }, // NORMALIZE_SPACE
77 : { 2, 2, Expr::BOOLEAN_RESULT, &nsGkAtoms::startsWith }, // STARTS_WITH
78 : { 0, 1, Expr::STRING_RESULT, &nsGkAtoms::string }, // STRING
79 : { 0, 1, Expr::NUMBER_RESULT, &nsGkAtoms::stringLength }, // STRING_LENGTH
80 : { 2, 3, Expr::STRING_RESULT, &nsGkAtoms::substring }, // SUBSTRING
81 : { 2, 2, Expr::STRING_RESULT, &nsGkAtoms::substringAfter }, // SUBSTRING_AFTER
82 : { 2, 2, Expr::STRING_RESULT, &nsGkAtoms::substringBefore }, // SUBSTRING_BEFORE
83 : { 3, 3, Expr::STRING_RESULT, &nsGkAtoms::translate }, // TRANSLATE
84 :
85 : { 0, 1, Expr::NUMBER_RESULT, &nsGkAtoms::number }, // NUMBER
86 : { 1, 1, Expr::NUMBER_RESULT, &nsGkAtoms::round }, // ROUND
87 : { 1, 1, Expr::NUMBER_RESULT, &nsGkAtoms::floor }, // FLOOR
88 : { 1, 1, Expr::NUMBER_RESULT, &nsGkAtoms::ceiling }, // CEILING
89 : { 1, 1, Expr::NUMBER_RESULT, &nsGkAtoms::sum }, // SUM
90 :
91 : { 1, 1, Expr::BOOLEAN_RESULT, &nsGkAtoms::boolean }, // BOOLEAN
92 : { 0, 0, Expr::BOOLEAN_RESULT, &nsGkAtoms::_false }, // _FALSE
93 : { 1, 1, Expr::BOOLEAN_RESULT, &nsGkAtoms::lang }, // LANG
94 : { 1, 1, Expr::BOOLEAN_RESULT, &nsGkAtoms::_not }, // _NOT
95 : { 0, 0, Expr::BOOLEAN_RESULT, &nsGkAtoms::_true } // _TRUE
96 : };
97 :
98 :
99 : /*
100 : * Evaluates this Expr based on the given context node and processor state
101 : * @param context the context node for evaluation of this Expr
102 : * @param ps the ContextState containing the stack information needed
103 : * for evaluation
104 : * @return the result of the evaluation
105 : */
106 : nsresult
107 0 : txCoreFunctionCall::evaluate(txIEvalContext* aContext, txAExprResult** aResult)
108 : {
109 0 : *aResult = nsnull;
110 :
111 0 : if (!requireParams(descriptTable[mType].mMinParams,
112 : descriptTable[mType].mMaxParams,
113 0 : aContext)) {
114 0 : return NS_ERROR_XPATH_BAD_ARGUMENT_COUNT;
115 : }
116 :
117 0 : nsresult rv = NS_OK;
118 0 : switch (mType) {
119 : case COUNT:
120 : {
121 0 : nsRefPtr<txNodeSet> nodes;
122 0 : rv = evaluateToNodeSet(mParams[0], aContext,
123 0 : getter_AddRefs(nodes));
124 0 : NS_ENSURE_SUCCESS(rv, rv);
125 :
126 0 : return aContext->recycler()->getNumberResult(nodes->size(),
127 0 : aResult);
128 : }
129 : case ID:
130 : {
131 0 : nsRefPtr<txAExprResult> exprResult;
132 0 : rv = mParams[0]->evaluate(aContext, getter_AddRefs(exprResult));
133 0 : NS_ENSURE_SUCCESS(rv, rv);
134 :
135 0 : nsRefPtr<txNodeSet> resultSet;
136 0 : rv = aContext->recycler()->getNodeSet(getter_AddRefs(resultSet));
137 0 : NS_ENSURE_SUCCESS(rv, rv);
138 :
139 0 : txXPathTreeWalker walker(aContext->getContextNode());
140 :
141 0 : if (exprResult->getResultType() == txAExprResult::NODESET) {
142 : txNodeSet* nodes = static_cast<txNodeSet*>
143 : (static_cast<txAExprResult*>
144 0 : (exprResult));
145 : PRInt32 i;
146 0 : for (i = 0; i < nodes->size(); ++i) {
147 0 : nsAutoString idList;
148 0 : txXPathNodeUtils::appendNodeValue(nodes->get(i), idList);
149 0 : nsWhitespaceTokenizer tokenizer(idList);
150 0 : while (tokenizer.hasMoreTokens()) {
151 0 : if (walker.moveToElementById(tokenizer.nextToken())) {
152 0 : resultSet->add(walker.getCurrentPosition());
153 : }
154 : }
155 : }
156 : }
157 : else {
158 0 : nsAutoString idList;
159 0 : exprResult->stringValue(idList);
160 0 : nsWhitespaceTokenizer tokenizer(idList);
161 0 : while (tokenizer.hasMoreTokens()) {
162 0 : if (walker.moveToElementById(tokenizer.nextToken())) {
163 0 : resultSet->add(walker.getCurrentPosition());
164 : }
165 : }
166 : }
167 :
168 0 : *aResult = resultSet;
169 0 : NS_ADDREF(*aResult);
170 :
171 0 : return NS_OK;
172 : }
173 : case LAST:
174 : {
175 0 : return aContext->recycler()->getNumberResult(aContext->size(),
176 0 : aResult);
177 : }
178 : case LOCAL_NAME:
179 : case NAME:
180 : case NAMESPACE_URI:
181 : {
182 : // Check for optional arg
183 0 : nsRefPtr<txNodeSet> nodes;
184 0 : if (!mParams.IsEmpty()) {
185 0 : rv = evaluateToNodeSet(mParams[0], aContext,
186 0 : getter_AddRefs(nodes));
187 0 : NS_ENSURE_SUCCESS(rv, rv);
188 :
189 0 : if (nodes->isEmpty()) {
190 0 : aContext->recycler()->getEmptyStringResult(aResult);
191 :
192 0 : return NS_OK;
193 : }
194 : }
195 :
196 0 : const txXPathNode& node = nodes ? nodes->get(0) :
197 0 : aContext->getContextNode();
198 0 : switch (mType) {
199 : case LOCAL_NAME:
200 : {
201 0 : StringResult* strRes = nsnull;
202 0 : rv = aContext->recycler()->getStringResult(&strRes);
203 0 : NS_ENSURE_SUCCESS(rv, rv);
204 :
205 0 : *aResult = strRes;
206 0 : txXPathNodeUtils::getLocalName(node, strRes->mValue);
207 :
208 0 : return NS_OK;
209 : }
210 : case NAMESPACE_URI:
211 : {
212 0 : StringResult* strRes = nsnull;
213 0 : rv = aContext->recycler()->getStringResult(&strRes);
214 0 : NS_ENSURE_SUCCESS(rv, rv);
215 :
216 0 : *aResult = strRes;
217 0 : txXPathNodeUtils::getNamespaceURI(node, strRes->mValue);
218 :
219 0 : return NS_OK;
220 : }
221 : case NAME:
222 : {
223 : // XXX Namespace: namespaces have a name
224 0 : if (txXPathNodeUtils::isAttribute(node) ||
225 0 : txXPathNodeUtils::isElement(node) ||
226 0 : txXPathNodeUtils::isProcessingInstruction(node)) {
227 0 : StringResult* strRes = nsnull;
228 0 : rv = aContext->recycler()->getStringResult(&strRes);
229 0 : NS_ENSURE_SUCCESS(rv, rv);
230 :
231 0 : *aResult = strRes;
232 0 : txXPathNodeUtils::getNodeName(node, strRes->mValue);
233 : }
234 : else {
235 0 : aContext->recycler()->getEmptyStringResult(aResult);
236 : }
237 :
238 0 : return NS_OK;
239 : }
240 : default:
241 : {
242 : break;
243 : }
244 : }
245 : }
246 : case POSITION:
247 : {
248 0 : return aContext->recycler()->getNumberResult(aContext->position(),
249 0 : aResult);
250 : }
251 :
252 : // String functions
253 :
254 : case CONCAT:
255 : {
256 0 : nsRefPtr<StringResult> strRes;
257 0 : rv = aContext->recycler()->getStringResult(getter_AddRefs(strRes));
258 0 : NS_ENSURE_SUCCESS(rv, rv);
259 :
260 0 : PRUint32 i, len = mParams.Length();
261 0 : for (i = 0; i < len; ++i) {
262 0 : rv = mParams[i]->evaluateToString(aContext, strRes->mValue);
263 0 : NS_ENSURE_SUCCESS(rv, rv);
264 : }
265 :
266 0 : NS_ADDREF(*aResult = strRes);
267 :
268 0 : return NS_OK;
269 : }
270 : case CONTAINS:
271 : {
272 0 : nsAutoString arg2;
273 0 : rv = mParams[1]->evaluateToString(aContext, arg2);
274 0 : NS_ENSURE_SUCCESS(rv, rv);
275 :
276 0 : if (arg2.IsEmpty()) {
277 0 : aContext->recycler()->getBoolResult(true, aResult);
278 : }
279 : else {
280 0 : nsAutoString arg1;
281 0 : rv = mParams[0]->evaluateToString(aContext, arg1);
282 0 : NS_ENSURE_SUCCESS(rv, rv);
283 :
284 0 : aContext->recycler()->getBoolResult(FindInReadable(arg2, arg1),
285 0 : aResult);
286 : }
287 :
288 0 : return NS_OK;
289 : }
290 : case NORMALIZE_SPACE:
291 : {
292 0 : nsAutoString resultStr;
293 0 : if (!mParams.IsEmpty()) {
294 0 : rv = mParams[0]->evaluateToString(aContext, resultStr);
295 0 : NS_ENSURE_SUCCESS(rv, rv);
296 : }
297 : else {
298 0 : txXPathNodeUtils::appendNodeValue(aContext->getContextNode(),
299 0 : resultStr);
300 : }
301 :
302 0 : nsRefPtr<StringResult> strRes;
303 0 : rv = aContext->recycler()->getStringResult(getter_AddRefs(strRes));
304 0 : NS_ENSURE_SUCCESS(rv, rv);
305 :
306 0 : bool addSpace = false;
307 0 : bool first = true;
308 0 : strRes->mValue.SetCapacity(resultStr.Length());
309 : PRUnichar c;
310 : PRUint32 src;
311 0 : for (src = 0; src < resultStr.Length(); src++) {
312 0 : c = resultStr.CharAt(src);
313 0 : if (XMLUtils::isWhitespace(c)) {
314 0 : addSpace = true;
315 : }
316 : else {
317 0 : if (addSpace && !first)
318 0 : strRes->mValue.Append(PRUnichar(' '));
319 :
320 0 : strRes->mValue.Append(c);
321 0 : addSpace = false;
322 0 : first = false;
323 : }
324 : }
325 0 : *aResult = strRes;
326 0 : NS_ADDREF(*aResult);
327 :
328 0 : return NS_OK;
329 : }
330 : case STARTS_WITH:
331 : {
332 0 : nsAutoString arg2;
333 0 : rv = mParams[1]->evaluateToString(aContext, arg2);
334 0 : NS_ENSURE_SUCCESS(rv, rv);
335 :
336 0 : bool result = false;
337 0 : if (arg2.IsEmpty()) {
338 0 : result = true;
339 : }
340 : else {
341 0 : nsAutoString arg1;
342 0 : rv = mParams[0]->evaluateToString(aContext, arg1);
343 0 : NS_ENSURE_SUCCESS(rv, rv);
344 :
345 0 : result = StringBeginsWith(arg1, arg2);
346 : }
347 :
348 0 : aContext->recycler()->getBoolResult(result, aResult);
349 :
350 0 : return NS_OK;
351 : }
352 : case STRING:
353 : {
354 0 : nsRefPtr<StringResult> strRes;
355 0 : rv = aContext->recycler()->getStringResult(getter_AddRefs(strRes));
356 0 : NS_ENSURE_SUCCESS(rv, rv);
357 :
358 0 : if (!mParams.IsEmpty()) {
359 0 : rv = mParams[0]->evaluateToString(aContext, strRes->mValue);
360 0 : NS_ENSURE_SUCCESS(rv, rv);
361 : }
362 : else {
363 0 : txXPathNodeUtils::appendNodeValue(aContext->getContextNode(),
364 0 : strRes->mValue);
365 : }
366 :
367 0 : NS_ADDREF(*aResult = strRes);
368 :
369 0 : return NS_OK;
370 : }
371 : case STRING_LENGTH:
372 : {
373 0 : nsAutoString resultStr;
374 0 : if (!mParams.IsEmpty()) {
375 0 : rv = mParams[0]->evaluateToString(aContext, resultStr);
376 0 : NS_ENSURE_SUCCESS(rv, rv);
377 : }
378 : else {
379 0 : txXPathNodeUtils::appendNodeValue(aContext->getContextNode(),
380 0 : resultStr);
381 : }
382 0 : rv = aContext->recycler()->getNumberResult(resultStr.Length(),
383 0 : aResult);
384 0 : NS_ENSURE_SUCCESS(rv, rv);
385 :
386 0 : return NS_OK;
387 : }
388 : case SUBSTRING:
389 : {
390 0 : nsAutoString src;
391 0 : rv = mParams[0]->evaluateToString(aContext, src);
392 0 : NS_ENSURE_SUCCESS(rv, rv);
393 :
394 : double start;
395 0 : rv = evaluateToNumber(mParams[1], aContext, &start);
396 0 : NS_ENSURE_SUCCESS(rv, rv);
397 :
398 : // check for NaN or +/-Inf
399 0 : if (txDouble::isNaN(start) ||
400 0 : txDouble::isInfinite(start) ||
401 0 : start >= src.Length() + 0.5) {
402 0 : aContext->recycler()->getEmptyStringResult(aResult);
403 :
404 0 : return NS_OK;
405 : }
406 :
407 0 : start = floor(start + 0.5) - 1;
408 :
409 : double end;
410 0 : if (mParams.Length() == 3) {
411 0 : rv = evaluateToNumber(mParams[2], aContext, &end);
412 0 : NS_ENSURE_SUCCESS(rv, rv);
413 :
414 0 : end += start;
415 0 : if (txDouble::isNaN(end) || end < 0) {
416 0 : aContext->recycler()->getEmptyStringResult(aResult);
417 :
418 0 : return NS_OK;
419 : }
420 :
421 0 : if (end > src.Length())
422 0 : end = src.Length();
423 : else
424 0 : end = floor(end + 0.5);
425 : }
426 : else {
427 0 : end = src.Length();
428 : }
429 :
430 0 : if (start < 0)
431 0 : start = 0;
432 :
433 0 : if (start > end) {
434 0 : aContext->recycler()->getEmptyStringResult(aResult);
435 :
436 0 : return NS_OK;
437 : }
438 :
439 0 : return aContext->recycler()->getStringResult(
440 0 : Substring(src, (PRUint32)start, (PRUint32)(end - start)),
441 0 : aResult);
442 : }
443 : case SUBSTRING_AFTER:
444 : {
445 0 : nsAutoString arg1;
446 0 : rv = mParams[0]->evaluateToString(aContext, arg1);
447 0 : NS_ENSURE_SUCCESS(rv, rv);
448 :
449 0 : nsAutoString arg2;
450 0 : rv = mParams[1]->evaluateToString(aContext, arg2);
451 0 : NS_ENSURE_SUCCESS(rv, rv);
452 :
453 0 : if (arg2.IsEmpty()) {
454 0 : return aContext->recycler()->getStringResult(arg1, aResult);
455 : }
456 :
457 0 : PRInt32 idx = arg1.Find(arg2);
458 0 : if (idx == kNotFound) {
459 0 : aContext->recycler()->getEmptyStringResult(aResult);
460 :
461 0 : return NS_OK;
462 : }
463 :
464 0 : const nsSubstring& result = Substring(arg1, idx + arg2.Length());
465 0 : return aContext->recycler()->getStringResult(result, aResult);
466 : }
467 : case SUBSTRING_BEFORE:
468 : {
469 0 : nsAutoString arg2;
470 0 : rv = mParams[1]->evaluateToString(aContext, arg2);
471 0 : NS_ENSURE_SUCCESS(rv, rv);
472 :
473 0 : if (arg2.IsEmpty()) {
474 0 : aContext->recycler()->getEmptyStringResult(aResult);
475 :
476 0 : return NS_OK;
477 : }
478 :
479 0 : nsAutoString arg1;
480 0 : rv = mParams[0]->evaluateToString(aContext, arg1);
481 0 : NS_ENSURE_SUCCESS(rv, rv);
482 :
483 0 : PRInt32 idx = arg1.Find(arg2);
484 0 : if (idx == kNotFound) {
485 0 : aContext->recycler()->getEmptyStringResult(aResult);
486 :
487 0 : return NS_OK;
488 : }
489 :
490 0 : return aContext->recycler()->getStringResult(StringHead(arg1, idx),
491 0 : aResult);
492 : }
493 : case TRANSLATE:
494 : {
495 0 : nsAutoString src;
496 0 : rv = mParams[0]->evaluateToString(aContext, src);
497 0 : NS_ENSURE_SUCCESS(rv, rv);
498 :
499 0 : if (src.IsEmpty()) {
500 0 : aContext->recycler()->getEmptyStringResult(aResult);
501 :
502 0 : return NS_OK;
503 : }
504 :
505 0 : nsRefPtr<StringResult> strRes;
506 0 : rv = aContext->recycler()->getStringResult(getter_AddRefs(strRes));
507 0 : NS_ENSURE_SUCCESS(rv, rv);
508 :
509 0 : strRes->mValue.SetCapacity(src.Length());
510 :
511 0 : nsAutoString oldChars, newChars;
512 0 : rv = mParams[1]->evaluateToString(aContext, oldChars);
513 0 : NS_ENSURE_SUCCESS(rv, rv);
514 :
515 0 : rv = mParams[2]->evaluateToString(aContext, newChars);
516 0 : NS_ENSURE_SUCCESS(rv, rv);
517 :
518 : PRUint32 i;
519 0 : PRInt32 newCharsLength = (PRInt32)newChars.Length();
520 0 : for (i = 0; i < src.Length(); i++) {
521 0 : PRInt32 idx = oldChars.FindChar(src.CharAt(i));
522 0 : if (idx != kNotFound) {
523 0 : if (idx < newCharsLength)
524 0 : strRes->mValue.Append(newChars.CharAt((PRUint32)idx));
525 : }
526 : else {
527 0 : strRes->mValue.Append(src.CharAt(i));
528 : }
529 : }
530 :
531 0 : NS_ADDREF(*aResult = strRes);
532 :
533 0 : return NS_OK;
534 : }
535 :
536 : // Number functions
537 :
538 : case NUMBER:
539 : {
540 : double res;
541 0 : if (!mParams.IsEmpty()) {
542 0 : rv = evaluateToNumber(mParams[0], aContext, &res);
543 0 : NS_ENSURE_SUCCESS(rv, rv);
544 : }
545 : else {
546 0 : nsAutoString resultStr;
547 0 : txXPathNodeUtils::appendNodeValue(aContext->getContextNode(),
548 0 : resultStr);
549 0 : res = txDouble::toDouble(resultStr);
550 : }
551 0 : return aContext->recycler()->getNumberResult(res, aResult);
552 : }
553 : case ROUND:
554 : {
555 : double dbl;
556 0 : rv = evaluateToNumber(mParams[0], aContext, &dbl);
557 0 : NS_ENSURE_SUCCESS(rv, rv);
558 :
559 0 : if (!txDouble::isNaN(dbl) && !txDouble::isInfinite(dbl)) {
560 0 : if (txDouble::isNeg(dbl) && dbl >= -0.5) {
561 0 : dbl *= 0;
562 : }
563 : else {
564 0 : dbl = floor(dbl + 0.5);
565 : }
566 : }
567 :
568 0 : return aContext->recycler()->getNumberResult(dbl, aResult);
569 : }
570 : case FLOOR:
571 : {
572 : double dbl;
573 0 : rv = evaluateToNumber(mParams[0], aContext, &dbl);
574 0 : NS_ENSURE_SUCCESS(rv, rv);
575 :
576 0 : if (!txDouble::isNaN(dbl) &&
577 0 : !txDouble::isInfinite(dbl) &&
578 0 : !(dbl == 0 && txDouble::isNeg(dbl))) {
579 0 : dbl = floor(dbl);
580 : }
581 :
582 0 : return aContext->recycler()->getNumberResult(dbl, aResult);
583 : }
584 : case CEILING:
585 : {
586 : double dbl;
587 0 : rv = evaluateToNumber(mParams[0], aContext, &dbl);
588 0 : NS_ENSURE_SUCCESS(rv, rv);
589 :
590 0 : if (!txDouble::isNaN(dbl) && !txDouble::isInfinite(dbl)) {
591 0 : if (txDouble::isNeg(dbl) && dbl > -1) {
592 0 : dbl *= 0;
593 : }
594 : else {
595 0 : dbl = ceil(dbl);
596 : }
597 : }
598 :
599 0 : return aContext->recycler()->getNumberResult(dbl, aResult);
600 : }
601 : case SUM:
602 : {
603 0 : nsRefPtr<txNodeSet> nodes;
604 0 : nsresult rv = evaluateToNodeSet(mParams[0], aContext,
605 0 : getter_AddRefs(nodes));
606 0 : NS_ENSURE_SUCCESS(rv, rv);
607 :
608 0 : double res = 0;
609 : PRInt32 i;
610 0 : for (i = 0; i < nodes->size(); ++i) {
611 0 : nsAutoString resultStr;
612 0 : txXPathNodeUtils::appendNodeValue(nodes->get(i), resultStr);
613 0 : res += txDouble::toDouble(resultStr);
614 : }
615 0 : return aContext->recycler()->getNumberResult(res, aResult);
616 : }
617 :
618 : // Boolean functions
619 :
620 : case BOOLEAN:
621 : {
622 : bool result;
623 0 : nsresult rv = mParams[0]->evaluateToBool(aContext, result);
624 0 : NS_ENSURE_SUCCESS(rv, rv);
625 :
626 0 : aContext->recycler()->getBoolResult(result, aResult);
627 :
628 0 : return NS_OK;
629 : }
630 : case _FALSE:
631 : {
632 0 : aContext->recycler()->getBoolResult(false, aResult);
633 :
634 0 : return NS_OK;
635 : }
636 : case LANG:
637 : {
638 0 : txXPathTreeWalker walker(aContext->getContextNode());
639 :
640 0 : nsAutoString lang;
641 : bool found;
642 0 : do {
643 : found = walker.getAttr(nsGkAtoms::lang, kNameSpaceID_XML,
644 0 : lang);
645 0 : } while (!found && walker.moveToParent());
646 :
647 0 : if (!found) {
648 0 : aContext->recycler()->getBoolResult(false, aResult);
649 :
650 0 : return NS_OK;
651 : }
652 :
653 0 : nsAutoString arg;
654 0 : rv = mParams[0]->evaluateToString(aContext, arg);
655 0 : NS_ENSURE_SUCCESS(rv, rv);
656 :
657 : bool result =
658 : StringBeginsWith(lang, arg,
659 0 : txCaseInsensitiveStringComparator()) &&
660 0 : (lang.Length() == arg.Length() ||
661 0 : lang.CharAt(arg.Length()) == '-');
662 :
663 0 : aContext->recycler()->getBoolResult(result, aResult);
664 :
665 0 : return NS_OK;
666 : }
667 : case _NOT:
668 : {
669 : bool result;
670 0 : rv = mParams[0]->evaluateToBool(aContext, result);
671 0 : NS_ENSURE_SUCCESS(rv, rv);
672 :
673 0 : aContext->recycler()->getBoolResult(!result, aResult);
674 :
675 0 : return NS_OK;
676 : }
677 : case _TRUE:
678 : {
679 0 : aContext->recycler()->getBoolResult(true, aResult);
680 :
681 0 : return NS_OK;
682 : }
683 : }
684 :
685 0 : aContext->receiveError(NS_LITERAL_STRING("Internal error"),
686 0 : NS_ERROR_UNEXPECTED);
687 0 : return NS_ERROR_UNEXPECTED;
688 : }
689 :
690 : Expr::ResultType
691 0 : txCoreFunctionCall::getReturnType()
692 : {
693 0 : return descriptTable[mType].mReturnType;
694 : }
695 :
696 : bool
697 0 : txCoreFunctionCall::isSensitiveTo(ContextSensitivity aContext)
698 : {
699 0 : switch (mType) {
700 : case COUNT:
701 : case CONCAT:
702 : case CONTAINS:
703 : case STARTS_WITH:
704 : case SUBSTRING:
705 : case SUBSTRING_AFTER:
706 : case SUBSTRING_BEFORE:
707 : case TRANSLATE:
708 : case ROUND:
709 : case FLOOR:
710 : case CEILING:
711 : case SUM:
712 : case BOOLEAN:
713 : case _NOT:
714 : case _FALSE:
715 : case _TRUE:
716 : {
717 0 : return argsSensitiveTo(aContext);
718 : }
719 : case ID:
720 : {
721 : return (aContext & NODE_CONTEXT) ||
722 0 : argsSensitiveTo(aContext);
723 : }
724 : case LAST:
725 : {
726 0 : return !!(aContext & SIZE_CONTEXT);
727 : }
728 : case LOCAL_NAME:
729 : case NAME:
730 : case NAMESPACE_URI:
731 : case NORMALIZE_SPACE:
732 : case STRING:
733 : case STRING_LENGTH:
734 : case NUMBER:
735 : {
736 0 : if (mParams.IsEmpty()) {
737 0 : return !!(aContext & NODE_CONTEXT);
738 : }
739 0 : return argsSensitiveTo(aContext);
740 : }
741 : case POSITION:
742 : {
743 0 : return !!(aContext & POSITION_CONTEXT);
744 : }
745 : case LANG:
746 : {
747 : return (aContext & NODE_CONTEXT) ||
748 0 : argsSensitiveTo(aContext);
749 : }
750 : }
751 :
752 0 : NS_NOTREACHED("how'd we get here?");
753 0 : return true;
754 : }
755 :
756 : // static
757 : bool
758 0 : txCoreFunctionCall::getTypeFromAtom(nsIAtom* aName, eType& aType)
759 : {
760 : PRUint32 i;
761 0 : for (i = 0; i < ArrayLength(descriptTable); ++i) {
762 0 : if (aName == *descriptTable[i].mName) {
763 0 : aType = static_cast<eType>(i);
764 :
765 0 : return true;
766 : }
767 : }
768 :
769 0 : return false;
770 : }
771 :
772 : #ifdef TX_TO_STRING
773 : nsresult
774 0 : txCoreFunctionCall::getNameAtom(nsIAtom** aAtom)
775 : {
776 0 : NS_ADDREF(*aAtom = *descriptTable[mType].mName);
777 0 : return NS_OK;
778 : }
779 : #endif
|