LCOV - code coverage report
Current view: top level - dom/xslt/xslt - txFormatNumberFunctionCall.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 218 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 7 0.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13