001: /***
002: * Retrotranslator: a Java bytecode transformer that translates Java classes
003: * compiled with JDK 5.0 into classes that can be run on JVM 1.4.
004: *
005: * Copyright (c) 2005 - 2008 Taras Puchko
006: * All rights reserved.
007: *
008: * Redistribution and use in source and binary forms, with or without
009: * modification, are permitted provided that the following conditions
010: * are met:
011: * 1. Redistributions of source code must retain the above copyright
012: * notice, this list of conditions and the following disclaimer.
013: * 2. Redistributions in binary form must reproduce the above copyright
014: * notice, this list of conditions and the following disclaimer in the
015: * documentation and/or other materials provided with the distribution.
016: * 3. Neither the name of the copyright holders nor the names of its
017: * contributors may be used to endorse or promote products derived from
018: * this software without specific prior written permission.
019: *
020: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
021: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
022: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
023: * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
024: * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
025: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
026: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
027: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
028: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
029: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
030: * THE POSSIBILITY OF SUCH DAMAGE.
031: */package net.sf.retrotranslator.runtime.format;
032:
033: import java.math.*;
034: import java.text.DecimalFormatSymbols;
035:
036: /**
037: * @author Taras Puchko
038: */
039: abstract class FloatingPointConversion extends NumericConversion {
040:
041: protected abstract void printf(FormatContext context,
042: boolean negative, BigDecimal argument);
043:
044: protected void printf(FormatContext context) {
045: Object argument = context.getArgument();
046: if (argument instanceof Double) {
047: printf(context, (Double) argument);
048: } else if (argument instanceof Float) {
049: printf(context, (Float) argument);
050: } else if (argument == null) {
051: context.writeRestricted(String.valueOf(argument));
052: } else if (argument instanceof BigDecimal) {
053: BigDecimal bigDecimal = (BigDecimal) argument;
054: printf(context, bigDecimal.signum() < 0, bigDecimal.abs());
055: } else {
056: throw context.getConversionException();
057: }
058: }
059:
060: private void printf(FormatContext context, double argument) {
061: if (!printSpecialNumber(context, argument)) {
062: printf(context, Double.doubleToLongBits(argument) < 0,
063: BigDecimal.valueOf(Math.abs(argument)));
064: }
065: }
066:
067: protected static void printComputerizedScientificNumber(
068: FormatContext context, boolean negative,
069: BigDecimal argument, int precision) {
070: String unscaled = argument.unscaledValue().toString();
071: StringBuilder builder = new StringBuilder();
072: builder.append(unscaled.charAt(0));
073: if (precision > 0 || context.isFlag('#')) {
074: builder.append('.');
075: }
076: if (precision < unscaled.length()) {
077: builder.append(unscaled.substring(1, precision + 1));
078: } else {
079: builder.append(unscaled.substring(1));
080: appendZeros(builder, precision + 1 - unscaled.length());
081: }
082: int exponent = unscaled.equals("0") ? 0 : unscaled.length()
083: - argument.scale() - 1;
084: builder.append('e').append(exponent < 0 ? '-' : '+');
085: int absoluteExponent = Math.abs(exponent);
086: if (absoluteExponent < 10) {
087: builder.append('0');
088: }
089: builder.append(absoluteExponent);
090: printNumber(context, negative, null, builder, context
091: .getSymbols(false));
092: }
093:
094: protected static void printDecimalNumber(FormatContext context,
095: boolean negative, BigDecimal argument, boolean localized) {
096: String unscaled = argument.unscaledValue().toString();
097: String integerPart = "0";
098: String fractionPart = "";
099: int separatorIndex = unscaled.length() - argument.scale();
100: if (separatorIndex < 0) {
101: fractionPart = appendZeros(new StringBuilder(),
102: -separatorIndex).append(unscaled).toString();
103: } else if (separatorIndex == 0) {
104: fractionPart = unscaled;
105: } else if (separatorIndex < unscaled.length()) {
106: integerPart = unscaled.substring(0, separatorIndex);
107: fractionPart = unscaled.substring(separatorIndex);
108: } else if (separatorIndex == unscaled.length()) {
109: integerPart = unscaled;
110: } else {
111: integerPart = appendZeros(new StringBuilder(unscaled),
112: separatorIndex - unscaled.length()).toString();
113: }
114: StringBuilder builder = new StringBuilder();
115: DecimalFormatSymbols symbols = context.getSymbols(localized);
116: appendNumber(builder, integerPart, context.isFlag(','), symbols);
117: if (fractionPart.length() > 0 || context.isFlag('#')) {
118: builder.append(symbols.getDecimalSeparator());
119: }
120: appendNumber(builder, fractionPart, false, symbols);
121: printNumber(context, negative, null, builder, symbols);
122: }
123:
124: protected static StringBuilder appendZeros(StringBuilder builder,
125: int count) {
126: for (int i = 0; i < count; i++) {
127: builder.append('0');
128: }
129: return builder;
130: }
131:
132: protected static BigDecimal round(BigDecimal decimal, int shift) {
133: int scale = decimal.scale() - shift;
134: return scale >= 0 ? decimal.setScale(scale,
135: BigDecimal.ROUND_HALF_UP) : decimal.movePointRight(
136: scale).setScale(0, BigDecimal.ROUND_HALF_UP)
137: .movePointLeft(scale);
138: }
139:
140: public static class ComputerizedScientificConversion extends
141: FloatingPointConversion {
142:
143: public void format(FormatContext context) {
144: context.checkFlags();
145: context.assertNoFlag(',');
146: printf(context);
147: }
148:
149: protected void printf(FormatContext context, boolean negative,
150: BigDecimal argument) {
151: int shift = argument.unscaledValue().toString().length()
152: - context.getNumberPrecision() - 1;
153: printComputerizedScientificNumber(context, negative, round(
154: argument, shift), context.getNumberPrecision());
155: }
156: }
157:
158: public static class DecimalConversion extends
159: FloatingPointConversion {
160:
161: public void format(FormatContext context) {
162: context.checkWidth();
163: context.checkFlags();
164: printf(context);
165: }
166:
167: protected void printf(FormatContext context, boolean negative,
168: BigDecimal argument) {
169: BigDecimal roundedArgument = argument.setScale(context
170: .getNumberPrecision(), BigDecimal.ROUND_HALF_UP);
171: printDecimalNumber(context, negative, roundedArgument, true);
172: }
173: }
174:
175: public static class GeneralScientificConversion extends
176: FloatingPointConversion {
177:
178: public void format(FormatContext context) {
179: context.checkFlags();
180: context.assertNoFlag('#');
181: printf(context);
182: }
183:
184: protected void printf(FormatContext context, boolean negative,
185: BigDecimal argument) {
186: int precision = Math.max(context.getNumberPrecision(), 1);
187: BigDecimal roundedArgument = round(argument, argument
188: .unscaledValue().toString().length()
189: - precision);
190: if (roundedArgument.compareTo(new BigDecimal(
191: BigInteger.ONE, 4)) >= 0
192: && roundedArgument.compareTo(BigDecimal.ONE
193: .movePointRight(precision)) < 0) {
194: printDecimalNumber(context, negative, roundedArgument,
195: context.getArgument() instanceof BigDecimal);
196: } else {
197: printComputerizedScientificNumber(context, negative,
198: roundedArgument, precision - 1);
199: }
200: }
201:
202: }
203:
204: }
|