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 : * Jonas Sicking.
19 : * Portions created by the Initial Developer are Copyright (C) 2001
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Jonas Sicking <sicking@bigfoot.com>
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 "txXSLTFunctions.h"
40 : #include "nsGkAtoms.h"
41 : #include "txIXPathContext.h"
42 : #include "txStylesheet.h"
43 : #include <math.h>
44 : #include "txNamespaceMap.h"
45 :
46 : #include "prdtoa.h"
47 :
48 : #define INVALID_PARAM_VALUE \
49 : NS_LITERAL_STRING("invalid parameter value for function")
50 :
51 : const PRUnichar txFormatNumberFunctionCall::FORMAT_QUOTE = '\'';
52 :
53 : /*
54 : * FormatNumberFunctionCall
55 : * A representation of the XSLT additional function: format-number()
56 : */
57 :
58 : /*
59 : * Creates a new format-number function call
60 : */
61 0 : txFormatNumberFunctionCall::txFormatNumberFunctionCall(txStylesheet* aStylesheet,
62 : txNamespaceMap* aMappings)
63 : : mStylesheet(aStylesheet),
64 0 : mMappings(aMappings)
65 : {
66 0 : }
67 :
68 : /*
69 : * Evaluates this Expr based on the given context node and processor state
70 : * @param context the context node for evaluation of this Expr
71 : * @param cs the ContextState containing the stack information needed
72 : * for evaluation
73 : * @return the result of the evaluation
74 : */
75 : nsresult
76 0 : txFormatNumberFunctionCall::evaluate(txIEvalContext* aContext,
77 : txAExprResult** aResult)
78 : {
79 0 : *aResult = nsnull;
80 0 : if (!requireParams(2, 3, aContext))
81 0 : return NS_ERROR_XPATH_BAD_ARGUMENT_COUNT;
82 :
83 : // Get number and format
84 : double value;
85 0 : txExpandedName formatName;
86 :
87 0 : nsresult rv = evaluateToNumber(mParams[0], aContext, &value);
88 0 : NS_ENSURE_SUCCESS(rv, rv);
89 :
90 0 : nsAutoString formatStr;
91 0 : rv = mParams[1]->evaluateToString(aContext, formatStr);
92 0 : NS_ENSURE_SUCCESS(rv, rv);
93 :
94 0 : if (mParams.Length() == 3) {
95 0 : nsAutoString formatQName;
96 0 : rv = mParams[2]->evaluateToString(aContext, formatQName);
97 0 : NS_ENSURE_SUCCESS(rv, rv);
98 :
99 0 : rv = formatName.init(formatQName, mMappings, false);
100 0 : NS_ENSURE_SUCCESS(rv, rv);
101 : }
102 :
103 0 : txDecimalFormat* format = mStylesheet->getDecimalFormat(formatName);
104 0 : if (!format) {
105 0 : nsAutoString err(NS_LITERAL_STRING("unknown decimal format"));
106 : #ifdef TX_TO_STRING
107 0 : err.AppendLiteral(" for: ");
108 0 : toString(err);
109 : #endif
110 0 : aContext->receiveError(err, NS_ERROR_XPATH_INVALID_ARG);
111 0 : return NS_ERROR_XPATH_INVALID_ARG;
112 : }
113 :
114 : // Special cases
115 0 : if (txDouble::isNaN(value)) {
116 0 : return aContext->recycler()->getStringResult(format->mNaN, aResult);
117 : }
118 :
119 0 : if (value == txDouble::POSITIVE_INFINITY) {
120 0 : return aContext->recycler()->getStringResult(format->mInfinity,
121 0 : aResult);
122 : }
123 :
124 0 : if (value == txDouble::NEGATIVE_INFINITY) {
125 0 : nsAutoString res;
126 0 : res.Append(format->mMinusSign);
127 0 : res.Append(format->mInfinity);
128 0 : return aContext->recycler()->getStringResult(res, aResult);
129 : }
130 :
131 : // Value is a normal finite number
132 0 : nsAutoString prefix;
133 0 : nsAutoString suffix;
134 0 : int minIntegerSize=0;
135 0 : int minFractionSize=0;
136 0 : int maxFractionSize=0;
137 0 : int multiplier=1;
138 0 : int groupSize=-1;
139 :
140 0 : PRUint32 pos = 0;
141 0 : PRUint32 formatLen = formatStr.Length();
142 : bool inQuote;
143 :
144 : // Get right subexpression
145 0 : inQuote = false;
146 0 : if (txDouble::isNeg(value)) {
147 0 : while (pos < formatLen &&
148 : (inQuote ||
149 0 : formatStr.CharAt(pos) != format->mPatternSeparator)) {
150 0 : if (formatStr.CharAt(pos) == FORMAT_QUOTE)
151 0 : inQuote = !inQuote;
152 0 : pos++;
153 : }
154 :
155 0 : if (pos == formatLen) {
156 0 : pos = 0;
157 0 : prefix.Append(format->mMinusSign);
158 : }
159 : else
160 0 : pos++;
161 : }
162 :
163 : // Parse the format string
164 0 : FormatParseState pState = Prefix;
165 0 : inQuote = false;
166 :
167 0 : PRUnichar c = 0;
168 0 : while (pos < formatLen && pState != Finished) {
169 0 : c=formatStr.CharAt(pos++);
170 :
171 0 : switch (pState) {
172 :
173 : case Prefix:
174 : case Suffix:
175 0 : if (!inQuote) {
176 0 : if (c == format->mPercent) {
177 0 : if (multiplier == 1)
178 0 : multiplier = 100;
179 : else {
180 0 : nsAutoString err(INVALID_PARAM_VALUE);
181 : #ifdef TX_TO_STRING
182 0 : err.AppendLiteral(": ");
183 0 : toString(err);
184 : #endif
185 : aContext->receiveError(err,
186 0 : NS_ERROR_XPATH_INVALID_ARG);
187 0 : return NS_ERROR_XPATH_INVALID_ARG;
188 : }
189 : }
190 0 : else if (c == format->mPerMille) {
191 0 : if (multiplier == 1)
192 0 : multiplier = 1000;
193 : else {
194 0 : nsAutoString err(INVALID_PARAM_VALUE);
195 : #ifdef TX_TO_STRING
196 0 : err.AppendLiteral(": ");
197 0 : toString(err);
198 : #endif
199 : aContext->receiveError(err,
200 0 : NS_ERROR_XPATH_INVALID_ARG);
201 0 : return NS_ERROR_XPATH_INVALID_ARG;
202 : }
203 : }
204 0 : else if (c == format->mDecimalSeparator ||
205 : c == format->mGroupingSeparator ||
206 : c == format->mZeroDigit ||
207 : c == format->mDigit ||
208 : c == format->mPatternSeparator) {
209 0 : pState = pState == Prefix ? IntDigit : Finished;
210 0 : pos--;
211 0 : break;
212 : }
213 : }
214 :
215 0 : if (c == FORMAT_QUOTE)
216 0 : inQuote = !inQuote;
217 0 : else if (pState == Prefix)
218 0 : prefix.Append(c);
219 : else
220 0 : suffix.Append(c);
221 0 : break;
222 :
223 : case IntDigit:
224 0 : if (c == format->mGroupingSeparator)
225 0 : groupSize=0;
226 0 : else if (c == format->mDigit) {
227 0 : if (groupSize >= 0)
228 0 : groupSize++;
229 : }
230 : else {
231 0 : pState = IntZero;
232 0 : pos--;
233 : }
234 0 : break;
235 :
236 : case IntZero:
237 0 : if (c == format->mGroupingSeparator)
238 0 : groupSize = 0;
239 0 : else if (c == format->mZeroDigit) {
240 0 : if (groupSize >= 0)
241 0 : groupSize++;
242 0 : minIntegerSize++;
243 : }
244 0 : else if (c == format->mDecimalSeparator) {
245 0 : pState = FracZero;
246 : }
247 : else {
248 0 : pState = Suffix;
249 0 : pos--;
250 : }
251 0 : break;
252 :
253 : case FracZero:
254 0 : if (c == format->mZeroDigit) {
255 0 : maxFractionSize++;
256 0 : minFractionSize++;
257 : }
258 : else {
259 0 : pState = FracDigit;
260 0 : pos--;
261 : }
262 0 : break;
263 :
264 : case FracDigit:
265 0 : if (c == format->mDigit)
266 0 : maxFractionSize++;
267 : else {
268 0 : pState = Suffix;
269 0 : pos--;
270 : }
271 0 : break;
272 :
273 : case Finished:
274 0 : break;
275 : }
276 : }
277 :
278 : // Did we manage to parse the entire formatstring and was it valid
279 0 : if ((c != format->mPatternSeparator && pos < formatLen) ||
280 : inQuote ||
281 : groupSize == 0) {
282 0 : nsAutoString err(INVALID_PARAM_VALUE);
283 : #ifdef TX_TO_STRING
284 0 : err.AppendLiteral(": ");
285 0 : toString(err);
286 : #endif
287 0 : aContext->receiveError(err, NS_ERROR_XPATH_INVALID_ARG);
288 0 : return NS_ERROR_XPATH_INVALID_ARG;
289 : }
290 :
291 :
292 : /*
293 : * FINALLY we're done with the parsing
294 : * now build the result string
295 : */
296 :
297 0 : value = fabs(value) * multiplier;
298 :
299 : // Prefix
300 0 : nsAutoString res(prefix);
301 :
302 : int bufsize;
303 0 : if (value > 1)
304 0 : bufsize = (int)log10(value) + 30;
305 : else
306 0 : bufsize = 1 + 30;
307 :
308 0 : char* buf = new char[bufsize];
309 0 : NS_ENSURE_TRUE(buf, NS_ERROR_OUT_OF_MEMORY);
310 :
311 : PRIntn bufIntDigits, sign;
312 : char* endp;
313 0 : PR_dtoa(value, 0, 0, &bufIntDigits, &sign, &endp, buf, bufsize-1);
314 :
315 0 : int buflen = endp - buf;
316 : int intDigits;
317 0 : intDigits = bufIntDigits > minIntegerSize ? bufIntDigits : minIntegerSize;
318 :
319 0 : if (groupSize < 0)
320 0 : groupSize = intDigits + 10; //to simplify grouping
321 :
322 : // XXX We shouldn't use SetLength.
323 0 : res.SetLength(res.Length() +
324 : intDigits + // integer digits
325 : 1 + // decimal separator
326 : maxFractionSize + // fractions
327 0 : (intDigits-1)/groupSize); // group separators
328 :
329 0 : PRInt32 i = bufIntDigits + maxFractionSize - 1;
330 0 : bool carry = (i+1 < buflen) && (buf[i+1] >= '5');
331 0 : bool hasFraction = false;
332 :
333 0 : PRUint32 resPos = res.Length()-1;
334 :
335 : // Fractions
336 0 : for (; i >= bufIntDigits; --i) {
337 : int digit;
338 0 : if (i >= buflen || i < 0) {
339 0 : digit = 0;
340 : }
341 : else {
342 0 : digit = buf[i] - '0';
343 : }
344 :
345 0 : if (carry) {
346 0 : digit = (digit + 1) % 10;
347 0 : carry = digit == 0;
348 : }
349 :
350 0 : if (hasFraction || digit != 0 || i < bufIntDigits+minFractionSize) {
351 0 : hasFraction = true;
352 : res.SetCharAt((PRUnichar)(digit + format->mZeroDigit),
353 0 : resPos--);
354 : }
355 : else {
356 0 : res.Truncate(resPos--);
357 : }
358 : }
359 :
360 : // Decimal separator
361 0 : if (hasFraction) {
362 0 : res.SetCharAt(format->mDecimalSeparator, resPos--);
363 : }
364 : else {
365 0 : res.Truncate(resPos--);
366 : }
367 :
368 : // Integer digits
369 0 : for (i = 0; i < intDigits; ++i) {
370 : int digit;
371 0 : if (bufIntDigits-i-1 >= buflen || bufIntDigits-i-1 < 0) {
372 0 : digit = 0;
373 : }
374 : else {
375 0 : digit = buf[bufIntDigits-i-1] - '0';
376 : }
377 :
378 0 : if (carry) {
379 0 : digit = (digit + 1) % 10;
380 0 : carry = digit == 0;
381 : }
382 :
383 0 : if (i != 0 && i%groupSize == 0) {
384 0 : res.SetCharAt(format->mGroupingSeparator, resPos--);
385 : }
386 :
387 0 : res.SetCharAt((PRUnichar)(digit + format->mZeroDigit), resPos--);
388 : }
389 :
390 0 : if (carry) {
391 0 : if (i%groupSize == 0) {
392 0 : res.Insert(format->mGroupingSeparator, resPos + 1);
393 : }
394 0 : res.Insert((PRUnichar)(1 + format->mZeroDigit), resPos + 1);
395 : }
396 :
397 0 : if (!hasFraction && !intDigits && !carry) {
398 : // If we havn't added any characters we add a '0'
399 : // This can only happen for formats like '##.##'
400 0 : res.Append(format->mZeroDigit);
401 : }
402 :
403 0 : delete [] buf;
404 :
405 : // Build suffix
406 0 : res.Append(suffix);
407 :
408 0 : return aContext->recycler()->getStringResult(res, aResult);
409 : } //-- evaluate
410 :
411 : Expr::ResultType
412 0 : txFormatNumberFunctionCall::getReturnType()
413 : {
414 0 : return STRING_RESULT;
415 : }
416 :
417 : bool
418 0 : txFormatNumberFunctionCall::isSensitiveTo(ContextSensitivity aContext)
419 : {
420 0 : return argsSensitiveTo(aContext);
421 : }
422 :
423 : #ifdef TX_TO_STRING
424 : nsresult
425 0 : txFormatNumberFunctionCall::getNameAtom(nsIAtom** aAtom)
426 : {
427 0 : *aAtom = nsGkAtoms::formatNumber;
428 0 : NS_ADDREF(*aAtom);
429 0 : return NS_OK;
430 : }
431 : #endif
432 :
433 : /*
434 : * txDecimalFormat
435 : * A representation of the XSLT element <xsl:decimal-format>
436 : */
437 :
438 0 : txDecimalFormat::txDecimalFormat() : mInfinity(NS_LITERAL_STRING("Infinity")),
439 0 : mNaN(NS_LITERAL_STRING("NaN"))
440 : {
441 0 : mDecimalSeparator = '.';
442 0 : mGroupingSeparator = ',';
443 0 : mMinusSign = '-';
444 0 : mPercent = '%';
445 0 : mPerMille = 0x2030;
446 0 : mZeroDigit = '0';
447 0 : mDigit = '#';
448 0 : mPatternSeparator = ';';
449 0 : }
450 :
451 0 : bool txDecimalFormat::isEqual(txDecimalFormat* other)
452 : {
453 : return mDecimalSeparator == other->mDecimalSeparator &&
454 : mGroupingSeparator == other->mGroupingSeparator &&
455 0 : mInfinity.Equals(other->mInfinity) &&
456 : mMinusSign == other->mMinusSign &&
457 0 : mNaN.Equals(other->mNaN) &&
458 : mPercent == other->mPercent &&
459 : mPerMille == other->mPerMille &&
460 : mZeroDigit == other->mZeroDigit &&
461 : mDigit == other->mDigit &&
462 0 : mPatternSeparator == other->mPatternSeparator;
463 : }
|