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