KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > gnu > kawa > functions > LispFormat


1 package gnu.kawa.functions;
2 import gnu.text.*;
3 import gnu.lists.*;
4 import java.text.Format JavaDoc;
5 import java.text.FieldPosition JavaDoc;
6 import java.text.ParseException JavaDoc;
7 import java.io.Writer JavaDoc;
8 import gnu.math.*;
9 import gnu.mapping.OutPort;
10
11 /** A representation of a parsed Common Lisp-style format. */
12
13 public class LispFormat extends CompoundFormat
14 {
15   public static final String JavaDoc paramFromList = "<from list>";
16   public static final String JavaDoc paramFromCount = "<from count>";
17   public static final String JavaDoc paramUnspecified = "<unspecified>";
18
19   public LispFormat(char[] format, int offset, int length)
20     throws ParseException JavaDoc
21   {
22     super(null, 0);
23     // The index in spec of the most recent ~{, ~(, ~{ or ~[.
24
int start_nesting = -1;
25     int choices_seen = 0; // Number of "~;" seen.
26

27     StringBuffer JavaDoc litbuf = new StringBuffer JavaDoc(100);
28     java.util.Stack JavaDoc stack = new java.util.Stack JavaDoc();
29
30     int limit = offset + length;
31     int i = offset;
32     for (;;)
33       {
34     if ((i >= limit || format[i] == '~') && litbuf.length() > 0)
35       {
36         stack.push(new LiteralFormat(litbuf));
37         litbuf.setLength(0);
38       }
39     if (i >= limit)
40       break;
41     char ch = format[i++];
42     if (ch != '~')
43       {
44         litbuf.append(ch);
45         continue;
46       }
47     int speci = stack.size();
48     ch = format[i++];
49     for (;;)
50       {
51         if (ch == '#')
52           {
53         stack.push(paramFromCount);
54         ch = format[i++];
55           }
56         else if (ch == 'v' || ch == 'V')
57           {
58         stack.push(paramFromList);
59         ch = format[i++];
60           }
61         else if (ch == '-' || Character.digit(ch, 10) >= 0)
62           {
63         boolean neg = (ch == '-');
64         if (neg)
65           ch = format[i++];
66         int val = 0;
67         int start = i;
68         for (;;)
69           {
70             int dig = Character.digit(ch, 10);
71             if (dig < 0)
72               break;
73             val = 10 * val + dig;
74             ch = format[i++];
75           }
76         stack.push(i - start < 8 ? IntNum.make(neg ? - val : val)
77                : IntNum.valueOf(format, start, i-start, 10, neg));
78           }
79         else if (ch == '\'')
80           {
81         stack.push(Char.make(format[i++]));
82         ch = format[i++];
83           }
84         else if (ch == ',')
85           {
86         stack.push(paramUnspecified);
87           }
88         else
89           break;
90         if (ch != ',')
91           break;
92         ch = format[i++];
93       }
94     boolean seenColon = false;
95     boolean seenAt = false;
96     for (;;)
97       {
98         if (ch == ':')
99           seenColon = true;
100         else if (ch == '@')
101           seenAt = true;
102         else
103           break;
104         ch = format[i++];
105       }
106     ch = Character.toUpperCase(ch);
107     int numParams = stack.size() - speci;
108     Format fmt;
109     int minWidth, padChar, charVal, param1, param2, param3, count;
110     switch (ch)
111       {
112       case 'R': case 'D': case 'O': case 'B': case 'X':
113         int argstart = speci;
114         int base;
115         if (ch == 'R') base = getParam(stack, argstart++);
116         else if (ch == 'D') base = 10;
117         else if (ch == 'O') base = 8;
118         else if (ch == 'X') base = 16;
119         else base = 2;
120         minWidth = getParam(stack, argstart);
121         padChar = getParam(stack, argstart+1);
122         int commaChar = getParam(stack, argstart+2);
123         int commaInterval = getParam(stack, argstart+3);
124             int flags = 0;
125             if (seenColon)
126               flags |= IntegerFormat.SHOW_GROUPS;
127             if (seenAt)
128               flags |= IntegerFormat.SHOW_PLUS;
129         fmt = IntegerFormat.getInstance(base, minWidth, padChar,
130                                             commaChar, commaInterval, flags);
131         break;
132       case 'P':
133         fmt = LispPluralFormat.getInstance(seenColon, seenAt);
134         break;
135       case 'E':
136       case 'F':
137       case 'G':
138       case '$':
139         LispRealFormat dfmt = new LispRealFormat();
140         dfmt.op = ch;
141         dfmt.arg1 = getParam(stack, speci);
142         dfmt.arg2 = getParam(stack, speci+1);
143         dfmt.arg3 = getParam(stack, speci+2);
144         dfmt.arg4 = getParam(stack, speci+3);
145         if (ch != '$')
146           {
147         dfmt.arg5 = getParam(stack, speci+4);
148         if (ch == 'E' || ch == 'G')
149           {
150             dfmt.arg6 = getParam(stack, speci+5);
151             dfmt.arg7 = getParam(stack, speci+6);
152           }
153           }
154         dfmt.showPlus = seenAt;
155         dfmt.internalPad = seenColon;
156         if (dfmt.argsUsed == 0)
157           fmt = dfmt.resolve(null, 0);
158         else
159           fmt = dfmt;
160         break;
161       case 'A': case 'S': case 'W':
162       case 'Y': // SRFI-48 "yuppify" (pretty-print)
163
// We don't distinguish between ~S and ~W. FIXME.
164
fmt = ObjectFormat.getInstance(ch != 'A');
165         if (numParams > 0)
166           {
167         minWidth = getParam(stack, speci);
168         int colInc = getParam(stack, speci+1);
169         int minPad = getParam(stack, speci+2);
170         padChar = getParam(stack, speci+3);
171         fmt = new LispObjectFormat((ReportFormat) fmt,
172                        minWidth, colInc, minPad,
173                        padChar, seenAt ? 0 : 100);
174           }
175         break;
176       case 'C':
177         charVal = numParams > 0 ? getParam(stack, speci)
178           : PARAM_FROM_LIST;
179         fmt = LispCharacterFormat.getInstance(charVal, 1,
180                           seenAt, seenColon);
181         break;
182       case '*':
183         fmt = new LispRepositionFormat(getParam(stack, speci),
184                        seenColon, seenAt);
185         break;
186       case '(':
187         ch = seenColon ? (seenAt ? 'U' : 'C') : (seenAt ? 'T': 'L');
188         CaseConvertFormat cfmt = new CaseConvertFormat(null, ch);
189         stack.setSize(speci);
190         stack.push(cfmt);
191         stack.push(IntNum.make(start_nesting));
192         start_nesting = speci;
193         continue;
194       case ')':
195         if (start_nesting < 0
196         || ! (stack.elementAt(start_nesting)
197               instanceof CaseConvertFormat))
198           throw new ParseException JavaDoc("saw ~) without matching ~(", i);
199         cfmt = (CaseConvertFormat) stack.elementAt(start_nesting);
200         cfmt.setBaseFormat(popFormats(stack, start_nesting + 2, speci));
201         start_nesting = ((IntNum) stack.pop()).intValue();
202         continue;
203       case '?':
204         LispIterationFormat lfmt = new LispIterationFormat();
205         lfmt.seenAt = seenAt;
206         lfmt.maxIterations = 1;
207         lfmt.atLeastOnce = true;
208         fmt = lfmt;
209         break;
210       case '{':
211         lfmt = new LispIterationFormat();
212         lfmt.seenAt = seenAt;
213         lfmt.seenColon = seenColon;
214         lfmt.maxIterations = getParam(stack, speci);
215         stack.setSize(speci);
216         stack.push(lfmt);
217         stack.push(IntNum.make(start_nesting));
218         start_nesting = speci;
219         continue;
220       case '}':
221         if (start_nesting < 0
222         || ! (stack.elementAt(start_nesting)
223               instanceof LispIterationFormat))
224           throw new ParseException JavaDoc("saw ~} without matching ~{", i);
225         lfmt = (LispIterationFormat) stack.elementAt(start_nesting);
226         lfmt.atLeastOnce = seenColon;
227         if (speci > start_nesting + 2)
228           lfmt.body = popFormats(stack, start_nesting + 2, speci);
229         start_nesting = ((IntNum) stack.pop()).intValue();
230         continue;
231       case '<':
232         LispPrettyFormat pfmt = new LispPrettyFormat();
233         pfmt.seenAt = seenAt;
234         if (seenColon)
235           {
236         pfmt.prefix = "(";
237         pfmt.suffix = ")";
238           }
239         else
240           {
241         pfmt.prefix = "";
242         pfmt.suffix = "";
243           }
244         stack.setSize(speci);
245         stack.push(pfmt);
246         stack.push(IntNum.make(start_nesting));
247         stack.push(IntNum.make(choices_seen));
248         start_nesting = speci;
249         choices_seen = 0;
250         continue;
251       case '>':
252         if (start_nesting < 0
253         || ! (stack.elementAt(start_nesting)
254               instanceof LispPrettyFormat))
255           throw new ParseException JavaDoc("saw ~> without matching ~<", i);
256         fmt = popFormats(stack, start_nesting + 3 + choices_seen, speci);
257         stack.push(fmt);
258         pfmt = (LispPrettyFormat) stack.elementAt(start_nesting);
259         pfmt.segments = getFormats(stack, start_nesting + 3, stack.size());
260         stack.setSize(start_nesting + 3);
261         start_nesting = ((IntNum) stack.pop()).intValue();
262         start_nesting = ((IntNum) stack.pop()).intValue();
263         if (seenColon)
264           { // Logical Block for pretty-printing
265
int nsegments = pfmt.segments.length;
266         if (nsegments > 3)
267           throw new ParseException JavaDoc("too many segments in Logical Block format", i);
268         if (nsegments >= 2)
269           {
270             if (! (pfmt.segments[0] instanceof LiteralFormat))
271               throw new ParseException JavaDoc("prefix segment is not literal", i);
272             pfmt.prefix = ((LiteralFormat) pfmt.segments[0]).content();
273             pfmt.body = pfmt.segments[1];
274           }
275         else
276           pfmt.body = pfmt.segments[0];
277         if (nsegments >=3)
278           {
279             if (! (pfmt.segments[2] instanceof LiteralFormat))
280               throw new ParseException JavaDoc("suffix segment is not literal", i);
281             pfmt.suffix = ((LiteralFormat) pfmt.segments[2]).content();
282           }
283           }
284         else // Justification
285
throw new ParseException JavaDoc("not implemented: justfication i.e. ~<...~>", i);
286         continue;
287       case '[':
288         LispChoiceFormat afmt = new LispChoiceFormat();
289         afmt.param = getParam(stack, speci);
290         if (afmt.param == PARAM_UNSPECIFIED)
291           afmt.param = PARAM_FROM_LIST;
292         if (seenColon)
293           afmt.testBoolean = true;
294         if (seenAt)
295           afmt.skipIfFalse = true;
296         stack.setSize(speci);
297         stack.push(afmt);
298         stack.push(IntNum.make(start_nesting));
299         stack.push(IntNum.make(choices_seen));
300         start_nesting = speci;
301         choices_seen = 0;
302         continue;
303       case ';':
304         if (start_nesting >= 0)
305           {
306         if (stack.elementAt(start_nesting)
307             instanceof LispChoiceFormat)
308           {
309             afmt = (LispChoiceFormat) stack.elementAt(start_nesting);
310             if (seenColon)
311               afmt.lastIsDefault = true;
312             fmt = popFormats(stack,
313                      start_nesting + 3 + choices_seen, speci);
314             stack.push(fmt);
315             choices_seen++;
316             continue;
317           }
318         else if (stack.elementAt(start_nesting)
319             instanceof LispPrettyFormat)
320           {
321             pfmt = (LispPrettyFormat) stack.elementAt(start_nesting);
322             if (seenAt)
323               pfmt.perLine = true;
324             fmt = popFormats(stack,
325                      start_nesting + 3 + choices_seen, speci);
326             stack.push(fmt);
327             choices_seen++;
328             continue;
329           }
330         // else if saw ~< ...
331
}
332         throw new ParseException JavaDoc("saw ~; without matching ~[ or ~<", i);
333       case ']':
334         if (start_nesting < 0
335         || ! (stack.elementAt(start_nesting)
336                       instanceof LispChoiceFormat))
337               throw new ParseException JavaDoc("saw ~] without matching ~[", i);
338         fmt = popFormats(stack, start_nesting + 3 + choices_seen, speci);
339         stack.push(fmt);
340         afmt = (LispChoiceFormat) stack.elementAt(start_nesting);
341         afmt.choices = getFormats(stack, start_nesting + 3, stack.size());
342         stack.setSize(start_nesting + 3);
343         choices_seen = ((IntNum) stack.pop()).intValue();
344         start_nesting = ((IntNum) stack.pop()).intValue();
345         continue;
346       case '^':
347         param1 = getParam(stack, speci);
348         param2 = getParam(stack, speci+1);
349         param3 = getParam(stack, speci+2);
350         fmt = new LispEscapeFormat(param1, param2, param3);
351         break;
352       case '\n':
353         if (seenAt)
354           litbuf.append(ch);
355         if (! seenColon)
356           {
357         while (i < limit)
358           {
359             ch = format[i++];
360             if (! Character.isWhitespace(ch))
361               {
362             i--;
363             break;
364               }
365           }
366           }
367         continue;
368       case '!':
369         fmt = FlushFormat.getInstance();
370         break;
371       case 'T':
372         param1 = getParam(stack, speci);
373         param2 = getParam(stack, speci+1);
374         param3 = getParam(stack, speci+2);
375         fmt = new LispTabulateFormat(param1, param2, param3, seenAt);
376         break;
377       case '&':
378         param1 = getParam(stack, speci);
379         fmt = new LispFreshlineFormat(param1);
380         break;
381       case 'I': // Indent
382
param1 = getParam(stack, speci);
383         if (param1 == PARAM_UNSPECIFIED)
384           param1 = 0;
385         fmt = LispIndentFormat.getInstance(param1, seenColon);
386         break;
387       case '_': // conditional newline
388
param1 = getParam(stack, speci);
389         if (param1 == PARAM_UNSPECIFIED)
390           param1 = 1;
391         charVal = seenColon && seenAt ? '\n' : ' ';
392         int kind;
393         if (seenAt && seenColon) kind = PrettyWriter.NEWLINE_MANDATORY;
394         else if (seenAt) kind = PrettyWriter.NEWLINE_MISER;
395         else if (seenColon) kind = PrettyWriter.NEWLINE_FILL;
396         else kind = PrettyWriter.NEWLINE_LINEAR;
397         fmt = LispNewlineFormat.getInstance(param1, kind);
398         break;
399       case '~':
400         if (numParams == 0)
401           {
402         litbuf.append(ch);
403         continue;
404           }
405         /* ... otherwise fall through ... */
406       case '|':
407         count = getParam(stack, speci);
408         if (count == PARAM_UNSPECIFIED)
409           count = 1;
410         // EXTENSION: Allow repeating other characters than '~'.
411
charVal = getParam(stack, speci+1);
412         if (charVal == PARAM_UNSPECIFIED)
413           charVal = ch == '|' ? '\f' : '~';
414         fmt = LispCharacterFormat.getInstance(charVal, count,
415                           false, false);
416         break;
417       case '%':
418         count = getParam(stack, speci);
419         if (count == PARAM_UNSPECIFIED)
420           count = 1;
421         fmt = LispNewlineFormat.getInstance(count,
422                         PrettyWriter.NEWLINE_LITERAL);
423         break;
424           default:
425         throw new ParseException JavaDoc("unrecognized format specifier ~"+ch, i);
426           }
427     stack.setSize(speci);
428     stack.push(fmt);
429       }
430     if (i > limit)
431       throw new IndexOutOfBoundsException JavaDoc();
432     if (start_nesting >= 0)
433       {
434     throw new ParseException JavaDoc("missing ~] or ~}", i);
435       }
436     this.length = stack.size();
437     this.formats = new Format[this.length];
438     stack.copyInto(this.formats);
439   }
440
441   static Format[] getFormats(java.util.Vector JavaDoc vector, int start, int end)
442   {
443     Format[] f = new Format[end - start];
444     for (int i = start; i < end; i++)
445       f[i - start] = (Format) vector.elementAt(i);
446     return f;
447   }
448
449   static Format popFormats(java.util.Vector JavaDoc vector, int start, int end)
450   {
451     Format f;
452     if (end == start + 1)
453       f = (Format) vector.elementAt(start);
454     else
455       f = new CompoundFormat(getFormats(vector, start, end));
456     vector.setSize(start);
457     return f;
458   }
459
460   public LispFormat (String JavaDoc str)
461     throws ParseException JavaDoc
462   {
463     this(str.toCharArray());
464   }
465
466   /*
467   private void clearSpecs (int speci, int max)
468   {
469     int num = specs_length - speci - 1;
470     for (int i = num; i < max; i++)
471       addSpec(PARAM_UNSPECIFIED);
472     specs_length = speci + 1;
473   }
474   */

475
476   /*
477   private void addSpec(Format fmt)
478   {
479     if (formats == null)
480       formats = new Format[4];
481     else
482       {
483     if (this.length == formats.length)
484       {
485         Format[] newformats = new Format[2 * this.length];
486         System.arraycopy(formats, 0, newformats, 0, this.length);
487         formats = newformats;
488       }
489       }
490     formats[this.length] = fmt;
491     addSpec(this.length);
492     this.length++;
493   }
494   */

495
496   /*
497   private void addSpec(int val)
498   {
499     //System.err.println("addSpec("+val+") at:"+specs_length);
500     int old_size = specs.length;
501     if (specs_length >= old_size)
502       {
503     int[] new_specs = new int[2 * old_size];
504     System.arraycopy(specs, 0, new_specs, 0, old_size);
505     specs = new_specs;
506       }
507     specs[specs_length++] = val;
508   }
509   */

510
511   public LispFormat(char[] format)
512     throws ParseException JavaDoc
513   {
514     this(format, 0, format.length);
515   }
516
517   public static int getParam(java.util.Vector JavaDoc vec, int index)
518   {
519     if (index >= vec.size())
520       return PARAM_UNSPECIFIED;
521     Object JavaDoc arg = vec.elementAt(index);
522     if (arg == paramFromList)
523       return PARAM_FROM_LIST;
524     if (arg == paramFromCount)
525       return PARAM_FROM_COUNT;
526     if (arg == paramUnspecified)
527       return PARAM_UNSPECIFIED;
528     return getParam(arg, PARAM_UNSPECIFIED);
529   }
530
531   /** Convert sequence (or Object[]) to Object[].
532    * Return null if not a valid Sequence. */

533   public static Object JavaDoc[] asArray (Object JavaDoc arg)
534   {
535     if (arg instanceof Object JavaDoc[])
536       return (Object JavaDoc[]) arg;
537     if (!(arg instanceof Sequence))
538       return null;
539     int count = ((Sequence) arg).size();
540     Object JavaDoc[] arr = new Object JavaDoc[count];
541     int i = 0;
542     while (arg instanceof Pair)
543       {
544     Pair pair = (Pair) arg;
545     arr[i++] = pair.car;
546     arg = pair.cdr;
547       }
548     if (i < count)
549       {
550     if (! (arg instanceof Sequence))
551       return null;
552     int npairs = i;
553     Sequence seq = (Sequence) arg;
554     for (; i < count; i++)
555       arr[i] = seq.get(npairs + i);
556       }
557     return arr;
558   }
559 }
560
561 /** Add plural suffixes ("s" or "y/ies") of English words.
562  * Used to implement the Common Lisp ~P ('Plural') format operator. */

