001: package gnu.kawa.functions;
002:
003: import gnu.text.*;
004: import java.text.ParseException;
005: import java.text.Format;
006: import gnu.mapping.*;
007: import gnu.lists.*;
008:
009: public class ParseFormat extends Procedure1 {
010: public static final ParseFormat parseFormat = new ParseFormat(false);
011:
012: boolean emacsStyle = true;
013: public static final int PARAM_UNSPECIFIED = LispFormat.PARAM_UNSPECIFIED;
014: public static final int PARAM_FROM_LIST = LispFormat.PARAM_FROM_LIST;
015:
016: public ParseFormat(boolean emacsStyle) {
017: this .emacsStyle = emacsStyle;
018: }
019:
020: public static final int SEEN_MINUS = 1;
021: public static final int SEEN_PLUS = 2;
022: public static final int SEEN_SPACE = 4;
023: public static final int SEEN_ZERO = 8;
024: public static final int SEEN_HASH = 16;
025:
026: public ReportFormat parseFormat(LineBufferedReader fmt)
027: throws java.text.ParseException, java.io.IOException {
028: return parseFormat(fmt, emacsStyle ? '?' : '~');
029: }
030:
031: public static ReportFormat parseFormat(LineBufferedReader fmt,
032: char magic) throws java.text.ParseException,
033: java.io.IOException {
034: StringBuffer fbuf = new StringBuffer(100);
035: int position = 0;
036: java.util.Vector formats = new java.util.Vector();
037: Format format;
038: for (;;) {
039: int ch = fmt.read();
040: if (ch >= 0) {
041: if (ch != magic) {
042: // FIXME - quote special characters!
043: fbuf.append((char) ch);
044: continue;
045: }
046: ch = fmt.read();
047: if (ch == magic) {
048: fbuf.append((char) ch);
049: continue;
050: }
051: }
052: int len = fbuf.length();
053: if (len > 0) {
054: char[] text = new char[len];
055: fbuf.getChars(0, len, text, 0);
056: fbuf.setLength(0);
057: formats.addElement(new LiteralFormat(text));
058: }
059: if (ch < 0)
060: break;
061: int digit;
062: if (ch == '$') {
063: ch = fmt.read();
064: position = Character.digit((char) ch, 10);
065: if (position < 0)
066: throw new ParseException(
067: "missing number (position) after '%$'", -1);
068: for (;;) {
069: ch = fmt.read();
070: digit = Character.digit((char) ch, 10);
071: if (digit < 0)
072: break;
073: position = 10 * position + digit;
074: }
075: position--; /* Convert to zero-origin index. */
076: }
077:
078: int flags = 0;
079: for (;; ch = fmt.read()) {
080: switch ((char) ch) {
081: case '-':
082: flags |= SEEN_MINUS;
083: continue;
084: case '+':
085: flags |= SEEN_PLUS;
086: continue;
087: case ' ':
088: flags |= SEEN_SPACE;
089: continue;
090: case '0':
091: flags |= SEEN_ZERO;
092: continue;
093: case '#':
094: flags |= SEEN_HASH;
095: continue;
096: }
097: break;
098: }
099:
100: int width = PARAM_UNSPECIFIED;
101: digit = Character.digit((char) ch, 10);
102: if (digit >= 0) {
103: width = digit;
104: for (;;) {
105: ch = fmt.read();
106: digit = Character.digit((char) ch, 10);
107: if (digit < 0)
108: break;
109: width = 10 * width + digit;
110: }
111: } else if (ch == '*')
112: width = PARAM_FROM_LIST;
113:
114: int precision = PARAM_UNSPECIFIED;
115: if (ch == '.') {
116: if (ch == '*')
117: precision = PARAM_FROM_LIST;
118: else {
119: precision = 0;
120: for (;;) {
121: ch = fmt.read();
122: digit = Character.digit((char) ch, 10);
123: if (digit < 0)
124: break;
125: precision = 10 * precision + digit;
126: }
127: }
128: }
129:
130: switch (ch) {
131: case 's':
132: case 'S':
133: format = new ObjectFormat(ch == 'S', precision);
134: break;
135:
136: case 'x':
137: case 'X':
138: case 'i':
139: case 'd':
140: case 'o':
141: int base;
142: int fflags = 0;
143: if (ch == 'd' || ch == 'i')
144: base = 10;
145: else if (ch == 'o')
146: base = 8;
147: else { /* if (ch == 'x' || ch == 'X') */
148: base = 16;
149: if (ch == 'X')
150: fflags = IntegerFormat.UPPERCASE;
151: }
152: boolean seenColon = false;
153: boolean seenAt = false;
154: char padChar = (flags & (SEEN_ZERO + SEEN_MINUS)) == SEEN_ZERO ? '0'
155: : ' ';
156: if ((flags & SEEN_HASH) != 0)
157: fflags |= IntegerFormat.SHOW_BASE;
158: if ((flags & SEEN_PLUS) != 0)
159: fflags |= IntegerFormat.SHOW_PLUS;
160: if ((flags & SEEN_MINUS) != 0)
161: fflags |= IntegerFormat.PAD_RIGHT;
162: if ((flags & SEEN_SPACE) != 0)
163: fflags |= IntegerFormat.SHOW_SPACE;
164: if (precision != PARAM_UNSPECIFIED) {
165: flags &= ~SEEN_ZERO;
166: fflags |= IntegerFormat.MIN_DIGITS;
167: format = IntegerFormat.getInstance(base, precision,
168: '0', PARAM_UNSPECIFIED, PARAM_UNSPECIFIED,
169: fflags);
170: } else
171: format = IntegerFormat.getInstance(base, width,
172: padChar, PARAM_UNSPECIFIED,
173: PARAM_UNSPECIFIED, fflags);
174: break;
175: case 'e':
176: case 'f':
177: case 'g':
178: format = new ObjectFormat(false); // FIXME
179: break;
180: default:
181: throw new ParseException("unknown format character '"
182: + ch + "'", -1);
183: }
184: if (width > 0) {
185: char padChar = (flags & SEEN_ZERO) != 0 ? '0' : ' ';
186: int where;
187: if ((flags & SEEN_MINUS) != 0)
188: where = 100;
189: else if (padChar == '0')
190: where = -1;
191: else
192: where = 0;
193: format = new gnu.text.PadFormat(format, width, padChar,
194: where);
195: }
196: // FIXME handle re-positioning
197: //fbuf.append('{');
198: // fbuf.append(position);
199: //fbuf.append('}');
200: formats.addElement(format);
201: position++;
202: }
203: // System.err.println("format: "+fbuf.toString());
204: int fcount = formats.size();
205: if (fcount == 1) {
206: Object f = formats.elementAt(0);
207: if (f instanceof ReportFormat)
208: return (ReportFormat) f;
209: }
210: Format[] farray = new Format[fcount];
211: formats.copyInto(farray);
212: return new CompoundFormat(farray);
213: }
214:
215: public Object apply1(Object arg) {
216: return asFormat(arg, emacsStyle ? '?' : '~');
217: }
218:
219: public static ReportFormat asFormat(Object arg, char style) {
220: try {
221: if (arg instanceof ReportFormat)
222: return (ReportFormat) arg;
223: if (style == '~')
224: return new LispFormat(arg.toString());
225: else {
226: InPort iport;
227: if (arg instanceof FString) {
228: FString str = (FString) arg;
229: iport = new CharArrayInPort(str.data, str.size);
230: } else
231: iport = new CharArrayInPort(arg.toString());
232: try {
233: return parseFormat(iport, style);
234: } finally {
235: iport.close();
236: }
237: }
238: } catch (java.io.IOException ex) {
239: throw new RuntimeException("Error parsing format (" + ex
240: + ")");
241: } catch (ParseException ex) {
242: throw new RuntimeException("Invalid format (" + ex + ")");
243: } catch (IndexOutOfBoundsException ex) {
244: throw new RuntimeException("End while parsing format");
245: }
246: }
247: }
|