Package com.google.caja.parser.js

Source Code of com.google.caja.parser.js.NumberLiteral

// Copyright (C) 2005 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.caja.parser.js;

import com.google.caja.lexer.FilePosition;
import com.google.javascript.jscomp.jsonml.JsonML;
import com.google.javascript.jscomp.jsonml.TagAttr;
import com.google.javascript.jscomp.jsonml.TagType;

import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;

/**
* A literal numeric value.  This class is abstract so that we can distinguish
* literals based on the format they were parsed from.
* If in doubt about which implementation to use, use {@link RealLiteral}.
*
* @author mikesamuel@gmail.com
*/
public abstract class NumberLiteral extends Literal {
  private static final long serialVersionUID = -5974378121634749014L;

  protected NumberLiteral(FilePosition pos) { super(pos); }

  @Override
  public abstract Number getValue();

  /** Same as {@code getValue().doubleValue()} */
  public abstract double doubleValue();

  @Override
  public boolean getValueInBooleanContext() {
    double n = doubleValue();
    return !(Double.isNaN(n) || 0D == n);
  }

  /**
   * Convert a number to a string using javascript rules.
   * <blockquote>
   * 9.8.1 ToString Applied to the Number Type
   *     <br><br>
   * The operator ToString converts a number m to string format as follows:
   * <ol>
   * <li>If m is NaN, return the string "NaN".
   * <li>If m is +0 or -0, return the string "0".
   * <li>If m is less than zero, return the string concatenation of the string
   *   "-" and ToString(-m).
   * <li>If m is infinity, return the string "Infinity".
   * <li>Otherwise, let n, k, and s be integers such that k >= 1,
   *   10**k-1 <= s < 10**k, the number value for s * 10**(n-k) is m,
   *   and k is as small as possible. Note that k is the number of
   *   digits in the decimal representation of s, that s is not
   *   divisible by 10, and that the least significant digit of s is not
   *   necessarily uniquely determined by these criteria.
   * <li>If k <= n <= 21, return the string consisting of the k digits
   *   of the decimal representation of s (in order, with no leading
   *   zeroes), followed by n-k occurrences of the character '0'.
   * <li>If 0 < n <= 21, return the string consisting of the
   *   most significant n digits of the decimal representation of s,
   *   followed by a decimal point '.', followed by the
   *   remaining k-n digits of the decimal representation of s.
   * <li>If -6 < n <= 0, return the string consisting of the
   *   character '0', followed by a decimal point '.', followed by -n
   *   occurrences of the character '0', followed by the k digits of
   *   the decimal representation of s.
   * <li>Otherwise, if k = 1, return the string consisting of the
   *   single digit of s, followed by lowercase character 'e', followed
   *   by a plus sign '+' or minus sign '-' according to whether n-1 is
   *   positive or negative, followed by the decimal representation of
   *   the integer abs(n-1) (with no leading zeros).
   * <li>Return the string consisting of the most significant digit
   *   of the decimal representation of s, followed by a decimal point
   *   '.', followed by the remaining k-1 digits of the decimal
   *   representation of s, followed by the lowercase character 'e',
   *   followed by a plus sign '+' or minus sign '-' according to
   *   whether n-1 is positive or negative, followed by the decimal
   *   representation of the integer abs(n-1) (with no leading zeros).
   * </ol>
   */
  public static String numberToString(double m) {
    if (Double.isNaN(m)) { return "NaN"; }
    if (m == 0) { return "0"; // incl. -0
    if (Double.isInfinite(m)) {
      return m >= 0 ? "Infinity" : "-Infinity";
    }
    return numberToString(new BigDecimal(m, TWENTY_ONE_DIGITS_ROUNDED_TO_ZERO));
  }

  private static final MathContext TWENTY_ONE_DIGITS_ROUNDED_TO_ZERO
      = new MathContext(21, RoundingMode.DOWN);

  public static String numberToString(BigDecimal m) {
    int cmp = BigDecimal.ZERO.compareTo(m);
    if (cmp == 0) {
      return "0";
    }
    if (cmp > 0) {
      return "-" + numberToString(m.abs());
    }

    // Round to 21 digits worth and adjust n-k
    if (m.precision() > 21) {
      m = new BigDecimal(m.unscaledValue(), m.scale(),
                         TWENTY_ONE_DIGITS_ROUNDED_TO_ZERO);
      assert m.precision() <= 21;
    }

    m = m.stripTrailingZeros();

    // Choose n, k, and s such that k >= 1, 10**k-1 <= s < 10**k,
    // the number value for s * 10**(n-k) is m, and k is as small as possible.

    int n, k;
    BigDecimal s;

    s = new BigDecimal(m.unscaledValue(), 0);

    // 10**k is the greatest power of 10 <= s, i.e. k = floor(log10(s))
    k = s.precision() - s.scale();

    // (1) s     = m * 10**m.scale()   by def of BigDecimal.unscaledValue()
    // (2) m     = s * 10**(n-k)       by def of s,n,k
    // (3) m     = s * 10**-m.scale()  from 1
    // (4) n - k = -m.scale()          by 2, 3
    // (5) n     = k - m.scale()
    n = k - m.scale();

    String intRep = s.unscaledValue().toString()// independent of Locale

    if (k <= n && n <= 21) {
      // 6. If k <= n <= 21, return the string consisting of the k digits
      //   of the decimal representation of s (in order, with no leading
      //   zeroes), followed by n-k occurrences of the character '0'.
      StringBuilder sb = new StringBuilder(n);
      sb.append(intRep.substring(0, k));
      while (sb.length() < n) { sb.append('0'); }
      return sb.toString();
    } else if (0 < n && n <= 21) {
      // 7. If 0 < n <= 21, return the string consisting of the
      //   most significant n digits of the decimal representation of s,
      //   followed by a decimal point '.', followed by the
      //   remaining k-n digits of the decimal representation of s.
      return intRep.substring(0, n) + "."
          + intRep.substring(n, Math.min(intRep.length(), 21));
    } else if (-6 < n && n <= 0) {
      // 8. If -6 < n <= 0, return the string consisting of the
      //   character '0', followed by a decimal point '.', followed by -n
      //   occurrences of the character '0', followed by the k digits of
      //   the decimal representation of s.
      StringBuilder sb = new StringBuilder(2 + -n + k);
      sb.append("0.");
      for (int i = -n; --i >= 0;) { sb.append('0'); }
      sb.append(intRep, 0, k);
      return sb.toString();
    } else if (k == 1) {
      // 9. Otherwise, if k = 1, return the string consisting of the
      //   single digit of s, followed by lowercase character 'e', followed
      //   by a plus sign '+' or minus sign '-' according to whether n-1 is
      //   positive or negative, followed by the decimal representation of
      //   the integer abs(n-1) (with no leading zeros).
      return intRep + ((n - 1) < 0 ? "e-" : "e+") + Math.abs(n - 1);
    } else {
      // 10. Return the string consisting of the most significant digit
      //   of the decimal representation of s, followed by a decimal point
      //   '.', folloarwed by the remaining k-1 digits of the decimal
      //   representation of s, followed by the lowercase character 'e',
      //   followed by a plus sign '+' or minus sign '-' according to
      //   whether n-1 is positive or negative, followed by the decimal
      //   representation of the integer abs(n-1) (with no leading zeros).
      return intRep.substring(0, 1) + "." + intRep.substring(1, k)
          + ((n - 1) < 0 ? "e-" : "e+") + Math.abs(n - 1);
    }
  }

  public String typeOf() { return "number"; }

  public JsonML toJsonML() {
    FilePosition pos = getFilePosition();
    Number value = getValue();
    if (value instanceof Double) {
      double d = value.doubleValue();
      if (Double.isNaN(d) || Double.isInfinite(d)) {
        NumberLiteral num = new IntegerLiteral(
            FilePosition.startOf(pos), Double.isNaN(d) ? 0 : d < 0 ? -1 : 1);
        NumberLiteral den = new IntegerLiteral(FilePosition.endOf(pos), 0);
        return Operation.createInfix(Operator.DIVISION, num, den).toJsonML();
      }
    }
    return JsonMLBuilder.builder(TagType.LiteralExpr, pos)
        .setAttribute(TagAttr.TYPE, "number")
        .setAttribute(TagAttr.VALUE, value)
        .build();
  }
}
TOP

Related Classes of com.google.caja.parser.js.NumberLiteral

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.