563
564 class LispPluralFormat extends ReportFormat
565 {
566   boolean backup;
567   boolean y;
568
569   public static LispPluralFormat getInstance (boolean backup, boolean y)
570   {
571     LispPluralFormat fmt = new LispPluralFormat();
572     fmt.backup = backup;
573     fmt.y = y;
574     return fmt;
575   }
576
577   public int format(Object JavaDoc[] args, int start,
578             Writer JavaDoc dst, FieldPosition JavaDoc fpos)
579     throws java.io.IOException JavaDoc
580   {
581     if (backup)
582       start--;
583     Object JavaDoc arg = args[start++];
584     boolean plural = arg != IntNum.one();
585     if (y)
586       print(dst, plural ? "ies" : "y");
587     else if (plural)
588       dst.write('s');
589     return start;
590   }
591 }
592
593
594 /** Handle formatting of characters.
595  * Used to implement the Common List ~C (Character) and ~~ (Tilde)
596  * format operators. */

597
598 class LispCharacterFormat extends ReportFormat
599 {
600   boolean seenAt;
601   boolean seenColon;
602   int count;
603   int charVal;
604
605   public static LispCharacterFormat
606   getInstance(int charVal, int count, boolean seenAt, boolean seenColon)
607   {
608     LispCharacterFormat fmt = new LispCharacterFormat();
609     fmt.count = count;
610     fmt.charVal = charVal;
611     fmt.seenAt = seenAt;
612     fmt.seenColon = seenColon;
613     return fmt;
614   }
615
616   public int format(Object JavaDoc[] args, int start,
617             Writer JavaDoc dst, FieldPosition JavaDoc fpos)
618     throws java.io.IOException JavaDoc
619   {
620     int count = getParam(this.count, 1, args, start);
621     if (this.count == LispFormat.PARAM_FROM_LIST) start++;
622     int charVal = getParam(this.charVal, '?', args, start);
623     if (this.charVal == LispFormat.PARAM_FROM_LIST) start++;
624     while (--count >= 0)
625       printChar (charVal, seenAt, seenColon, dst);
626     return start;
627   }
628
629   public static void printChar(int ch, boolean seenAt, boolean seenColon,
630                    Writer JavaDoc dst)
631     throws java.io.IOException JavaDoc
632   {
633     if (seenAt)
634       {
635     print(dst, Char.toScmReadableString(ch));
636       }
637     else if (seenColon)
638       {
639     if (ch < ' ')
640       {
641         dst.write('^');
642         dst.write(ch + 0x40);
643       }
644     else if (ch >= 0x7f)
645       {
646         print(dst, "#\\");
647         print(dst, Integer.toString(ch, 8));
648       }
649     else
650       dst.write(ch);
651       }
652     else
653       {
654     // if (ch > 0xFFFF) print surrogate chars; else
655
dst.write(ch);
656       }
657   }
658 }
659
660 /** Handle formatting of newline ~% and ~_ format operator. */
661
662 class LispNewlineFormat extends ReportFormat
663 {
664   static final String JavaDoc line_separator
665     = System.getProperty("line.separator", "\n");
666
667   /** One of NEWLINE_LITERAL, NEWLINE_LINEAR, NEWLINE_FILL, NEWLINE_MISER
668    * or NEWLINE_MANDATORY. These are defined in gnu.text.PrettyWriter. */

669   int kind;
670
671   int count;
672
673   public static LispNewlineFormat
674   getInstance(int count, int kind)
675   {
676     LispNewlineFormat fmt = new LispNewlineFormat();
677     fmt.count = count;
678     fmt.kind = kind;
679     return fmt;
680   }
681
682   public int format(Object JavaDoc[] args, int start,
683             Writer JavaDoc dst, FieldPosition JavaDoc fpos)
684     throws java.io.IOException JavaDoc
685   {
686     int count = getParam(this.count, 1, args, start);
687     if (this.count == LispFormat.PARAM_FROM_LIST) start++;
688     while (--count >= 0)
689       printNewline (kind, dst);
690     return start;
691   }
692
693   public static void printNewline(int kind, Writer JavaDoc dst)
694     throws java.io.IOException JavaDoc
695   {
696     if (dst instanceof OutPort && kind != PrettyWriter.NEWLINE_LITERAL)
697       ((OutPort) dst).writeBreak(kind);
698     else if (dst instanceof java.io.PrintWriter JavaDoc)
699       // May make a difference if autoflush. // FIXME flush if OutPort?
700
((java.io.PrintWriter JavaDoc) dst).println();
701     else
702       dst.write(line_separator);
703   }
704 }
705
706 /** Handle formatting of ~I (indent) format operator. */
707
708 class LispIndentFormat extends ReportFormat
709 {
710   boolean current;
711
712   int columns;
713
714   public static LispIndentFormat
715   getInstance(int columns, boolean current)
716   {
717     LispIndentFormat fmt = new LispIndentFormat();
718     fmt.columns = columns;
719     fmt.current = current;
720     return fmt;
721   }
722
723   public int format(Object JavaDoc[] args, int start,
724             Writer JavaDoc dst, FieldPosition JavaDoc fpos)
725     throws java.io.IOException JavaDoc
726   {
727     int columns = getParam(this.columns, 0, args, start);
728     if (this.columns == LispFormat.PARAM_FROM_LIST) start++;
729     if (dst instanceof OutPort)
730       ((OutPort) dst).setIndentation(columns, current);
731     return start;
732   }
733 }
734
735 /** Perform general padding.
736  * Used to implement the Common Lisp ~A (Ascii) and ~ (S-expression),
737  * format operators, unless they have no parameters. */

