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.text.DecimalFormatSymbols;
034: import java.util.*;
035:
036: /**
037: * @author Taras Puchko
038: */
039: public abstract class FormatContext {
040:
041: private static final DecimalFormatSymbols US_SYMBOLS = new DecimalFormatSymbols(
042: Locale.US);
043:
044: private Locale locale;
045: private DecimalFormatSymbols symbols;
046: private Object[] arguments;
047: private int effectiveIndex;
048: private int ordinaryIndex;
049: private boolean effectiveIndexComputed;
050: private String specifier;
051: private int explicitIndex;
052: private String flags;
053: private int width;
054: private int precision;
055: private String conversion;
056: private String format;
057: private int position;
058:
059: protected FormatContext(Locale locale) {
060: this .locale = locale;
061: }
062:
063: public abstract void append(char c);
064:
065: public abstract void append(String s);
066:
067: public abstract void append(String s, int start, int end);
068:
069: public abstract boolean writeFormattable();
070:
071: public void printf(String format, Object... args) {
072: this .format = format;
073: arguments = args;
074: effectiveIndex = 0;
075: ordinaryIndex = 0;
076: position = 0;
077: int index;
078: while ((index = format.indexOf('%', position)) >= 0) {
079: append(format, position, index);
080: scanOptions(index);
081: effectiveIndexComputed = false;
082: specifier = format.substring(index, position);
083: Conversion instance = Conversion.getInstance(conversion);
084: if (instance == null) {
085: throw new UnknownFormatConversionException(conversion);
086: }
087: instance.format(this );
088: }
089: append(format, position, format.length());
090: }
091:
092: private void scanOptions(int index) {
093: position = index + 1;
094: try {
095: scanExplicitIndex();
096: scanFlags();
097: scanWidth();
098: scanPrecision();
099: scanConversion();
100: } catch (IndexOutOfBoundsException e) {
101: throw new UnknownFormatConversionException(format
102: .substring(index));
103: }
104: }
105:
106: private void scanExplicitIndex() {
107: int index = skipDigits(format, position);
108: if (index > position && format.charAt(index) == '$') {
109: explicitIndex = parse(format, position, index);
110: position = index + 1;
111: } else {
112: explicitIndex = -1;
113: }
114: }
115:
116: private void scanFlags() {
117: int index = skipFlags(format, position);
118: if (index > position) {
119: flags = format.substring(position, index);
120: position = index;
121: } else {
122: flags = "";
123: }
124: }
125:
126: private void scanWidth() {
127: int index = skipDigits(format, position);
128: if (index > position) {
129: width = parse(format, position, index);
130: position = index;
131: } else {
132: width = -1;
133: }
134: }
135:
136: private void scanPrecision() {
137: if (format.charAt(position) == '.') {
138: position++;
139: int index = skipDigits(format, position);
140: if (index > position) {
141: precision = parse(format, position, index);
142: position = index;
143: } else {
144: throw new IndexOutOfBoundsException();
145: }
146: } else {
147: precision = -1;
148: }
149: }
150:
151: private void scanConversion() {
152: char c = format.charAt(position);
153: int endIndex = (c == 't' || c == 'T') ? position + 2
154: : position + 1;
155: conversion = format.substring(position, endIndex);
156: position = endIndex;
157: }
158:
159: private static int skipDigits(String s, int index) {
160: char c = s.charAt(index);
161: while (c >= '0' && c <= '9') {
162: c = s.charAt(++index);
163: }
164: return index;
165: }
166:
167: private static int skipFlags(String s, int index) {
168: char c = s.charAt(index);
169: while (c == '-' || c == '#' || c == '+' || c == ' ' || c == '0'
170: || c == ',' || c == '(' || c == '<') {
171: c = s.charAt(++index);
172: }
173: return index;
174: }
175:
176: private static Integer parse(String format, int beginIndex,
177: int endIndex) {
178: return Integer.valueOf(format.substring(beginIndex, endIndex));
179: }
180:
181: private char getConversionType() {
182: return conversion.charAt(0);
183: }
184:
185: public int getWidth() {
186: return width;
187: }
188:
189: public int getPrecision() {
190: return precision;
191: }
192:
193: public int getNumberPrecision() {
194: return precision >= 0 ? precision : 6;
195: }
196:
197: public Locale getLocale() {
198: return locale;
199: }
200:
201: public DecimalFormatSymbols getSymbols(boolean localized) {
202: if (localized && locale != null) {
203: if (symbols == null) {
204: symbols = new DecimalFormatSymbols(locale);
205: }
206: return symbols;
207: } else {
208: return US_SYMBOLS;
209: }
210: }
211:
212: public IllegalFormatConversionException getConversionException() {
213: return new IllegalFormatConversionException(
214: getConversionType(), getArgument().getClass());
215: }
216:
217: public boolean isUpperCase() {
218: return Character.isUpperCase(getConversionType());
219: }
220:
221: public boolean isFlag(char c) {
222: return flags.indexOf(c) >= 0;
223: }
224:
225: public Object getArgument() {
226: if (!effectiveIndexComputed) {
227: computeEffectiveIndex();
228: effectiveIndexComputed = true;
229: }
230: return arguments[effectiveIndex - 1];
231: }
232:
233: private void computeEffectiveIndex() {
234: if (flags.indexOf('<') < 0) {
235: effectiveIndex = explicitIndex != -1 ? explicitIndex
236: : ++ordinaryIndex;
237: }
238: if (arguments == null || effectiveIndex == 0
239: || effectiveIndex > arguments.length) {
240: throw new MissingFormatArgumentException(specifier);
241: }
242: }
243:
244: public void writeRestricted(String s) {
245: writePadded(precision != -1 && precision < s.length() ? s
246: .substring(0, precision) : s);
247: }
248:
249: public void writePadded(String s) {
250: if (isFlag('-')) {
251: writeCaseSensitive(s);
252: writePadding(s);
253: } else {
254: writePadding(s);
255: writeCaseSensitive(s);
256: }
257: }
258:
259: private void writePadding(String s) {
260: for (int i = width - s.length(); i > 0; i--) {
261: append(' ');
262: }
263: }
264:
265: private void writeCaseSensitive(String s) {
266: if (isUpperCase()) {
267: append(s.toUpperCase());
268: } else {
269: append(s);
270: }
271: }
272:
273: public void assertNoFlag(char flag) {
274: if (isFlag(flag))
275: throw new FormatFlagsConversionMismatchException(flags,
276: getConversionType());
277: }
278:
279: public void assertNoPrecision() {
280: if (precision != -1)
281: throw new IllegalFormatPrecisionException(precision);
282: }
283:
284: public void assertNoWidth() {
285: if (width != -1)
286: throw new IllegalFormatWidthException(width);
287: }
288:
289: public void checkWidth() {
290: if ((isFlag('-') || isFlag('0')) && width == -1) {
291: throw new MissingFormatWidthException(specifier);
292: }
293: }
294:
295: public void checkFlags() {
296: if (isFlag('+') && isFlag(' ') || isFlag('-') && isFlag('0')) {
297: throw new IllegalFormatFlagsException(flags);
298: }
299: }
300:
301: }
|