Back to index

lightning-sunbird  0.9+nobinonly
txFormatNumberFunctionCall.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is TransforMiiX XSLT processor code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Jonas Sicking.
00019  * Portions created by the Initial Developer are Copyright (C) 2001
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Jonas Sicking <sicking@bigfoot.com>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either the GNU General Public License Version 2 or later (the "GPL"), or
00027  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "XSLTFunctions.h"
00040 #include "txAtoms.h"
00041 #include "txIXPathContext.h"
00042 #include "txStylesheet.h"
00043 #include <math.h>
00044 #include "ExprResult.h"
00045 #include "txNamespaceMap.h"
00046 
00047 #include "prdtoa.h"
00048 
00049 #define INVALID_PARAM_VALUE \
00050     NS_LITERAL_STRING("invalid parameter value for function")
00051 
00052 const PRUnichar txFormatNumberFunctionCall::FORMAT_QUOTE = '\'';
00053 
00054 /*
00055  * FormatNumberFunctionCall
00056  * A representation of the XSLT additional function: format-number()
00057  */
00058 
00059 /*
00060  * Creates a new format-number function call
00061  */
00062 txFormatNumberFunctionCall::txFormatNumberFunctionCall(txStylesheet* aStylesheet,
00063                                                        txNamespaceMap* aMappings)
00064     : mStylesheet(aStylesheet),
00065       mMappings(aMappings)
00066 {
00067 }
00068 
00069 /*
00070  * Evaluates this Expr based on the given context node and processor state
00071  * @param context the context node for evaluation of this Expr
00072  * @param cs the ContextState containing the stack information needed
00073  * for evaluation
00074  * @return the result of the evaluation
00075  */
00076 nsresult
00077 txFormatNumberFunctionCall::evaluate(txIEvalContext* aContext,
00078                                      txAExprResult** aResult)
00079 {
00080     nsresult rv = NS_OK;
00081     *aResult = nsnull;
00082     if (!requireParams(2, 3, aContext))
00083         return NS_ERROR_XPATH_BAD_ARGUMENT_COUNT;
00084 
00085     // Get number and format
00086     txListIterator iter(&params);
00087 
00088     double value;
00089     nsAutoString formatStr;
00090     txExpandedName formatName;
00091 
00092     value = evaluateToNumber((Expr*)iter.next(), aContext);
00093     evaluateToString((Expr*)iter.next(), aContext, formatStr);
00094     if (iter.hasNext()) {
00095         nsAutoString formatQName;
00096         evaluateToString((Expr*)iter.next(), aContext, formatQName);
00097         rv = formatName.init(formatQName, mMappings, MB_FALSE);
00098         NS_ENSURE_SUCCESS(rv, rv);
00099     }
00100 
00101     txDecimalFormat* format = mStylesheet->getDecimalFormat(formatName);
00102     if (!format) {
00103         nsAutoString err(NS_LITERAL_STRING("unknown decimal format"));
00104 #ifdef TX_TO_STRING
00105         err.AppendLiteral(" for: ");
00106         toString(err);
00107 #endif
00108         aContext->receiveError(err, NS_ERROR_XPATH_INVALID_ARG);
00109         return NS_ERROR_XPATH_INVALID_ARG;
00110     }
00111 
00112     // Special cases
00113     if (Double::isNaN(value)) {
00114         return aContext->recycler()->getStringResult(format->mNaN, aResult);
00115     }
00116 
00117     if (value == Double::POSITIVE_INFINITY) {
00118         return aContext->recycler()->getStringResult(format->mInfinity,
00119                                                      aResult);
00120     }
00121 
00122     if (value == Double::NEGATIVE_INFINITY) {
00123         nsAutoString res;
00124         res.Append(format->mMinusSign);
00125         res.Append(format->mInfinity);
00126         return aContext->recycler()->getStringResult(res, aResult);
00127     }
00128     
00129     // Value is a normal finite number
00130     nsAutoString prefix;
00131     nsAutoString suffix;
00132     int minIntegerSize=0;
00133     int minFractionSize=0;
00134     int maxFractionSize=0;
00135     int multiplier=1;
00136     int groupSize=-1;
00137 
00138     PRUint32 pos = 0;
00139     PRUint32 formatLen = formatStr.Length();
00140     MBool inQuote;
00141 
00142     // Get right subexpression
00143     inQuote = MB_FALSE;
00144     if (Double::isNeg(value)) {
00145         while (pos < formatLen &&
00146                (inQuote ||
00147                 formatStr.CharAt(pos) != format->mPatternSeparator)) {
00148             if (formatStr.CharAt(pos) == FORMAT_QUOTE)
00149                 inQuote = !inQuote;
00150             pos++;
00151         }
00152 
00153         if (pos == formatLen) {
00154             pos = 0;
00155             prefix.Append(format->mMinusSign);
00156         }
00157         else
00158             pos++;
00159     }
00160 
00161     // Parse the format string
00162     FormatParseState pState = Prefix;
00163     inQuote = MB_FALSE;
00164 
00165     PRUnichar c = 0;
00166     while (pos < formatLen && pState != Finished) {
00167         c=formatStr.CharAt(pos++);
00168 
00169         switch (pState) {
00170 
00171         case Prefix:
00172         case Suffix:
00173             if (!inQuote) {
00174                 if (c == format->mPercent) {
00175                     if (multiplier == 1)
00176                         multiplier = 100;
00177                     else {
00178                         nsAutoString err(INVALID_PARAM_VALUE);
00179 #ifdef TX_TO_STRING
00180                         err.AppendLiteral(": ");
00181                         toString(err);
00182 #endif
00183                         aContext->receiveError(err,
00184                                                NS_ERROR_XPATH_INVALID_ARG);
00185                         return NS_ERROR_XPATH_INVALID_ARG;
00186                     }
00187                 }
00188                 else if (c == format->mPerMille) {
00189                     if (multiplier == 1)
00190                         multiplier = 1000;
00191                     else {
00192                         nsAutoString err(INVALID_PARAM_VALUE);
00193 #ifdef TX_TO_STRING
00194                         err.AppendLiteral(": ");
00195                         toString(err);
00196 #endif
00197                         aContext->receiveError(err,
00198                                                NS_ERROR_XPATH_INVALID_ARG);
00199                         return NS_ERROR_XPATH_INVALID_ARG;
00200                     }
00201                 }
00202                 else if (c == format->mDecimalSeparator ||
00203                          c == format->mGroupingSeparator ||
00204                          c == format->mZeroDigit ||
00205                          c == format->mDigit ||
00206                          c == format->mPatternSeparator) {
00207                     pState = pState == Prefix ? IntDigit : Finished;
00208                     pos--;
00209                     break;
00210                 }
00211             }
00212 
00213             if (c == FORMAT_QUOTE)
00214                 inQuote = !inQuote;
00215             else if (pState == Prefix)
00216                 prefix.Append(c);
00217             else
00218                 suffix.Append(c);
00219             break;
00220 
00221         case IntDigit:
00222             if (c == format->mGroupingSeparator)
00223                 groupSize=0;
00224             else if (c == format->mDigit) {
00225                 if (groupSize >= 0)
00226                     groupSize++;
00227             }
00228             else {
00229                 pState = IntZero;
00230                 pos--;
00231             }
00232             break;
00233 
00234         case IntZero:
00235             if (c == format->mGroupingSeparator)
00236                 groupSize = 0;
00237             else if (c == format->mZeroDigit) {
00238                 if (groupSize >= 0)
00239                     groupSize++;
00240                 minIntegerSize++;
00241             }
00242             else if (c == format->mDecimalSeparator) {
00243                 pState = FracZero;
00244             }
00245             else {
00246                 pState = Suffix;
00247                 pos--;
00248             }
00249             break;
00250 
00251         case FracZero:
00252             if (c == format->mZeroDigit) {
00253                 maxFractionSize++;
00254                 minFractionSize++;
00255             }
00256             else {
00257                 pState = FracDigit;
00258                 pos--;
00259             }
00260             break;
00261 
00262         case FracDigit:
00263             if (c == format->mDigit)
00264                 maxFractionSize++;
00265             else {
00266                 pState = Suffix;
00267                 pos--;
00268             }
00269             break;
00270 
00271         case Finished:
00272             break;
00273         }
00274     }
00275 
00276     // Did we manage to parse the entire formatstring and was it valid
00277     if ((c != format->mPatternSeparator && pos < formatLen) ||
00278         inQuote ||
00279         groupSize == 0) {
00280         nsAutoString err(INVALID_PARAM_VALUE);
00281 #ifdef TX_TO_STRING
00282         err.AppendLiteral(": ");
00283         toString(err);
00284 #endif
00285         aContext->receiveError(err, NS_ERROR_XPATH_INVALID_ARG);
00286         return NS_ERROR_XPATH_INVALID_ARG;
00287     }
00288 
00289 
00290     /*
00291      * FINALLY we're done with the parsing
00292      * now build the result string
00293      */
00294 
00295     value = fabs(value) * multiplier;
00296 
00297     // Prefix
00298     nsAutoString res(prefix);
00299 
00300     int bufsize;
00301     if (value > 1)
00302         bufsize = (int)log10(value) + 30;
00303     else
00304         bufsize = 1 + 30;
00305 
00306     char* buf = new char[bufsize];
00307     NS_ENSURE_TRUE(buf, NS_ERROR_OUT_OF_MEMORY);
00308 
00309     PRIntn bufIntDigits, sign;
00310     char* endp;
00311     PR_dtoa(value, 0, 0, &bufIntDigits, &sign, &endp, buf, bufsize-1);
00312 
00313     int buflen = endp - buf;
00314     int intDigits;
00315     intDigits = bufIntDigits > minIntegerSize ? bufIntDigits : minIntegerSize;
00316 
00317     if (groupSize < 0)
00318         groupSize = intDigits + 10; //to simplify grouping
00319 
00320     // XXX We shouldn't use SetLength.
00321     res.SetLength(res.Length() +
00322                   intDigits +               // integer digits
00323                   1 +                       // decimal separator
00324                   maxFractionSize +         // fractions
00325                   (intDigits-1)/groupSize); // group separators
00326 
00327     PRInt32 i = bufIntDigits + maxFractionSize - 1;
00328     MBool carry = (i+1 < buflen) && (buf[i+1] >= '5');
00329     MBool hasFraction = MB_FALSE;
00330 
00331     PRUint32 resPos = res.Length()-1;
00332 
00333     // Fractions
00334     for (; i >= bufIntDigits; --i) {
00335         int digit;
00336         if (i >= buflen || i < 0) {
00337             digit = 0;
00338         }
00339         else {
00340             digit = buf[i] - '0';
00341         }
00342         
00343         if (carry) {
00344             digit = (digit + 1) % 10;
00345             carry = digit == 0;
00346         }
00347 
00348         if (hasFraction || digit != 0 || i < bufIntDigits+minFractionSize) {
00349             hasFraction = MB_TRUE;
00350             res.SetCharAt((PRUnichar)(digit + format->mZeroDigit),
00351                           resPos--);
00352         }
00353         else {
00354             res.Truncate(resPos--);
00355         }
00356     }
00357 
00358     // Decimal separator
00359     if (hasFraction) {
00360         res.SetCharAt(format->mDecimalSeparator, resPos--);
00361     }
00362     else {
00363         res.Truncate(resPos--);
00364     }
00365 
00366     // Integer digits
00367     for (i = 0; i < intDigits; ++i) {
00368         int digit;
00369         if (bufIntDigits-i-1 >= buflen || bufIntDigits-i-1 < 0) {
00370             digit = 0;
00371         }
00372         else {
00373             digit = buf[bufIntDigits-i-1] - '0';
00374         }
00375         
00376         if (carry) {
00377             digit = (digit + 1) % 10;
00378             carry = digit == 0;
00379         }
00380 
00381         if (i != 0 && i%groupSize == 0) {
00382             res.SetCharAt(format->mGroupingSeparator, resPos--);
00383         }
00384 
00385         res.SetCharAt((PRUnichar)(digit + format->mZeroDigit), resPos--);
00386     }
00387 
00388     if (carry) {
00389         if (i%groupSize == 0) {
00390             res.Insert(format->mGroupingSeparator, resPos + 1);
00391         }
00392         res.Insert((PRUnichar)(1 + format->mZeroDigit), resPos + 1);
00393     }
00394     
00395     if (!hasFraction && !intDigits && !carry) {
00396         // If we havn't added any characters we add a '0'
00397         // This can only happen for formats like '##.##'
00398         res.Append(format->mZeroDigit);
00399     }
00400 
00401     delete [] buf;
00402 
00403     // Build suffix
00404     res.Append(suffix);
00405 
00406     return aContext->recycler()->getStringResult(res, aResult);
00407 } //-- evaluate
00408 
00409 #ifdef TX_TO_STRING
00410 nsresult
00411 txFormatNumberFunctionCall::getNameAtom(nsIAtom** aAtom)
00412 {
00413     *aAtom = txXSLTAtoms::formatNumber;
00414     NS_ADDREF(*aAtom);
00415     return NS_OK;
00416 }
00417 #endif
00418 
00419 /*
00420  * txDecimalFormat
00421  * A representation of the XSLT element <xsl:decimal-format>
00422  */
00423 
00424 txDecimalFormat::txDecimalFormat() : mInfinity(NS_LITERAL_STRING("Infinity")),
00425                                      mNaN(NS_LITERAL_STRING("NaN"))
00426 {
00427     mDecimalSeparator = '.';
00428     mGroupingSeparator = ',';
00429     mMinusSign = '-';
00430     mPercent = '%';
00431     mPerMille = 0x2030;
00432     mZeroDigit = '0';
00433     mDigit = '#';
00434     mPatternSeparator = ';';
00435 }
00436 
00437 MBool txDecimalFormat::isEqual(txDecimalFormat* other)
00438 {
00439     return mDecimalSeparator == other->mDecimalSeparator &&
00440            mGroupingSeparator == other->mGroupingSeparator &&
00441            mInfinity.Equals(other->mInfinity) &&
00442            mMinusSign == other->mMinusSign &&
00443            mNaN.Equals(other->mNaN) &&
00444            mPercent == other->mPercent &&
00445            mPerMille == other->mPerMille &&
00446            mZeroDigit == other->mZeroDigit &&
00447            mDigit == other->mDigit &&
00448            mPatternSeparator  == other->mPatternSeparator;
00449 }