738
739 class LispObjectFormat extends ReportFormat
740 {
741   int minWidth;
742   int colInc;
743   int minPad;
744   int padChar;
745   int where;
746   ReportFormat base;
747
748   public LispObjectFormat(ReportFormat base,
749               int minWidth, int colInc, int minPad, int padChar,
750               int where)
751   {
752     this.base = base;
753     this.minWidth = minWidth;
754     this.colInc = colInc;
755     this.minPad = minPad;
756     this.padChar = padChar;
757     this.where = where;
758   }
759
760   public int format(Object JavaDoc[] args, int start,
761             Writer JavaDoc dst, FieldPosition JavaDoc fpos)
762     throws java.io.IOException JavaDoc
763   {
764     int minWidth = getParam(this.minWidth, 0, args, start);
765     if (this.minWidth == LispFormat.PARAM_FROM_LIST) start++;
766     int colInc = getParam(this.colInc, 1, args, start);
767     if (this.colInc == LispFormat.PARAM_FROM_LIST) start++;
768     int minPad = getParam(this.minPad, 0, args, start);
769     if (this.minPad == LispFormat.PARAM_FROM_LIST) start++;
770     char padChar = getParam(this.padChar, ' ', args, start);
771     if (this.padChar == LispFormat.PARAM_FROM_LIST) start++;
772     return gnu.text.PadFormat.format(base, args, start, dst,
773                      padChar, minWidth, colInc, minPad,
774                      where, fpos);
775   }
776 }
777
778 class LispEscapeFormat extends ReportFormat
779 {
780   int param1;
781   int param2;
782   int param3;
783   boolean escapeAll;
784
785   public final static LispEscapeFormat alwaysTerminate
786   = new LispEscapeFormat(0, LispFormat.PARAM_UNSPECIFIED);
787
788   public LispEscapeFormat(int param1, int param2)
789   {
790     this.param1 = param1;
791     this.param2 = param2;
792     this.param3 = LispFormat.PARAM_UNSPECIFIED;
793   }
794
795   public LispEscapeFormat(int param1, int param2, int param3)
796   {
797     this.param1 = param1;
798     this.param2 = param2;
799     this.param3 = param3;
800   }
801
802   static Numeric getParam(int param, Object JavaDoc[] args, int start)
803   {
804     if (param == LispFormat.PARAM_FROM_COUNT)
805       return IntNum.make(args.length - start);
806     if (param == LispFormat.PARAM_FROM_LIST)
807       {
808     Object JavaDoc arg = args[start];
809     if (arg instanceof Numeric)
810       return (Numeric) arg;
811     if (arg instanceof Number JavaDoc)
812       {
813         if (arg instanceof Float JavaDoc || arg instanceof Double JavaDoc)
814           return new DFloNum(((Number JavaDoc) arg).doubleValue());
815         return IntNum.make(((Number JavaDoc) arg).longValue());
816       }
817     if (arg instanceof Char)
818       return new IntNum(((Char) arg).intValue());
819     if (arg instanceof Character JavaDoc)
820       return new IntNum((int) ((Character JavaDoc) arg).charValue());
821     return new DFloNum(Double.NaN);
822       }
823     return IntNum.make(param);
824   }
825
826   /** WRONG: Tests if we should exit the the surrounding format.
827    * Returns 2*ARGS_USED+(DO_TERMINATE?1:0), where ARGS_USED is the
828    * number of arguments consumed by the specification, and
829    * DO_TERMINATE is true if we actually should exit.
830    */

831   public int format(Object JavaDoc[] args, int start, Writer JavaDoc dst, FieldPosition JavaDoc fpos)
832     throws java.io.IOException JavaDoc
833   {
834     int orig_start = start;
835     boolean do_terminate;
836     if (param1 == LispFormat.PARAM_UNSPECIFIED)
837       do_terminate = start == args.length;
838     else if (param2 == LispFormat.PARAM_UNSPECIFIED && param1 == 0)
839       do_terminate = true; // Efficiency hack
840
else
841       {
842     Numeric arg1 = getParam(param1, args, start);
843     if (param1 == LispFormat.PARAM_FROM_LIST) start++;
844     if (param2 == LispFormat.PARAM_UNSPECIFIED)
845       {
846         do_terminate = arg1.isZero();
847       }
848     else
849       {
850         Numeric arg2 = getParam(param2, args, start);
851         if (param2 == LispFormat.PARAM_FROM_LIST) start++;
852         if (param3 == LispFormat.PARAM_UNSPECIFIED)
853           {
854         do_terminate = arg1.equals(arg2);
855           }
856         else
857           {
858         Numeric arg3 = getParam(param3, args, start);
859         if (param3 == LispFormat.PARAM_FROM_LIST) start++;
860         do_terminate = arg2.geq(arg1) && arg3.geq(arg2);
861           }
862       }
863       }
864     return result(! do_terminate ? 0 : escapeAll ? ESCAPE_ALL : ESCAPE_NORMAL,
865           start);
866   }
867
868   public final static int ESCAPE_NORMAL = 0xF1;
869   public final static int ESCAPE_ALL = 0xF2;
870 }
871
872 /** Handle <code>~&lt;...~&gt;</code> - pretty-printing logical block.
873  * (Justification is not implemented.) */

