0001: package gnu.kawa.functions;
0002:
0003: import gnu.text.*;
0004: import gnu.lists.*;
0005: import java.text.Format;
0006: import java.text.FieldPosition;
0007: import java.text.ParseException;
0008: import java.io.Writer;
0009: import gnu.math.*;
0010: import gnu.mapping.OutPort;
0011:
0012: /** A representation of a parsed Common Lisp-style format. */
0013:
0014: public class LispFormat extends CompoundFormat {
0015: public static final String paramFromList = "<from list>";
0016: public static final String paramFromCount = "<from count>";
0017: public static final String paramUnspecified = "<unspecified>";
0018:
0019: public LispFormat(char[] format, int offset, int length)
0020: throws ParseException {
0021: super (null, 0);
0022: // The index in spec of the most recent ~{, ~(, ~{ or ~[.
0023: int start_nesting = -1;
0024: int choices_seen = 0; // Number of "~;" seen.
0025:
0026: StringBuffer litbuf = new StringBuffer(100);
0027: java.util.Stack stack = new java.util.Stack();
0028:
0029: int limit = offset + length;
0030: int i = offset;
0031: for (;;) {
0032: if ((i >= limit || format[i] == '~') && litbuf.length() > 0) {
0033: stack.push(new LiteralFormat(litbuf));
0034: litbuf.setLength(0);
0035: }
0036: if (i >= limit)
0037: break;
0038: char ch = format[i++];
0039: if (ch != '~') {
0040: litbuf.append(ch);
0041: continue;
0042: }
0043: int speci = stack.size();
0044: ch = format[i++];
0045: for (;;) {
0046: if (ch == '#') {
0047: stack.push(paramFromCount);
0048: ch = format[i++];
0049: } else if (ch == 'v' || ch == 'V') {
0050: stack.push(paramFromList);
0051: ch = format[i++];
0052: } else if (ch == '-' || Character.digit(ch, 10) >= 0) {
0053: boolean neg = (ch == '-');
0054: if (neg)
0055: ch = format[i++];
0056: int val = 0;
0057: int start = i;
0058: for (;;) {
0059: int dig = Character.digit(ch, 10);
0060: if (dig < 0)
0061: break;
0062: val = 10 * val + dig;
0063: ch = format[i++];
0064: }
0065: stack.push(i - start < 8 ? IntNum.make(neg ? -val
0066: : val) : IntNum.valueOf(format, start, i
0067: - start, 10, neg));
0068: } else if (ch == '\'') {
0069: stack.push(Char.make(format[i++]));
0070: ch = format[i++];
0071: } else if (ch == ',') {
0072: stack.push(paramUnspecified);
0073: } else
0074: break;
0075: if (ch != ',')
0076: break;
0077: ch = format[i++];
0078: }
0079: boolean seenColon = false;
0080: boolean seenAt = false;
0081: for (;;) {
0082: if (ch == ':')
0083: seenColon = true;
0084: else if (ch == '@')
0085: seenAt = true;
0086: else
0087: break;
0088: ch = format[i++];
0089: }
0090: ch = Character.toUpperCase(ch);
0091: int numParams = stack.size() - speci;
0092: Format fmt;
0093: int minWidth, padChar, charVal, param1, param2, param3, count;
0094: switch (ch) {
0095: case 'R':
0096: case 'D':
0097: case 'O':
0098: case 'B':
0099: case 'X':
0100: int argstart = speci;
0101: int base;
0102: if (ch == 'R')
0103: base = getParam(stack, argstart++);
0104: else if (ch == 'D')
0105: base = 10;
0106: else if (ch == 'O')
0107: base = 8;
0108: else if (ch == 'X')
0109: base = 16;
0110: else
0111: base = 2;
0112: minWidth = getParam(stack, argstart);
0113: padChar = getParam(stack, argstart + 1);
0114: int commaChar = getParam(stack, argstart + 2);
0115: int commaInterval = getParam(stack, argstart + 3);
0116: int flags = 0;
0117: if (seenColon)
0118: flags |= IntegerFormat.SHOW_GROUPS;
0119: if (seenAt)
0120: flags |= IntegerFormat.SHOW_PLUS;
0121: fmt = IntegerFormat.getInstance(base, minWidth,
0122: padChar, commaChar, commaInterval, flags);
0123: break;
0124: case 'P':
0125: fmt = LispPluralFormat.getInstance(seenColon, seenAt);
0126: break;
0127: case 'E':
0128: case 'F':
0129: case 'G':
0130: case '$':
0131: LispRealFormat dfmt = new LispRealFormat();
0132: dfmt.op = ch;
0133: dfmt.arg1 = getParam(stack, speci);
0134: dfmt.arg2 = getParam(stack, speci + 1);
0135: dfmt.arg3 = getParam(stack, speci + 2);
0136: dfmt.arg4 = getParam(stack, speci + 3);
0137: if (ch != '$') {
0138: dfmt.arg5 = getParam(stack, speci + 4);
0139: if (ch == 'E' || ch == 'G') {
0140: dfmt.arg6 = getParam(stack, speci + 5);
0141: dfmt.arg7 = getParam(stack, speci + 6);
0142: }
0143: }
0144: dfmt.showPlus = seenAt;
0145: dfmt.internalPad = seenColon;
0146: if (dfmt.argsUsed == 0)
0147: fmt = dfmt.resolve(null, 0);
0148: else
0149: fmt = dfmt;
0150: break;
0151: case 'A':
0152: case 'S':
0153: case 'W':
0154: case 'Y': // SRFI-48 "yuppify" (pretty-print)
0155: // We don't distinguish between ~S and ~W. FIXME.
0156: fmt = ObjectFormat.getInstance(ch != 'A');
0157: if (numParams > 0) {
0158: minWidth = getParam(stack, speci);
0159: int colInc = getParam(stack, speci + 1);
0160: int minPad = getParam(stack, speci + 2);
0161: padChar = getParam(stack, speci + 3);
0162: fmt = new LispObjectFormat((ReportFormat) fmt,
0163: minWidth, colInc, minPad, padChar,
0164: seenAt ? 0 : 100);
0165: }
0166: break;
0167: case 'C':
0168: charVal = numParams > 0 ? getParam(stack, speci)
0169: : PARAM_FROM_LIST;
0170: fmt = LispCharacterFormat.getInstance(charVal, 1,
0171: seenAt, seenColon);
0172: break;
0173: case '*':
0174: fmt = new LispRepositionFormat(getParam(stack, speci),
0175: seenColon, seenAt);
0176: break;
0177: case '(':
0178: ch = seenColon ? (seenAt ? 'U' : 'C') : (seenAt ? 'T'
0179: : 'L');
0180: CaseConvertFormat cfmt = new CaseConvertFormat(null, ch);
0181: stack.setSize(speci);
0182: stack.push(cfmt);
0183: stack.push(IntNum.make(start_nesting));
0184: start_nesting = speci;
0185: continue;
0186: case ')':
0187: if (start_nesting < 0
0188: || !(stack.elementAt(start_nesting) instanceof CaseConvertFormat))
0189: throw new ParseException(
0190: "saw ~) without matching ~(", i);
0191: cfmt = (CaseConvertFormat) stack
0192: .elementAt(start_nesting);
0193: cfmt.setBaseFormat(popFormats(stack, start_nesting + 2,
0194: speci));
0195: start_nesting = ((IntNum) stack.pop()).intValue();
0196: continue;
0197: case '?':
0198: LispIterationFormat lfmt = new LispIterationFormat();
0199: lfmt.seenAt = seenAt;
0200: lfmt.maxIterations = 1;
0201: lfmt.atLeastOnce = true;
0202: fmt = lfmt;
0203: break;
0204: case '{':
0205: lfmt = new LispIterationFormat();
0206: lfmt.seenAt = seenAt;
0207: lfmt.seenColon = seenColon;
0208: lfmt.maxIterations = getParam(stack, speci);
0209: stack.setSize(speci);
0210: stack.push(lfmt);
0211: stack.push(IntNum.make(start_nesting));
0212: start_nesting = speci;
0213: continue;
0214: case '}':
0215: if (start_nesting < 0
0216: || !(stack.elementAt(start_nesting) instanceof LispIterationFormat))
0217: throw new ParseException(
0218: "saw ~} without matching ~{", i);
0219: lfmt = (LispIterationFormat) stack
0220: .elementAt(start_nesting);
0221: lfmt.atLeastOnce = seenColon;
0222: if (speci > start_nesting + 2)
0223: lfmt.body = popFormats(stack, start_nesting + 2,
0224: speci);
0225: start_nesting = ((IntNum) stack.pop()).intValue();
0226: continue;
0227: case '<':
0228: LispPrettyFormat pfmt = new LispPrettyFormat();
0229: pfmt.seenAt = seenAt;
0230: if (seenColon) {
0231: pfmt.prefix = "(";
0232: pfmt.suffix = ")";
0233: } else {
0234: pfmt.prefix = "";
0235: pfmt.suffix = "";
0236: }
0237: stack.setSize(speci);
0238: stack.push(pfmt);
0239: stack.push(IntNum.make(start_nesting));
0240: stack.push(IntNum.make(choices_seen));
0241: start_nesting = speci;
0242: choices_seen = 0;
0243: continue;
0244: case '>':
0245: if (start_nesting < 0
0246: || !(stack.elementAt(start_nesting) instanceof LispPrettyFormat))
0247: throw new ParseException(
0248: "saw ~> without matching ~<", i);
0249: fmt = popFormats(stack, start_nesting + 3
0250: + choices_seen, speci);
0251: stack.push(fmt);
0252: pfmt = (LispPrettyFormat) stack
0253: .elementAt(start_nesting);
0254: pfmt.segments = getFormats(stack, start_nesting + 3,
0255: stack.size());
0256: stack.setSize(start_nesting + 3);
0257: start_nesting = ((IntNum) stack.pop()).intValue();
0258: start_nesting = ((IntNum) stack.pop()).intValue();
0259: if (seenColon) { // Logical Block for pretty-printing
0260: int nsegments = pfmt.segments.length;
0261: if (nsegments > 3)
0262: throw new ParseException(
0263: "too many segments in Logical Block format",
0264: i);
0265: if (nsegments >= 2) {
0266: if (!(pfmt.segments[0] instanceof LiteralFormat))
0267: throw new ParseException(
0268: "prefix segment is not literal", i);
0269: pfmt.prefix = ((LiteralFormat) pfmt.segments[0])
0270: .content();
0271: pfmt.body = pfmt.segments[1];
0272: } else
0273: pfmt.body = pfmt.segments[0];
0274: if (nsegments >= 3) {
0275: if (!(pfmt.segments[2] instanceof LiteralFormat))
0276: throw new ParseException(
0277: "suffix segment is not literal", i);
0278: pfmt.suffix = ((LiteralFormat) pfmt.segments[2])
0279: .content();
0280: }
0281: } else
0282: // Justification
0283: throw new ParseException(
0284: "not implemented: justfication i.e. ~<...~>",
0285: i);
0286: continue;
0287: case '[':
0288: LispChoiceFormat afmt = new LispChoiceFormat();
0289: afmt.param = getParam(stack, speci);
0290: if (afmt.param == PARAM_UNSPECIFIED)
0291: afmt.param = PARAM_FROM_LIST;
0292: if (seenColon)
0293: afmt.testBoolean = true;
0294: if (seenAt)
0295: afmt.skipIfFalse = true;
0296: stack.setSize(speci);
0297: stack.push(afmt);
0298: stack.push(IntNum.make(start_nesting));
0299: stack.push(IntNum.make(choices_seen));
0300: start_nesting = speci;
0301: choices_seen = 0;
0302: continue;
0303: case ';':
0304: if (start_nesting >= 0) {
0305: if (stack.elementAt(start_nesting) instanceof LispChoiceFormat) {
0306: afmt = (LispChoiceFormat) stack
0307: .elementAt(start_nesting);
0308: if (seenColon)
0309: afmt.lastIsDefault = true;
0310: fmt = popFormats(stack, start_nesting + 3
0311: + choices_seen, speci);
0312: stack.push(fmt);
0313: choices_seen++;
0314: continue;
0315: } else if (stack.elementAt(start_nesting) instanceof LispPrettyFormat) {
0316: pfmt = (LispPrettyFormat) stack
0317: .elementAt(start_nesting);
0318: if (seenAt)
0319: pfmt.perLine = true;
0320: fmt = popFormats(stack, start_nesting + 3
0321: + choices_seen, speci);
0322: stack.push(fmt);
0323: choices_seen++;
0324: continue;
0325: }
0326: // else if saw ~< ...
0327: }
0328: throw new ParseException(
0329: "saw ~; without matching ~[ or ~<", i);
0330: case ']':
0331: if (start_nesting < 0
0332: || !(stack.elementAt(start_nesting) instanceof LispChoiceFormat))
0333: throw new ParseException(
0334: "saw ~] without matching ~[", i);
0335: fmt = popFormats(stack, start_nesting + 3
0336: + choices_seen, speci);
0337: stack.push(fmt);
0338: afmt = (LispChoiceFormat) stack
0339: .elementAt(start_nesting);
0340: afmt.choices = getFormats(stack, start_nesting + 3,
0341: stack.size());
0342: stack.setSize(start_nesting + 3);
0343: choices_seen = ((IntNum) stack.pop()).intValue();
0344: start_nesting = ((IntNum) stack.pop()).intValue();
0345: continue;
0346: case '^':
0347: param1 = getParam(stack, speci);
0348: param2 = getParam(stack, speci + 1);
0349: param3 = getParam(stack, speci + 2);
0350: fmt = new LispEscapeFormat(param1, param2, param3);
0351: break;
0352: case '\n':
0353: if (seenAt)
0354: litbuf.append(ch);
0355: if (!seenColon) {
0356: while (i < limit) {
0357: ch = format[i++];
0358: if (!Character.isWhitespace(ch)) {
0359: i--;
0360: break;
0361: }
0362: }
0363: }
0364: continue;
0365: case '!':
0366: fmt = FlushFormat.getInstance();
0367: break;
0368: case 'T':
0369: param1 = getParam(stack, speci);
0370: param2 = getParam(stack, speci + 1);
0371: param3 = getParam(stack, speci + 2);
0372: fmt = new LispTabulateFormat(param1, param2, param3,
0373: seenAt);
0374: break;
0375: case '&':
0376: param1 = getParam(stack, speci);
0377: fmt = new LispFreshlineFormat(param1);
0378: break;
0379: case 'I': // Indent
0380: param1 = getParam(stack, speci);
0381: if (param1 == PARAM_UNSPECIFIED)
0382: param1 = 0;
0383: fmt = LispIndentFormat.getInstance(param1, seenColon);
0384: break;
0385: case '_': // conditional newline
0386: param1 = getParam(stack, speci);
0387: if (param1 == PARAM_UNSPECIFIED)
0388: param1 = 1;
0389: charVal = seenColon && seenAt ? '\n' : ' ';
0390: int kind;
0391: if (seenAt && seenColon)
0392: kind = PrettyWriter.NEWLINE_MANDATORY;
0393: else if (seenAt)
0394: kind = PrettyWriter.NEWLINE_MISER;
0395: else if (seenColon)
0396: kind = PrettyWriter.NEWLINE_FILL;
0397: else
0398: kind = PrettyWriter.NEWLINE_LINEAR;
0399: fmt = LispNewlineFormat.getInstance(param1, kind);
0400: break;
0401: case '~':
0402: if (numParams == 0) {
0403: litbuf.append(ch);
0404: continue;
0405: }
0406: /* ... otherwise fall through ... */
0407: case '|':
0408: count = getParam(stack, speci);
0409: if (count == PARAM_UNSPECIFIED)
0410: count = 1;
0411: // EXTENSION: Allow repeating other characters than '~'.
0412: charVal = getParam(stack, speci + 1);
0413: if (charVal == PARAM_UNSPECIFIED)
0414: charVal = ch == '|' ? '\f' : '~';
0415: fmt = LispCharacterFormat.getInstance(charVal, count,
0416: false, false);
0417: break;
0418: case '%':
0419: count = getParam(stack, speci);
0420: if (count == PARAM_UNSPECIFIED)
0421: count = 1;
0422: fmt = LispNewlineFormat.getInstance(count,
0423: PrettyWriter.NEWLINE_LITERAL);
0424: break;
0425: default:
0426: throw new ParseException(
0427: "unrecognized format specifier ~" + ch, i);
0428: }
0429: stack.setSize(speci);
0430: stack.push(fmt);
0431: }
0432: if (i > limit)
0433: throw new IndexOutOfBoundsException();
0434: if (start_nesting >= 0) {
0435: throw new ParseException("missing ~] or ~}", i);
0436: }
0437: this .length = stack.size();
0438: this .formats = new Format[this .length];
0439: stack.copyInto(this .formats);
0440: }
0441:
0442: static Format[] getFormats(java.util.Vector vector, int start,
0443: int end) {
0444: Format[] f = new Format[end - start];
0445: for (int i = start; i < end; i++)
0446: f[i - start] = (Format) vector.elementAt(i);
0447: return f;
0448: }
0449:
0450: static Format popFormats(java.util.Vector vector, int start, int end) {
0451: Format f;
0452: if (end == start + 1)
0453: f = (Format) vector.elementAt(start);
0454: else
0455: f = new CompoundFormat(getFormats(vector, start, end));
0456: vector.setSize(start);
0457: return f;
0458: }
0459:
0460: public LispFormat(String str) throws ParseException {
0461: this (str.toCharArray());
0462: }
0463:
0464: /*
0465: private void clearSpecs (int speci, int max)
0466: {
0467: int num = specs_length - speci - 1;
0468: for (int i = num; i < max; i++)
0469: addSpec(PARAM_UNSPECIFIED);
0470: specs_length = speci + 1;
0471: }
0472: */
0473:
0474: /*
0475: private void addSpec(Format fmt)
0476: {
0477: if (formats == null)
0478: formats = new Format[4];
0479: else
0480: {
0481: if (this.length == formats.length)
0482: {
0483: Format[] newformats = new Format[2 * this.length];
0484: System.arraycopy(formats, 0, newformats, 0, this.length);
0485: formats = newformats;
0486: }
0487: }
0488: formats[this.length] = fmt;
0489: addSpec(this.length);
0490: this.length++;
0491: }
0492: */
0493:
0494: /*
0495: private void addSpec(int val)
0496: {
0497: //System.err.println("addSpec("+val+") at:"+specs_length);
0498: int old_size = specs.length;
0499: if (specs_length >= old_size)
0500: {
0501: int[] new_specs = new int[2 * old_size];
0502: System.arraycopy(specs, 0, new_specs, 0, old_size);
0503: specs = new_specs;
0504: }
0505: specs[specs_length++] = val;
0506: }
0507: */
0508:
0509: public LispFormat(char[] format) throws ParseException {
0510: this (format, 0, format.length);
0511: }
0512:
0513: public static int getParam(java.util.Vector vec, int index) {
0514: if (index >= vec.size())
0515: return PARAM_UNSPECIFIED;
0516: Object arg = vec.elementAt(index);
0517: if (arg == paramFromList)
0518: return PARAM_FROM_LIST;
0519: if (arg == paramFromCount)
0520: return PARAM_FROM_COUNT;
0521: if (arg == paramUnspecified)
0522: return PARAM_UNSPECIFIED;
0523: return getParam(arg, PARAM_UNSPECIFIED);
0524: }
0525:
0526: /** Convert sequence (or Object[]) to Object[].
0527: * Return null if not a valid Sequence. */
0528: public static Object[] asArray(Object arg) {
0529: if (arg instanceof Object[])
0530: return (Object[]) arg;
0531: if (!(arg instanceof Sequence))
0532: return null;
0533: int count = ((Sequence) arg).size();
0534: Object[] arr = new Object[count];
0535: int i = 0;
0536: while (arg instanceof Pair) {
0537: Pair pair = (Pair) arg;
0538: arr[i++] = pair.car;
0539: arg = pair.cdr;
0540: }
0541: if (i < count) {
0542: if (!(arg instanceof Sequence))
0543: return null;
0544: int npairs = i;
0545: Sequence seq = (Sequence) arg;
0546: for (; i < count; i++)
0547: arr[i] = seq.get(npairs + i);
0548: }
0549: return arr;
0550: }
0551: }
0552:
0553: /** Add plural suffixes ("s" or "y/ies") of English words.
0554: * Used to implement the Common Lisp ~P ('Plural') format operator. */
0555:
0556: class LispPluralFormat extends ReportFormat {
0557: boolean backup;
0558: boolean y;
0559:
0560: public static LispPluralFormat getInstance(boolean backup, boolean y) {
0561: LispPluralFormat fmt = new LispPluralFormat();
0562: fmt.backup = backup;
0563: fmt.y = y;
0564: return fmt;
0565: }
0566:
0567: public int format(Object[] args, int start, Writer dst,
0568: FieldPosition fpos) throws java.io.IOException {
0569: if (backup)
0570: start--;
0571: Object arg = args[start++];
0572: boolean plural = arg != IntNum.one();
0573: if (y)
0574: print(dst, plural ? "ies" : "y");
0575: else if (plural)
0576: dst.write('s');
0577: return start;
0578: }
0579: }
0580:
0581: /** Handle formatting of characters.
0582: * Used to implement the Common List ~C (Character) and ~~ (Tilde)
0583: * format operators. */
0584:
0585: class LispCharacterFormat extends ReportFormat {
0586: boolean seenAt;
0587: boolean seenColon;
0588: int count;
0589: int charVal;
0590:
0591: public static LispCharacterFormat getInstance(int charVal,
0592: int count, boolean seenAt, boolean seenColon) {
0593: LispCharacterFormat fmt = new LispCharacterFormat();
0594: fmt.count = count;
0595: fmt.charVal = charVal;
0596: fmt.seenAt = seenAt;
0597: fmt.seenColon = seenColon;
0598: return fmt;
0599: }
0600:
0601: public int format(Object[] args, int start, Writer dst,
0602: FieldPosition fpos) throws java.io.IOException {
0603: int count = getParam(this .count, 1, args, start);
0604: if (this .count == LispFormat.PARAM_FROM_LIST)
0605: start++;
0606: int charVal = getParam(this .charVal, '?', args, start);
0607: if (this .charVal == LispFormat.PARAM_FROM_LIST)
0608: start++;
0609: while (--count >= 0)
0610: printChar(charVal, seenAt, seenColon, dst);
0611: return start;
0612: }
0613:
0614: public static void printChar(int ch, boolean seenAt,
0615: boolean seenColon, Writer dst) throws java.io.IOException {
0616: if (seenAt) {
0617: print(dst, Char.toScmReadableString(ch));
0618: } else if (seenColon) {
0619: if (ch < ' ') {
0620: dst.write('^');
0621: dst.write(ch + 0x40);
0622: } else if (ch >= 0x7f) {
0623: print(dst, "#\\");
0624: print(dst, Integer.toString(ch, 8));
0625: } else
0626: dst.write(ch);
0627: } else {
0628: // if (ch > 0xFFFF) print surrogate chars; else
0629: dst.write(ch);
0630: }
0631: }
0632: }
0633:
0634: /** Handle formatting of newline ~% and ~_ format operator. */
0635:
0636: class LispNewlineFormat extends ReportFormat {
0637: static final String line_separator = System.getProperty(
0638: "line.separator", "\n");
0639:
0640: /** One of NEWLINE_LITERAL, NEWLINE_LINEAR, NEWLINE_FILL, NEWLINE_MISER
0641: * or NEWLINE_MANDATORY. These are defined in gnu.text.PrettyWriter. */
0642: int kind;
0643:
0644: int count;
0645:
0646: public static LispNewlineFormat getInstance(int count, int kind) {
0647: LispNewlineFormat fmt = new LispNewlineFormat();
0648: fmt.count = count;
0649: fmt.kind = kind;
0650: return fmt;
0651: }
0652:
0653: public int format(Object[] args, int start, Writer dst,
0654: FieldPosition fpos) throws java.io.IOException {
0655: int count = getParam(this .count, 1, args, start);
0656: if (this .count == LispFormat.PARAM_FROM_LIST)
0657: start++;
0658: while (--count >= 0)
0659: printNewline(kind, dst);
0660: return start;
0661: }
0662:
0663: public static void printNewline(int kind, Writer dst)
0664: throws java.io.IOException {
0665: if (dst instanceof OutPort
0666: && kind != PrettyWriter.NEWLINE_LITERAL)
0667: ((OutPort) dst).writeBreak(kind);
0668: else if (dst instanceof java.io.PrintWriter)
0669: // May make a difference if autoflush. // FIXME flush if OutPort?
0670: ((java.io.PrintWriter) dst).println();
0671: else
0672: dst.write(line_separator);
0673: }
0674: }
0675:
0676: /** Handle formatting of ~I (indent) format operator. */
0677:
0678: class LispIndentFormat extends ReportFormat {
0679: boolean current;
0680:
0681: int columns;
0682:
0683: public static LispIndentFormat getInstance(int columns,
0684: boolean current) {
0685: LispIndentFormat fmt = new LispIndentFormat();
0686: fmt.columns = columns;
0687: fmt.current = current;
0688: return fmt;
0689: }
0690:
0691: public int format(Object[] args, int start, Writer dst,
0692: FieldPosition fpos) throws java.io.IOException {
0693: int columns = getParam(this .columns, 0, args, start);
0694: if (this .columns == LispFormat.PARAM_FROM_LIST)
0695: start++;
0696: if (dst instanceof OutPort)
0697: ((OutPort) dst).setIndentation(columns, current);
0698: return start;
0699: }
0700: }
0701:
0702: /** Perform general padding.
0703: * Used to implement the Common Lisp ~A (Ascii) and ~ (S-expression),
0704: * format operators, unless they have no parameters. */
0705:
0706: class LispObjectFormat extends ReportFormat {
0707: int minWidth;
0708: int colInc;
0709: int minPad;
0710: int padChar;
0711: int where;
0712: ReportFormat base;
0713:
0714: public LispObjectFormat(ReportFormat base, int minWidth,
0715: int colInc, int minPad, int padChar, int where) {
0716: this .base = base;
0717: this .minWidth = minWidth;
0718: this .colInc = colInc;
0719: this .minPad = minPad;
0720: this .padChar = padChar;
0721: this .where = where;
0722: }
0723:
0724: public int format(Object[] args, int start, Writer dst,
0725: FieldPosition fpos) throws java.io.IOException {
0726: int minWidth = getParam(this .minWidth, 0, args, start);
0727: if (this .minWidth == LispFormat.PARAM_FROM_LIST)
0728: start++;
0729: int colInc = getParam(this .colInc, 1, args, start);
0730: if (this .colInc == LispFormat.PARAM_FROM_LIST)
0731: start++;
0732: int minPad = getParam(this .minPad, 0, args, start);
0733: if (this .minPad == LispFormat.PARAM_FROM_LIST)
0734: start++;
0735: char padChar = getParam(this .padChar, ' ', args, start);
0736: if (this .padChar == LispFormat.PARAM_FROM_LIST)
0737: start++;
0738: return gnu.text.PadFormat.format(base, args, start, dst,
0739: padChar, minWidth, colInc, minPad, where, fpos);
0740: }
0741: }
0742:
0743: class LispEscapeFormat extends ReportFormat {
0744: int param1;
0745: int param2;
0746: int param3;
0747: boolean escapeAll;
0748:
0749: public final static LispEscapeFormat alwaysTerminate = new LispEscapeFormat(
0750: 0, LispFormat.PARAM_UNSPECIFIED);
0751:
0752: public LispEscapeFormat(int param1, int param2) {
0753: this .param1 = param1;
0754: this .param2 = param2;
0755: this .param3 = LispFormat.PARAM_UNSPECIFIED;
0756: }
0757:
0758: public LispEscapeFormat(int param1, int param2, int param3) {
0759: this .param1 = param1;
0760: this .param2 = param2;
0761: this .param3 = param3;
0762: }
0763:
0764: static Numeric getParam(int param, Object[] args, int start) {
0765: if (param == LispFormat.PARAM_FROM_COUNT)
0766: return IntNum.make(args.length - start);
0767: if (param == LispFormat.PARAM_FROM_LIST) {
0768: Object arg = args[start];
0769: if (arg instanceof Numeric)
0770: return (Numeric) arg;
0771: if (arg instanceof Number) {
0772: if (arg instanceof Float || arg instanceof Double)
0773: return new DFloNum(((Number) arg).doubleValue());
0774: return IntNum.make(((Number) arg).longValue());
0775: }
0776: if (arg instanceof Char)
0777: return new IntNum(((Char) arg).intValue());
0778: if (arg instanceof Character)
0779: return new IntNum((int) ((Character) arg).charValue());
0780: return new DFloNum(Double.NaN);
0781: }
0782: return IntNum.make(param);
0783: }
0784:
0785: /** WRONG: Tests if we should exit the the surrounding format.
0786: * Returns 2*ARGS_USED+(DO_TERMINATE?1:0), where ARGS_USED is the
0787: * number of arguments consumed by the specification, and
0788: * DO_TERMINATE is true if we actually should exit.
0789: */
0790: public int format(Object[] args, int start, Writer dst,
0791: FieldPosition fpos) throws java.io.IOException {
0792: int orig_start = start;
0793: boolean do_terminate;
0794: if (param1 == LispFormat.PARAM_UNSPECIFIED)
0795: do_terminate = start == args.length;
0796: else if (param2 == LispFormat.PARAM_UNSPECIFIED && param1 == 0)
0797: do_terminate = true; // Efficiency hack
0798: else {
0799: Numeric arg1 = getParam(param1, args, start);
0800: if (param1 == LispFormat.PARAM_FROM_LIST)
0801: start++;
0802: if (param2 == LispFormat.PARAM_UNSPECIFIED) {
0803: do_terminate = arg1.isZero();
0804: } else {
0805: Numeric arg2 = getParam(param2, args, start);
0806: if (param2 == LispFormat.PARAM_FROM_LIST)
0807: start++;
0808: if (param3 == LispFormat.PARAM_UNSPECIFIED) {
0809: do_terminate = arg1.equals(arg2);
0810: } else {
0811: Numeric arg3 = getParam(param3, args, start);
0812: if (param3 == LispFormat.PARAM_FROM_LIST)
0813: start++;
0814: do_terminate = arg2.geq(arg1) && arg3.geq(arg2);
0815: }
0816: }
0817: }
0818: return result(!do_terminate ? 0 : escapeAll ? ESCAPE_ALL
0819: : ESCAPE_NORMAL, start);
0820: }
0821:
0822: public final static int ESCAPE_NORMAL = 0xF1;
0823: public final static int ESCAPE_ALL = 0xF2;
0824: }
0825:
0826: /** Handle <code>~<...~></code> - pretty-printing logical block.
0827: * (Justification is not implemented.) */
0828:
0829: class LispPrettyFormat extends ReportFormat {
0830: Format[] segments;
0831: Format body;
0832: String prefix;
0833: String suffix;
0834: boolean perLine;
0835: boolean seenAt;
0836:
0837: public int format(Object[] args, int start, Writer dst,
0838: FieldPosition fpos) throws java.io.IOException {
0839: String pre = prefix;
0840: String suf = suffix;
0841: OutPort out = dst instanceof OutPort ? (OutPort) dst : null;
0842: try {
0843: if (seenAt) {
0844: if (out != null)
0845: out.startLogicalBlock(pre, perLine, suffix);
0846: start = ReportFormat.format(body, args, start, dst,
0847: fpos);
0848: } else {
0849: Object curArg = args[start];
0850: Object[] curArr = LispFormat.asArray(curArg);
0851: if (curArr == null)
0852: pre = suf = "";
0853: if (out != null)
0854: out.startLogicalBlock(pre, perLine, suffix);
0855: if (curArr == null)
0856: ObjectFormat.format(curArg, dst, -1, true);
0857: else
0858: ReportFormat.format(body, curArr, 0, dst, fpos);
0859: start++;
0860: }
0861: } finally {
0862: if (out != null)
0863: out.endLogicalBlock(suf);
0864: }
0865: return start;
0866: }
0867:
0868: public String toString() {
0869: StringBuffer sbuf = new StringBuffer();
0870: sbuf.append("LispPrettyFormat[");
0871: sbuf.append("prefix: \"");
0872: sbuf.append(prefix);
0873: sbuf.append("\", suffix: \"");
0874: sbuf.append(suffix);
0875: sbuf.append("\", body: ");
0876: sbuf.append(body);
0877: sbuf.append("]");
0878: return sbuf.toString();
0879: }
0880: }
0881:
0882: class LispIterationFormat extends ReportFormat {
0883: int maxIterations;
0884: boolean seenAt;
0885: boolean seenColon;
0886: boolean atLeastOnce;
0887:
0888: Format body;
0889:
0890: public static int format(Format body, int maxIterations,
0891: Object[] args, int start, Writer dst, boolean seenColon,
0892: boolean atLeastOnce) throws java.io.IOException {
0893: for (int i = 0;; i++) {
0894: if (i == maxIterations && maxIterations != -1)
0895: break;
0896: if (start == args.length && (i > 0 || !atLeastOnce))
0897: break;
0898: if (seenColon) {
0899: Object curArg = args[start];
0900: Object[] curArr = LispFormat.asArray(curArg);
0901: if (curArr == null) { // ?
0902: }
0903: int result = ReportFormat.format(body, curArr, 0, dst,
0904: null);
0905: start++;
0906: if (ReportFormat.resultCode(result) == LispEscapeFormat.ESCAPE_ALL)
0907: break;
0908: } else {
0909: start = ReportFormat.format(body, args, start, dst,
0910: null);
0911: if (start < 0) {
0912: start = ReportFormat.nextArg(start);
0913: break;
0914: }
0915: }
0916: }
0917: return start;
0918: }
0919:
0920: public int format(Object[] args, int start, Writer dst,
0921: FieldPosition fpos) throws java.io.IOException {
0922: int maxIterations = getParam(this .maxIterations, -1, args,
0923: start);
0924: if (this .maxIterations == LispFormat.PARAM_FROM_LIST)
0925: start++;
0926:
0927: Format body = this .body;
0928: if (body == null) {
0929: // from args
0930: Object arg = args[start++];
0931: if (arg instanceof java.text.Format)
0932: body = (java.text.Format) arg;
0933: else {
0934: try {
0935: body = new LispFormat(arg.toString());
0936: } catch (Exception ex) {
0937: print(dst, "<invalid argument for \"~{~}\" format>");
0938: return args.length; // FIXME
0939: }
0940: }
0941: }
0942: if (seenAt) {
0943: return format(body, maxIterations, args, start, dst,
0944: seenColon, atLeastOnce);
0945: } else {
0946: Object arg = args[start];
0947: Object[] curArgs = LispFormat.asArray(arg);
0948: if (curArgs == null)
0949: dst.write("{" + arg + "}".toString());
0950: else
0951: format(body, maxIterations, curArgs, 0, dst, seenColon,
0952: atLeastOnce);
0953: return start + 1;
0954: }
0955: }
0956:
0957: public String toString() {
0958: StringBuffer sbuf = new StringBuffer();
0959: sbuf.append("LispIterationFormat[");
0960: sbuf.append(body);
0961: sbuf.append("]");
0962: return sbuf.toString();
0963: }
0964: }
0965:
0966: class LispChoiceFormat extends ReportFormat {
0967: int param;
0968: boolean lastIsDefault;
0969: boolean testBoolean; // choice[0] is selected if arg is false.
0970: boolean skipIfFalse;
0971: Format[] choices;
0972:
0973: public int format(Object[] args, int start, Writer dst,
0974: FieldPosition fpos) throws java.io.IOException {
0975: Format fmt;
0976: if (testBoolean) // Handles ~:[false~;true~]
0977: {
0978: fmt = choices[args[start] == Boolean.FALSE ? 0 : 1];
0979: start++;
0980: } else if (!skipIfFalse) {
0981: int index = getParam(this .param,
0982: LispFormat.PARAM_FROM_LIST, args, start);
0983: if (param == LispFormat.PARAM_FROM_LIST)
0984: start++;
0985: if (index < 0 || index >= choices.length) {
0986: if (lastIsDefault)
0987: index = choices.length - 1;
0988: else
0989: return start;
0990: }
0991: fmt = choices[index];
0992: } else {
0993: if (args[start] == Boolean.FALSE)
0994: return start + 1;
0995: fmt = choices[0];
0996: }
0997: return ReportFormat.format(fmt, args, start, dst, fpos);
0998: }
0999: }
1000:
1001: class LispRepositionFormat extends ReportFormat {
1002: boolean backwards;
1003: boolean absolute;
1004: int count;
1005:
1006: public LispRepositionFormat(int count, boolean backwards,
1007: boolean absolute) {
1008: this .count = count;
1009: this .backwards = backwards;
1010: this .absolute = absolute;
1011: }
1012:
1013: public int format(Object[] args, int start, Writer dst,
1014: FieldPosition fpos) throws java.io.IOException {
1015: int count = getParam(this .count, absolute ? 0 : 1, args, start);
1016: if (!absolute) {
1017: if (backwards)
1018: count = -count;
1019: count += start;
1020: }
1021: return count < 0 ? 0 : count > args.length ? args.length
1022: : count;
1023: }
1024: }
1025:
1026: class LispFreshlineFormat extends ReportFormat {
1027: int count;
1028:
1029: public LispFreshlineFormat(int count) {
1030: this .count = count;
1031: }
1032:
1033: public int format(Object[] args, int start, Writer dst,
1034: FieldPosition fpos) throws java.io.IOException {
1035: int count = getParam(this .count, 1, args, start);
1036: if (this .count == LispFormat.PARAM_FROM_LIST)
1037: start++;
1038: if (count > 0) {
1039: if (dst instanceof OutPort) {
1040: ((OutPort) dst).freshLine();
1041: count--;
1042: }
1043: while (--count >= 0)
1044: dst.write('\n');
1045: }
1046: return start;
1047: }
1048: }
1049:
1050: class LispTabulateFormat extends ReportFormat {
1051: boolean relative;
1052: int colnum;
1053: int colinc;
1054: int padChar;
1055:
1056: public LispTabulateFormat(int colnum, int colinc, int padChar,
1057: boolean relative) {
1058: this .colnum = colnum;
1059: this .colinc = colinc;
1060: this .relative = relative;
1061: this .padChar = padChar;
1062: }
1063:
1064: public int format(Object[] args, int start, Writer dst,
1065: FieldPosition fpos) throws java.io.IOException {
1066: int colnum = getParam(this .colnum, 1, args, start);
1067: if (this .colnum == LispFormat.PARAM_FROM_LIST)
1068: start++;
1069: int colinc = getParam(this .colinc, 1, args, start);
1070: if (this .colinc == LispFormat.PARAM_FROM_LIST)
1071: start++;
1072: // Extension from SLIB:
1073: char padChar = getParam(this .padChar, ' ', args, start);
1074: if (this .padChar == LispFormat.PARAM_FROM_LIST)
1075: start++;
1076: int column = -1;
1077: if (dst instanceof OutPort)
1078: column = ((OutPort) dst).getColumnNumber();
1079: int spaces;
1080: if (column >= 0) {
1081: if (!relative) {
1082: if (column < colnum)
1083: spaces = colnum - column;
1084: else if (colinc <= 0)
1085: spaces = 0;
1086: else
1087: spaces = colinc - (column - colnum) % colinc;
1088: } else {
1089: spaces = colnum + colinc - (column + colnum) % colinc;
1090: }
1091: } else {
1092: spaces = relative ? colnum : 2;
1093: }
1094: while (--spaces >= 0)
1095: dst.write(padChar);
1096: return start;
1097: }
1098: }
1099:
1100: /* Support for ~F, ~$, ~E, ~G. */
1101:
1102: class LispRealFormat extends ReportFormat {
1103: char op;
1104: int arg1;
1105: int arg2;
1106: int arg3;
1107: int arg4;
1108: int arg5;
1109: int arg6;
1110: int arg7;
1111: boolean showPlus;
1112: boolean internalPad;
1113: /** Twice the number of args consumed; odd if any arg is PARAM_FROM_COUNT. */
1114: int argsUsed;
1115:
1116: LispRealFormat() {
1117: argsUsed = (arg1 == LispFormat.PARAM_FROM_COUNT
1118: || arg2 == LispFormat.PARAM_FROM_COUNT
1119: || arg3 == LispFormat.PARAM_FROM_COUNT
1120: || arg4 == LispFormat.PARAM_FROM_COUNT
1121: || arg5 == LispFormat.PARAM_FROM_COUNT
1122: || arg6 == LispFormat.PARAM_FROM_COUNT || arg7 == LispFormat.PARAM_FROM_COUNT) ? 1
1123: : 0;
1124: if (arg1 == LispFormat.PARAM_FROM_LIST)
1125: argsUsed += 2;
1126: if (arg2 == LispFormat.PARAM_FROM_LIST)
1127: argsUsed += 2;
1128: if (arg3 == LispFormat.PARAM_FROM_LIST)
1129: argsUsed += 2;
1130: if (arg4 == LispFormat.PARAM_FROM_LIST)
1131: argsUsed += 2;
1132: if (arg5 == LispFormat.PARAM_FROM_LIST)
1133: argsUsed += 2;
1134: if (arg6 == LispFormat.PARAM_FROM_LIST)
1135: argsUsed += 2;
1136: if (arg7 == LispFormat.PARAM_FROM_LIST)
1137: argsUsed += 2;
1138: }
1139:
1140: public Format resolve(Object[] args, int start) {
1141: if (op == '$') {
1142: FixedRealFormat mfmt = new FixedRealFormat();
1143: int decimals = getParam(this .arg1, 2, args, start);
1144: if (this .arg1 == LispFormat.PARAM_FROM_LIST)
1145: start++;
1146: int digits = getParam(this .arg2, 1, args, start);
1147: if (this .arg2 == LispFormat.PARAM_FROM_LIST)
1148: start++;
1149: int width = getParam(this .arg3, 0, args, start);
1150: if (this .arg3 == LispFormat.PARAM_FROM_LIST)
1151: start++;
1152: char padChar = getParam(this .arg4, ' ', args, start);
1153: if (this .arg4 == LispFormat.PARAM_FROM_LIST)
1154: start++;
1155:
1156: mfmt.setMaximumFractionDigits(decimals);
1157: mfmt.setMinimumIntegerDigits(digits);
1158: mfmt.width = width;
1159: mfmt.padChar = padChar;
1160: mfmt.internalPad = internalPad;
1161: mfmt.showPlus = showPlus;
1162: return mfmt;
1163: } else if (op == 'F') {
1164: FixedRealFormat mfmt = new FixedRealFormat();
1165: int width = getParam(this .arg1, 0, args, start);
1166: if (this .arg1 == LispFormat.PARAM_FROM_LIST)
1167: start++;
1168: int decimals = getParam(this .arg2, -1, args, start);
1169: if (this .arg2 == LispFormat.PARAM_FROM_LIST)
1170: start++;
1171: int scale = getParam(this .arg3, 0, args, start);
1172: if (this .arg3 == LispFormat.PARAM_FROM_LIST)
1173: start++;
1174: mfmt.overflowChar = getParam(this .arg4, '\0', args, start);
1175: if (this .arg4 == LispFormat.PARAM_FROM_LIST)
1176: start++;
1177: char padChar = getParam(this .arg5, ' ', args, start);
1178: if (this .arg5 == LispFormat.PARAM_FROM_LIST)
1179: start++;
1180: mfmt.setMaximumFractionDigits(decimals);
1181: mfmt.setMinimumIntegerDigits(0);
1182: mfmt.width = width;
1183: mfmt.scale = scale;
1184: mfmt.padChar = padChar;
1185: mfmt.internalPad = internalPad;
1186: mfmt.showPlus = showPlus;
1187: return mfmt;
1188: } else // if (op == 'E' || op == 'G')
1189: {
1190: ExponentialFormat efmt = new ExponentialFormat();
1191: efmt.exponentShowSign = true;
1192: efmt.width = getParam(this .arg1, 0, args, start);
1193: if (this .arg1 == LispFormat.PARAM_FROM_LIST)
1194: start++;
1195: efmt.fracDigits = getParam(this .arg2, -1, args, start);
1196: if (this .arg2 == LispFormat.PARAM_FROM_LIST)
1197: start++;
1198: efmt.expDigits = getParam(this .arg3, 0, args, start);
1199: if (this .arg3 == LispFormat.PARAM_FROM_LIST)
1200: start++;
1201: efmt.intDigits = getParam(this .arg4, 1, args, start);
1202: if (this .arg4 == LispFormat.PARAM_FROM_LIST)
1203: start++;
1204: efmt.overflowChar = getParam(this .arg5, '\0', args, start);
1205: if (this .arg5 == LispFormat.PARAM_FROM_LIST)
1206: start++;
1207: efmt.padChar = getParam(this .arg6, ' ', args, start);
1208: if (this .arg6 == LispFormat.PARAM_FROM_LIST)
1209: start++;
1210: efmt.exponentChar = getParam(this .arg7, 'E', args, start);
1211: if (this .arg7 == LispFormat.PARAM_FROM_LIST)
1212: start++;
1213: efmt.general = op == 'G';
1214: efmt.showPlus = showPlus;
1215: return efmt;
1216: }
1217: }
1218:
1219: public int format(Object[] args, int start, Writer dst,
1220: FieldPosition fpos) throws java.io.IOException {
1221: StringBuffer sbuf = new StringBuffer(100);
1222: Format fmt = resolve(args, start);
1223: start += argsUsed >> 1;
1224: RealNum value = (RealNum) args[start++];
1225: fmt.format(value, sbuf, fpos);
1226: dst.write(sbuf.toString());
1227: return start;
1228: }
1229: }
|