874
875 class LispPrettyFormat extends ReportFormat
876 {
877   Format[] segments;
878   Format body;
879   String JavaDoc prefix;
880   String JavaDoc suffix;
881   boolean perLine;
882   boolean seenAt;
883
884   public int format(Object JavaDoc[] args, int start,
885                     Writer JavaDoc dst, FieldPosition JavaDoc fpos)
886     throws java.io.IOException JavaDoc
887   {
888     String JavaDoc pre = prefix;
889     String JavaDoc suf = suffix;
890     OutPort out = dst instanceof OutPort ? (OutPort) dst : null;
891     try
892       {
893     if (seenAt)
894       {
895         if (out != null)
896           out.startLogicalBlock(pre, perLine, suffix);
897         start = ReportFormat.format(body, args, start, dst, fpos);
898       }
899     else
900       {
901         Object JavaDoc curArg = args[start];
902         Object JavaDoc[] curArr = LispFormat.asArray(curArg);
903         if (curArr == null)
904           pre = suf = "";
905         if (out != null)
906           out.startLogicalBlock(pre, perLine, suffix);
907         if (curArr == null)
908           ObjectFormat.format(curArg, dst, -1, true);
909         else
910           ReportFormat.format(body, curArr, 0, dst, fpos);
911         start++;
912       }
913       }
914     finally
915       {
916     if (out != null)
917       out.endLogicalBlock(suf);
918       }
919     return start;
920   }
921
922   public String JavaDoc toString ()
923   {
924     StringBuffer JavaDoc sbuf = new StringBuffer JavaDoc();
925     sbuf.append("LispPrettyFormat[");
926     sbuf.append("prefix: \""); sbuf.append(prefix);
927     sbuf.append("\", suffix: \""); sbuf.append(suffix);
928     sbuf.append("\", body: ");
929     sbuf.append(body);
930     sbuf.append("]");
931     return sbuf.toString();
932   }
933 }
934
935 class LispIterationFormat extends ReportFormat
936 {
937   int maxIterations;
938   boolean seenAt;
939   boolean seenColon;
940   boolean atLeastOnce;
941
942   Format body;
943
944   public static int format(Format body, int maxIterations,
945                Object JavaDoc[] args, int start,
946                Writer JavaDoc dst, boolean seenColon, boolean atLeastOnce)
947     throws java.io.IOException JavaDoc
948   {
949     for (int i = 0; ; i++)
950       {
951     if (i == maxIterations && maxIterations != -1)
952       break;
953     if (start == args.length && (i > 0 || ! atLeastOnce))
954       break;
955     if (seenColon)
956       {
957         Object JavaDoc curArg = args[start];
958         Object JavaDoc[] curArr = LispFormat.asArray(curArg);
959         if (curArr == null)
960           { // ?
961
}
962         int result = ReportFormat.format(body, curArr, 0, dst, null);
963         start++;
964         if (ReportFormat.resultCode(result) == LispEscapeFormat.ESCAPE_ALL)
965           break;
966       }
967     else
968       {
969         start = ReportFormat.format(body, args, start, dst, null);
970         if (start < 0)
971           {
972         start = ReportFormat.nextArg(start);
973         break;
974           }
975       }
976       }
977     return start;
978   }
979
980   public int format(Object JavaDoc[] args, int start,
981                     Writer JavaDoc dst, FieldPosition JavaDoc fpos)
982     throws java.io.IOException JavaDoc
983   {
984     int maxIterations = getParam(this.maxIterations, -1,
985                         args, start);
986     if (this.maxIterations == LispFormat.PARAM_FROM_LIST) start++;
987
988     Format body = this.body;
989     if (body == null)
990       {
991     // from args
992
Object JavaDoc arg = args[start++];
993     if (arg instanceof java.text.Format JavaDoc)
994       body = (java.text.Format JavaDoc) arg;
995     else
996       {
997         try
998           {
999         body = new LispFormat(arg.toString());
1000          }
1001        catch (Exception JavaDoc ex)
1002          {
1003        print(dst, "<invalid argument for \"~{~}\" format>");
1004        return args.length; // FIXME
1005
}
1006      }
1007      }
1008    if (seenAt)
1009      {
1010    return format(body, maxIterations, args, start,
1011              dst, seenColon, atLeastOnce);
1012      }
1013    else
1014      {
1015    Object JavaDoc arg = args[start];
1016    Object JavaDoc[] curArgs = LispFormat.asArray(arg);
1017    if (curArgs == null)
1018      dst.write("{"+arg+"}".toString());
1019    else
1020      format(body, maxIterations, curArgs, 0,
1021         dst, seenColon, atLeastOnce);
1022    return start + 1;
1023      }
1024  }
1025
1026  public String JavaDoc toString ()
1027  {
1028    StringBuffer JavaDoc sbuf = new StringBuffer JavaDoc();
1029    sbuf.append("LispIterationFormat[");
1030    sbuf.append(body);
1031    sbuf.append("]");
1032    return sbuf.toString();
1033  }
1034}
1035
1036class LispChoiceFormat extends ReportFormat
1037{
1038  int param;
1039  boolean lastIsDefault;
1040  boolean testBoolean; // choice[0] is selected if arg is false.
1041
boolean skipIfFalse;
1042  Format[] choices;
1043
1044  public int format(Object JavaDoc[] args, int start, Writer JavaDoc dst, FieldPosition JavaDoc fpos)
1045    throws java.io.IOException JavaDoc
1046  {
1047    Format fmt;
1048    if (testBoolean) // Handles ~:[false~;true~]
1049
{
1050    fmt = choices[args[start] == Boolean.FALSE ? 0 : 1];
1051    start++;
1052      }
1053    else if (! skipIfFalse)
1054      {
1055    int index = getParam(this.param, LispFormat.PARAM_FROM_LIST,
1056                    args, start);
1057    if (param == LispFormat.PARAM_FROM_LIST) start++;
1058    if (index < 0 || index >= choices.length)
1059      {
1060        if (lastIsDefault)
1061          index = choices.length - 1;
1062        else
1063          return start;
1064      }
1065    fmt = choices[index];
1066      }
1067    else
1068      {
1069    if (args[start] == Boolean.FALSE)
1070      return start + 1;
1071    fmt = choices[0];
1072      }
1073    return ReportFormat.format(fmt, args, start, dst, fpos);
1074  }
1075}
1076
1077class LispRepositionFormat extends ReportFormat
1078{
1079  boolean backwards;
1080  boolean absolute;
1081  int count;
1082
1083  public LispRepositionFormat(int count, boolean backwards, boolean absolute)
1084  {
1085    this.count = count;
1086    this.backwards = backwards;
1087    this.absolute = absolute;
1088  }
1089
1090  public int format(Object JavaDoc[] args, int start, Writer JavaDoc dst, FieldPosition JavaDoc fpos)
1091    throws java.io.IOException JavaDoc
1092  {
1093    int count = getParam(this.count, absolute ? 0 : 1,
1094                    args, start);
1095    if (!absolute)
1096      {
1097    if (backwards)
1098      count = -count;
1099    count += start;
1100      }
1101    return count < 0 ? 0 : count > args.length ? args.length : count;
1102  }
1103}
1104
1105class LispFreshlineFormat extends ReportFormat
1106{
1107  int count;
1108
1109  public LispFreshlineFormat (int count)
1110  {
1111    this.count = count;
1112  }
1113
1114  public int format(Object JavaDoc[] args, int start, Writer JavaDoc dst, FieldPosition JavaDoc fpos)
1115    throws java.io.IOException JavaDoc
1116  {
1117    int count = getParam(this.count, 1, args, start);
1118    if (this.count == LispFormat.PARAM_FROM_LIST) start++;
1119    if (count > 0)
1120      {
1121    if (dst instanceof OutPort)
1122      {
1123        ((OutPort) dst).freshLine();
1124        count--;
1125      }
1126    while (--count >= 0)
1127      dst.write('\n');
1128      }
1129    return start;
1130  }
1131}
1132
1133class LispTabulateFormat extends ReportFormat
1134{
1135  boolean relative;
1136  int colnum;
1137  int colinc;
1138  int padChar;
1139
1140  public LispTabulateFormat(int colnum, int colinc,
1141                int padChar, boolean relative)
1142  {
1143    this.colnum = colnum;
1144    this.colinc = colinc;
1145    this.relative = relative;
1146    this.padChar = padChar;
1147  }
1148
1149  public int format(Object JavaDoc[] args, int start, Writer JavaDoc dst, FieldPosition JavaDoc fpos)
1150    throws java.io.IOException JavaDoc
1151  {
1152    int colnum = getParam(this.colnum, 1, args, start);
1153    if (this.colnum == LispFormat.PARAM_FROM_LIST) start++;
1154    int colinc = getParam(this.colinc, 1, args, start);
1155    if (this.colinc == LispFormat.PARAM_FROM_LIST) start++;
1156    // Extension from SLIB:
1157
char padChar = getParam(this.padChar, ' ', args, start);
1158    if (this.padChar == LispFormat.PARAM_FROM_LIST) start++;
1159    int column = -1;
1160    if (dst instanceof OutPort)
1161      column = ((OutPort) dst).getColumnNumber();
1162    int spaces;
1163    if (column >= 0)
1164      {
1165    if (! relative)
1166      {
1167        if (column < colnum)
1168          spaces = colnum - column;
1169        else if (colinc <= 0)
1170          spaces = 0;
1171        else
1172          spaces = colinc - (column - colnum) % colinc;
1173      }
1174    else
1175      {
1176        spaces = colnum + colinc - (column + colnum) % colinc;
1177      }
1178      }
1179    else
1180      {
1181    spaces = relative ? colnum : 2;
1182      }
1183    while (--spaces >= 0)
1184      dst.write(padChar);
1185    return start;
1186  }
1187}
1188
1189/* Support for ~F, ~$, ~E, ~G. */
1190
1191class LispRealFormat extends ReportFormat
1192{
1193  char op;
1194  int arg1;
1195  int arg2;
1196  int arg3;
1197  int arg4;
1198  int arg5;
1199  int arg6;
1200  int arg7;
1201  boolean showPlus;
1202  boolean internalPad;
1203  /** Twice the number of args consumed; odd if any arg is PARAM_FROM_COUNT. */
1204  int argsUsed;
1205
1206  LispRealFormat()
1207  {
1208    argsUsed = (arg1 == LispFormat.PARAM_FROM_COUNT
1209        || arg2 == LispFormat.PARAM_FROM_COUNT
1210        || arg3 == LispFormat.PARAM_FROM_COUNT
1211        || arg4 == LispFormat.PARAM_FROM_COUNT
1212        || arg5 == LispFormat.PARAM_FROM_COUNT
1213        || arg6 == LispFormat.PARAM_FROM_COUNT
1214        || arg7 == LispFormat.PARAM_FROM_COUNT) ? 1 : 0;
1215    if (arg1 == LispFormat.PARAM_FROM_LIST) argsUsed += 2;
1216    if (arg2 == LispFormat.PARAM_FROM_LIST) argsUsed += 2;
1217    if (arg3 == LispFormat.PARAM_FROM_LIST) argsUsed += 2;
1218    if (arg4 == LispFormat.PARAM_FROM_LIST) argsUsed += 2;
1219    if (arg5 == LispFormat.PARAM_FROM_LIST) argsUsed += 2;
1220    if (arg6 == LispFormat.PARAM_FROM_LIST) argsUsed += 2;
1221    if (arg7 == LispFormat.PARAM_FROM_LIST) argsUsed += 2;
1222  }
1223
1224  public Format resolve (Object JavaDoc[] args, int start)
1225  {
1226    if (op == '$')
1227      {
1228    FixedRealFormat mfmt = new FixedRealFormat();
1229    int decimals = getParam(this.arg1, 2, args, start);
1230    if (this.arg1 == LispFormat.PARAM_FROM_LIST) start++;
1231    int digits = getParam(this.arg2, 1, args, start);
1232    if (this.arg2 == LispFormat.PARAM_FROM_LIST) start++;
1233    int width = getParam(this.arg3, 0, args, start);
1234    if (this.arg3 == LispFormat.PARAM_FROM_LIST) start++;
1235    char padChar = getParam(this.arg4, ' ', args, start);
1236    if (this.arg4 == LispFormat.PARAM_FROM_LIST) start++;
1237
1238    mfmt.setMaximumFractionDigits(decimals);
1239    mfmt.setMinimumIntegerDigits(digits);
1240    mfmt.width = width;
1241    mfmt.padChar = padChar;
1242    mfmt.internalPad = internalPad;
1243    mfmt.showPlus = showPlus;
1244    return mfmt;
1245      }
1246    else if (op == 'F')
1247      {
1248    FixedRealFormat mfmt = new FixedRealFormat();
1249    int width = getParam(this.arg1, 0, args, start);
1250    if (this.arg1 == LispFormat.PARAM_FROM_LIST) start++;
1251    int decimals = getParam(this.arg2, -1, args, start);
1252    if (this.arg2 == LispFormat.PARAM_FROM_LIST) start++;
1253    int scale = getParam(this.arg3, 0, args, start);
1254    if (this.arg3 == LispFormat.PARAM_FROM_LIST) start++;
1255    mfmt.overflowChar = getParam(this.arg4, '\0', args, start);
1256    if (this.arg4 == LispFormat.PARAM_FROM_LIST) start++;
1257    char padChar = getParam(this.arg5, ' ', args, start);
1258    if (this.arg5 == LispFormat.PARAM_FROM_LIST) start++;
1259    mfmt.setMaximumFractionDigits(decimals);
1260    mfmt.setMinimumIntegerDigits(0);
1261    mfmt.width = width;
1262    mfmt.scale = scale;
1263    mfmt.padChar = padChar;
1264    mfmt.internalPad = internalPad;
1265    mfmt.showPlus = showPlus;
1266    return mfmt;
1267      }
1268    else // if (op == 'E' || op == 'G')
1269
{
1270    ExponentialFormat efmt = new ExponentialFormat();
1271        efmt.exponentShowSign = true;
1272    efmt.width = getParam(this.arg1, 0, args, start);
1273    if (this.arg1 == LispFormat.PARAM_FROM_LIST) start++;
1274    efmt.fracDigits = getParam(this.arg2, -1, args, start);
1275    if (this.arg2 == LispFormat.PARAM_FROM_LIST) start++;
1276    efmt.expDigits = getParam(this.arg3, 0, args, start);
1277    if (this.arg3 == LispFormat.PARAM_FROM_LIST) start++;
1278    efmt.intDigits = getParam(this.arg4, 1, args, start);
1279    if (this.arg4 == LispFormat.PARAM_FROM_LIST) start++;
1280    efmt.overflowChar = getParam(this.arg5, '\0', args, start);
1281    if (this.arg5 == LispFormat.PARAM_FROM_LIST) start++;
1282    efmt.padChar = getParam(this.arg6, ' ', args, start);
1283    if (this.arg6 == LispFormat.PARAM_FROM_LIST) start++;
1284    efmt.exponentChar = getParam(this.arg7, 'E', args, start);
1285    if (this.arg7 == LispFormat.PARAM_FROM_LIST) start++;
1286    efmt.general = op == 'G';
1287    efmt.showPlus = showPlus;
1288    return efmt;
1289      }
1290  }
1291
1292  public int format(Object JavaDoc[] args, int start, Writer JavaDoc dst, FieldPosition JavaDoc fpos)
1293    throws java.io.IOException JavaDoc
1294  {
1295    StringBuffer JavaDoc sbuf = new StringBuffer JavaDoc(100);
1296    Format fmt = resolve(args, start);
1297    start += argsUsed >> 1;
1298    RealNum value = (RealNum) args[start++];
1299    fmt.format(value, sbuf, fpos);
1300    dst.write(sbuf.toString());
1301    return start;
1302  }
1303}
1304
Popular Tags