0001: // Copyright (c) 2001 Per M.A. Bothner.
0002: // This is free software; for terms and warranty disclaimer see ./COPYING.
0003:
0004: package gnu.text;
0005:
0006: import java.io.*;
0007:
0008: /** A pretty printer.
0009: *
0010: * This code is transcribed from pprint.lisp in Steel Bank Common Lisp,
0011: * which is again based on the code in CMU Common Lisp.
0012: */
0013:
0014: public class PrettyWriter extends java.io.Writer {
0015: protected Writer out;
0016:
0017: public PrettyWriter(java.io.Writer base) {
0018: this .out = out;
0019: isPrettyPrinting = true;
0020: }
0021:
0022: public PrettyWriter(java.io.Writer out, int lineLength) {
0023: this .out = out;
0024: this .lineLength = lineLength;
0025: isPrettyPrinting = lineLength > 1;
0026: }
0027:
0028: public PrettyWriter(java.io.Writer out, boolean isPrettyPrinting) {
0029: this .out = out;
0030: this .isPrettyPrinting = isPrettyPrinting;
0031: }
0032:
0033: /** Line length we should format to. */
0034: int lineLength = 80;
0035:
0036: public boolean isPrettyPrinting;
0037:
0038: public static int initialBufferSize = 126;
0039:
0040: /** Holds all the text that has been output but not yet printed. */
0041: public/* FIXME */char[] buffer = new char[initialBufferSize];
0042:
0043: /** The index into BUFFER where more text should be put. */
0044: public/* FIXME */int bufferFillPointer;
0045:
0046: /** Total amount of stuff that has been shifted out of the buffer.
0047: * Whenever we output stuff from the buffer, we shift the remaining noise
0048: * over. This makes it difficult to keep references to locations in
0049: * the buffer. */
0050: int bufferOffset;
0051:
0052: /** The column the first character in the buffer will appear in.
0053: * Normally zero, but if we end up with a very long line with no breaks in it
0054: * we might have to output part of it. Then this will no longer be zero. */
0055: int bufferStartColumn;
0056:
0057: /** The line number we are currently on. Used for *print-lines* abrevs and
0058: * to tell when sections have been split across multiple lines. */
0059: int lineNumber;
0060:
0061: // There are three different units for measuring character positions:
0062: // COLUMN - offset (in characters) from the start of the current line.
0063: // INDEX - index into the output buffer.
0064: // POSN - some position in the stream of characters cycling through
0065: // the output buffer.
0066:
0067: private int indexPosn(int index) {
0068: return index + bufferOffset;
0069: }
0070:
0071: private int posnIndex(int posn) {
0072: return posn - bufferOffset;
0073: }
0074:
0075: private int posnColumn(int posn) {
0076: return indexColumn(posnIndex(posn));
0077: }
0078:
0079: /** Stack of logical blocks in effect at the buffer start.
0080: * I.e. blocks for which reallyStartLogicalBlock has been called. */
0081: int[] blocks = new int[10 * LOGICAL_BLOCK_LENGTH];
0082: static final private int LOGICAL_BLOCK_LENGTH = 6;
0083: static final private int BLOCK_START_COLUMN = -1;
0084: static final private int BLOCK_SECTION_COLUMN = -2;
0085: static final private int BLOCK_PER_LINE_PREFIX_END = -3;
0086: static final private int BLOCK_PREFIX_LENGTH = -4;
0087: static final private int BLOCK_SUFFIX_LENGTH = -5;
0088: static final private int BLOCK_SECTION_START_LINE = -6;
0089: int blockDepth = LOGICAL_BLOCK_LENGTH;
0090:
0091: /** Buffer holding the per-line prefix active at the buffer start.
0092: * Indentation is included in this. The length of this is stored
0093: * in the logical block stack. */
0094: char[] prefix = new char[initialBufferSize];
0095:
0096: /** Buffer holding the total remaining suffix active at the buffer start.
0097: * The characters are right-justified in the buffer to make it easier
0098: * to output the buffer. The length is stored in the logical block stack. */
0099: char[] suffix = new char[initialBufferSize];
0100:
0101: static final int QUEUE_INIT_ALLOC_SIZE = 300; // FIXME
0102: int[] queueInts = new int[QUEUE_INIT_ALLOC_SIZE];
0103: /** For simplicity, queueStrings is the same size as queueInts. */
0104: String[] queueStrings = new String[QUEUE_INIT_ALLOC_SIZE];
0105: int queueTail;
0106: /** Number of elements (in queueInts and queueStrings) in use. */
0107: int queueSize;
0108: /** Index (into queueInts) of current unclosed begin-block node. */
0109: int currentBlock;
0110: public int pendingBlocksCount;
0111:
0112: static final int QUEUED_OP_TYPE = 0;
0113:
0114: private int getQueueType(int index) {
0115: return queueInts[index] & 0xFF;
0116: }
0117:
0118: private int getQueueSize(int index) {
0119: return queueInts[index] >> 16;
0120: }
0121:
0122: /** Relative offset of POSN field of a QUEUED_OP> */
0123: static final int QUEUED_OP_POSN = 1;
0124: /** Size of "base part" of a QUEUED_OP. */
0125: static final int QUEUED_OP_SIZE = 2;
0126:
0127: /** A dummy queue item used at the high end of the queue buffer
0128: * when there isn't enough space for the needed queue item. */
0129: static final int QUEUED_OP_DUMMY_TYPE = 0;
0130:
0131: /** "Abstract" type for beginning of section.
0132: * A section is from a block-start to a newline, from a newline to
0133: * the next newline (in the same block?), or from a newline to
0134: * the block end (?). */
0135: /*static final int QUEUED_OP_SECTION_START_TYPE = 1;*/
0136: static final int QUEUED_OP_SECTION_START_SIZE = QUEUED_OP_SIZE + 2;
0137: static final int QUEUED_OP_SECTION_START_DEPTH = QUEUED_OP_SIZE;
0138: static final int QUEUED_OP_SECTION_START_SECTION_END = QUEUED_OP_SIZE + 1;
0139:
0140: /** A newline queue item. */
0141: static final int QUEUED_OP_NEWLINE_TYPE = 2;
0142: static final int QUEUED_OP_NEWLINE_SIZE = QUEUED_OP_SECTION_START_SIZE + 1;
0143: static final int QUEUED_OP_NEWLINE_KIND = QUEUED_OP_SECTION_START_SIZE;
0144: public static final int NEWLINE_LINEAR = 'N';
0145: public static final int NEWLINE_LITERAL = 'L';
0146: public static final int NEWLINE_FILL = 'F';
0147: public static final int NEWLINE_MISER = 'M';
0148: public static final int NEWLINE_MANDATORY = 'R'; // "required"
0149:
0150: static final int QUEUED_OP_INDENTATION_TYPE = 3;
0151: static final int QUEUED_OP_INDENTATION_SIZE = QUEUED_OP_SIZE + 2;
0152: static final int QUEUED_OP_INDENTATION_KIND = QUEUED_OP_SIZE;
0153: static final int QUEUED_OP_INDENTATION_BLOCK = 'B';
0154: static final int QUEUED_OP_INDENTATION_CURRENT = 'C';
0155: static final int QUEUED_OP_INDENTATION_AMOUNT = QUEUED_OP_SIZE + 1;
0156:
0157: /** A "block-start" queue item. */
0158: static final int QUEUED_OP_BLOCK_START_TYPE = 4;
0159: static final int QUEUED_OP_BLOCK_START_SIZE = QUEUED_OP_SECTION_START_SIZE + 1;
0160: /** If the QUEUED_OP_SECTION_START_SECTION_END < 0, it points to
0161: * the previous (outer) un-closed block-start.
0162: * If QUEUED_OP_SECTION_START_SECTION_END > 0, it points to the
0163: * corresponding block-end node.
0164: * In both cases the pointers are relative to the current BLOCK_START. */
0165: static final int QUEUED_OP_BLOCK_START_BLOCK_END = QUEUED_OP_SECTION_START_SIZE;
0166: static final int QUEUED_OP_BLOCK_PREFIX = 0;
0167: static final int QUEUED_OP_BLOCK_START_SUFFIX = 1;
0168:
0169: static final int QUEUED_OP_BLOCK_END_TYPE = 5;
0170: static final int QUEUED_OP_BLOCK_END_SIZE = QUEUED_OP_SIZE;
0171:
0172: static final int QUEUED_OP_TAB_TYPE = 6;
0173: static final int QUEUED_OP_TAB_SIZE = QUEUED_OP_SIZE + 3;
0174: static final int QUEUED_OP_TAB_FLAGS = QUEUED_OP_SIZE;
0175: static final int QUEUED_OP_TAB_IS_SECTION = 1;
0176: static final int QUEUED_OP_TAB_IS_RELATIVE = 2;
0177: static final int QUEUED_OP_TAB_COLNUM = QUEUED_OP_SIZE + 1;
0178: static final int QUEUED_OP_TAB_COLINC = QUEUED_OP_SIZE + 2;
0179:
0180: private int getSectionColumn() {
0181: return blocks[blockDepth + BLOCK_SECTION_COLUMN];
0182: }
0183:
0184: private int getStartColumn() {
0185: return blocks[blockDepth + BLOCK_START_COLUMN];
0186: }
0187:
0188: private int getPerLinePrefixEnd() {
0189: return blocks[blockDepth + BLOCK_PER_LINE_PREFIX_END];
0190: }
0191:
0192: private int getPrefixLength() {
0193: return blocks[blockDepth + BLOCK_PREFIX_LENGTH];
0194: }
0195:
0196: private int getSuffixLength() {
0197: return blocks[blockDepth + BLOCK_SUFFIX_LENGTH];
0198: }
0199:
0200: private int getSectionStartLine() {
0201: return blocks[blockDepth + BLOCK_SECTION_START_LINE];
0202: }
0203:
0204: public void write(int ch) {
0205: //System.err.print("{WRITE-ch: "+((char)ch)+"}");
0206: if (ch == '\n' && isPrettyPrinting)
0207: enqueueNewline(NEWLINE_LITERAL);
0208: else {
0209: ensureSpaceInBuffer(1);
0210: int fillPointer = bufferFillPointer;
0211: buffer[fillPointer] = (char) ch;
0212: bufferFillPointer = 1 + fillPointer;
0213: }
0214: }
0215:
0216: public void write(String str) {
0217: write(str, 0, str.length());
0218: }
0219:
0220: public void write(String str, int start, int count) {
0221: //System.err.print("{WRITE-str: "+str.substring(start, start+count)+"}");
0222: while (count > 0) {
0223: int cnt = count;
0224: // May allocate for space than we need (if the buffer gets fluhed). FIXME
0225: int available = ensureSpaceInBuffer(count);
0226: if (cnt > available)
0227: cnt = available;
0228: int fillPointer = bufferFillPointer;
0229: count -= cnt;
0230: while (--cnt >= 0) {
0231: char ch = str.charAt(start++);
0232: if (ch == '\n' && isPrettyPrinting) {
0233: bufferFillPointer = fillPointer;
0234: enqueueNewline(NEWLINE_LITERAL);
0235: fillPointer = bufferFillPointer;
0236: } else
0237: buffer[fillPointer++] = (char) ch;
0238: }
0239: bufferFillPointer = fillPointer;
0240: }
0241: }
0242:
0243: public void write(char[] str) {
0244: write(str, 0, str.length);
0245: }
0246:
0247: public void write(char[] str, int start, int count) {
0248: //System.err.print("{WRITE: "+new String(str, start, count)+"}");
0249: int end = start + count;
0250: retry: while (count > 0) {
0251: // Look for newline. Should be merged with following loop. FIXME.
0252: for (int i = start; i < end; i++) {
0253: if (str[i] == '\n' && isPrettyPrinting) {
0254: write(str, start, i - start); // Recurse
0255: enqueueNewline(NEWLINE_LITERAL);
0256: start = i + 1;
0257: count = end - start;
0258: continue retry;
0259: }
0260: }
0261:
0262: for (;;) {
0263: int available = ensureSpaceInBuffer(count);
0264: int cnt = available < count ? available : count;
0265: int fillPointer = bufferFillPointer;
0266: int newFillPtr = fillPointer + cnt;
0267: for (int i = fillPointer; i < newFillPtr; i++)
0268: buffer[i] = str[start++];
0269: bufferFillPointer = newFillPtr;
0270: count -= cnt;
0271: if (count == 0)
0272: break;
0273: }
0274: }
0275: }
0276:
0277: private void pushLogicalBlock(int column, int perLineEnd,
0278: int prefixLength, int suffixLength, int sectionStartLine) {
0279: int newLength = blockDepth + LOGICAL_BLOCK_LENGTH;
0280: if (newLength >= blocks.length) {
0281: int[] newBlocks = new int[2 * blocks.length];
0282: System.arraycopy(blocks, 0, newBlocks, 0, blockDepth);
0283: blocks = newBlocks;
0284: }
0285: blockDepth = newLength;
0286: blocks[blockDepth + BLOCK_START_COLUMN] = column;
0287: blocks[blockDepth + BLOCK_SECTION_COLUMN] = column;
0288: blocks[blockDepth + BLOCK_PER_LINE_PREFIX_END] = perLineEnd;
0289: blocks[blockDepth + BLOCK_PREFIX_LENGTH] = prefixLength;
0290: blocks[blockDepth + BLOCK_SUFFIX_LENGTH] = suffixLength;
0291: blocks[blockDepth + BLOCK_SECTION_START_LINE] = sectionStartLine;
0292: }
0293:
0294: void reallyStartLogicalBlock(int column, String prefix,
0295: String suffix) {
0296: int perLineEnd = getPerLinePrefixEnd();
0297: int prefixLength = getPrefixLength();
0298: int suffixLength = getSuffixLength();
0299: pushLogicalBlock(column, perLineEnd, prefixLength,
0300: suffixLength, lineNumber);
0301: setIndentation(column);
0302: if (prefix != null) {
0303: blocks[blockDepth + BLOCK_PER_LINE_PREFIX_END] = column;
0304: int plen = prefix.length();
0305: prefix.getChars(0, plen, this .suffix, column - plen);
0306: }
0307: if (suffix != null) {
0308: // Prepend the new suffix in front of the old suffix in this.suffix.
0309: // The suffix is stored at the "right" (high-index) end of
0310: // this.suffix to make it easier to prepend new suffixes.
0311: char[] totalSuffix = this .suffix;
0312: int totalSuffixLen = totalSuffix.length;
0313: int additional = suffix.length();
0314: int newSuffixLen = suffixLength + additional;
0315: if (newSuffixLen > totalSuffixLen) {
0316: int newTotalSuffixLen = enoughSpace(totalSuffixLen,
0317: additional);
0318: this .suffix = new char[newTotalSuffixLen];
0319: System.arraycopy(totalSuffix, totalSuffixLen
0320: - suffixLength, this .suffix, newTotalSuffixLen
0321: - suffixLength, suffixLength);
0322: totalSuffixLen = newTotalSuffixLen;
0323: }
0324: suffix.getChars(0, additional, totalSuffix, totalSuffixLen
0325: - newSuffixLen);
0326: blocks[blockDepth + BLOCK_SUFFIX_LENGTH] = newSuffixLen;
0327: }
0328:
0329: }
0330:
0331: int enqueueTab(int flags, int colnum, int colinc) // DONE
0332: {
0333: int addr = enqueue(QUEUED_OP_TAB_TYPE, QUEUED_OP_TAB_SIZE);
0334: queueInts[addr + QUEUED_OP_TAB_FLAGS] = flags;
0335: queueInts[addr + QUEUED_OP_TAB_COLNUM] = colnum;
0336: queueInts[addr + QUEUED_OP_TAB_COLINC] = colinc;
0337: return addr;
0338: }
0339:
0340: /** Calculate howmuch space to allocate for a buffer.
0341: * @param current the current size of the buffer
0342: * @param want how much more space is needed
0343: */
0344: private static int enoughSpace(int current, int want) {
0345: int doubled = 2 * current;
0346: int enough = current + ((5 * want) >> 2);
0347: return doubled > enough ? doubled : enough;
0348: }
0349:
0350: public void setIndentation(int column) {
0351: char[] prefix = this .prefix;
0352: int prefixLen = prefix.length;
0353: int current = getPrefixLength();
0354: int minimum = getPerLinePrefixEnd();
0355: if (minimum > column)
0356: column = minimum;
0357: if (column > prefixLen) {
0358: prefix = new char[enoughSpace(prefixLen, column - prefixLen)];
0359: System.arraycopy(this .prefix, 0, prefix, 0, current);
0360: this .prefix = prefix;
0361: }
0362: if (column > current) {
0363: for (int i = current; i < column; i++)
0364: prefix[i] = ' ';
0365: }
0366: blocks[blockDepth + BLOCK_PREFIX_LENGTH] = column;
0367: }
0368:
0369: void reallyEndLogicalBlock() {
0370: int oldIndent = getPrefixLength();
0371: blockDepth -= LOGICAL_BLOCK_LENGTH; // Pop
0372: int newIndent = getPrefixLength();
0373: if (newIndent > oldIndent) {
0374: for (int i = oldIndent; i < newIndent; i++)
0375: prefix[i] = ' ';
0376: }
0377: }
0378:
0379: public int enqueue(int kind, int size) {
0380: int oldLength = queueInts.length;
0381: int endAvail = oldLength - queueTail - queueSize;
0382: if (endAvail > 0 && size > endAvail)
0383: enqueue(QUEUED_OP_DUMMY_TYPE, endAvail);
0384: if (queueSize + size > oldLength) {
0385: int newLength = enoughSpace(oldLength, size);
0386: int[] newInts = new int[newLength];
0387: String[] newStrings = new String[newLength];
0388: int queueHead = queueTail + queueSize - oldLength;
0389: if (queueHead > 0) {
0390: System.arraycopy(queueInts, 0, newInts, 0, queueHead);
0391: System.arraycopy(queueStrings, 0, newStrings, 0,
0392: queueHead);
0393: }
0394: int part1Len = oldLength - queueTail;
0395: int deltaLength = newLength - oldLength;
0396: System.arraycopy(queueInts, queueTail, newInts, queueTail
0397: + deltaLength, part1Len);
0398: System.arraycopy(queueStrings, queueTail, newStrings,
0399: queueTail + deltaLength, part1Len);
0400: queueInts = newInts;
0401: queueStrings = newStrings;
0402: if (currentBlock >= queueTail)
0403: currentBlock += deltaLength;
0404: queueTail += deltaLength;
0405: }
0406: int addr = queueTail + queueSize;
0407: if (addr >= queueInts.length)
0408: addr -= queueInts.length;
0409: queueInts[addr + QUEUED_OP_TYPE] = kind | (size << 16);
0410: if (size > 1)
0411: queueInts[addr + QUEUED_OP_POSN] = indexPosn(bufferFillPointer);
0412: queueSize += size;
0413: return addr;
0414: }
0415:
0416: public void enqueueNewline(int kind) // DONE
0417: {
0418: int depth = pendingBlocksCount;
0419: int newline = enqueue(QUEUED_OP_NEWLINE_TYPE,
0420: QUEUED_OP_NEWLINE_SIZE);
0421: queueInts[newline + QUEUED_OP_NEWLINE_KIND] = kind;
0422: queueInts[newline + QUEUED_OP_SECTION_START_DEPTH] = pendingBlocksCount;
0423: queueInts[newline + QUEUED_OP_SECTION_START_SECTION_END] = 0;
0424: int entry = queueTail;
0425: int todo = queueSize;
0426: while (todo > 0) {
0427: if (entry == queueInts.length)
0428: entry = 0;
0429: if (entry == newline)
0430: break;
0431: int type = getQueueType(entry);
0432: if ((type == QUEUED_OP_NEWLINE_TYPE || type == QUEUED_OP_BLOCK_START_TYPE)
0433: && queueInts[entry
0434: + QUEUED_OP_SECTION_START_SECTION_END] == 0
0435: && depth <= queueInts[entry
0436: + QUEUED_OP_SECTION_START_DEPTH]) {
0437: int delta = newline - entry;
0438: if (delta < 0)
0439: delta += queueInts.length;
0440: queueInts[entry + QUEUED_OP_SECTION_START_SECTION_END] = delta;
0441: }
0442: int size = getQueueSize(entry);
0443: todo -= size;
0444: entry += size;
0445: }
0446: maybeOutput(kind == NEWLINE_LITERAL
0447: || kind == NEWLINE_MANDATORY);
0448: }
0449:
0450: public final void writeBreak(int kind) {
0451: if (isPrettyPrinting)
0452: enqueueNewline(kind);
0453: }
0454:
0455: public int enqueueIndent(int kind, int amount) {
0456: int result = enqueue(QUEUED_OP_INDENTATION_TYPE,
0457: QUEUED_OP_INDENTATION_SIZE);
0458: queueInts[result + QUEUED_OP_INDENTATION_KIND] = kind;
0459: queueInts[result + QUEUED_OP_INDENTATION_AMOUNT] = amount;
0460: return result;
0461: }
0462:
0463: public void addIndentation(int amount, boolean current) {
0464: if (isPrettyPrinting)
0465: enqueueIndent((current ? QUEUED_OP_INDENTATION_CURRENT
0466: : QUEUED_OP_INDENTATION_BLOCK), amount);
0467: }
0468:
0469: public void startLogicalBlock(String prefix, boolean perLine,
0470: String suffix) {
0471: if (prefix != null)
0472: write(prefix);
0473: if (!isPrettyPrinting)
0474: return;
0475: int start = enqueue(QUEUED_OP_BLOCK_START_TYPE,
0476: QUEUED_OP_BLOCK_START_SIZE);
0477: queueInts[start + QUEUED_OP_SECTION_START_DEPTH] = pendingBlocksCount;
0478: queueStrings[start + QUEUED_OP_BLOCK_PREFIX] = perLine ? prefix
0479: : null;
0480: queueStrings[start + QUEUED_OP_BLOCK_START_SUFFIX] = suffix;
0481: pendingBlocksCount++;
0482: currentBlock -= start;
0483: if (currentBlock > 0)
0484: currentBlock -= queueInts.length;
0485: queueInts[start + QUEUED_OP_SECTION_START_SECTION_END] = 0;
0486: queueInts[start + QUEUED_OP_BLOCK_START_BLOCK_END] = currentBlock;
0487: currentBlock = start;
0488: }
0489:
0490: public void endLogicalBlock() {
0491: int end = enqueue(QUEUED_OP_BLOCK_END_TYPE,
0492: QUEUED_OP_BLOCK_END_SIZE);
0493: pendingBlocksCount--;
0494: if (blockDepth >= LOGICAL_BLOCK_LENGTH
0495: * (pendingBlocksCount + 2)) {
0496: // reallyStartLogicalBlock has been called for the matching
0497: // BEGIN_BLOCK, so it is no longer in the queue. Instead it is in
0498: // the 'blocks' stack.
0499: int suffixLength = blocks[blockDepth + BLOCK_SUFFIX_LENGTH];
0500: int suffixPreviousLength = blocks[blockDepth
0501: - LOGICAL_BLOCK_LENGTH + BLOCK_SUFFIX_LENGTH];
0502: if (suffixLength > suffixPreviousLength)
0503: write(this .suffix, this .suffix.length - suffixLength,
0504: suffixLength - suffixPreviousLength);
0505: return;
0506: }
0507: int start = currentBlock;
0508: currentBlock = queueInts[start
0509: + QUEUED_OP_BLOCK_START_BLOCK_END];
0510: // Make currentBlock absolute instead of relative.
0511: currentBlock += start;
0512: if (currentBlock < 0)
0513: currentBlock += queueInts.length;
0514: String suffix = queueStrings[start
0515: + QUEUED_OP_BLOCK_START_SUFFIX];
0516: if (suffix != null)
0517: write(suffix);
0518: int endFromStart = end - start;
0519: if (endFromStart < 0) // wrap-around.
0520: endFromStart += queueInts.length;
0521: queueInts[start + QUEUED_OP_BLOCK_START_BLOCK_END] = endFromStart;
0522: }
0523:
0524: public void endLogicalBlock(String suffix) {
0525: if (isPrettyPrinting)
0526: endLogicalBlock();
0527: else if (suffix != null)
0528: write(suffix);
0529: }
0530:
0531: // Tab support
0532:
0533: int computeTabSize(int tab, int sectionStart, int column) // DONE
0534: {
0535: int flags = queueInts[tab + QUEUED_OP_TAB_FLAGS];
0536: boolean isSection = (flags & QUEUED_OP_TAB_IS_SECTION) != 0;
0537: boolean isRelative = (flags & QUEUED_OP_TAB_IS_RELATIVE) != 0;
0538: int origin = isSection ? sectionStart : 0;
0539: int colnum = queueInts[tab + QUEUED_OP_TAB_COLNUM];
0540: int colinc = queueInts[tab + QUEUED_OP_TAB_COLINC];
0541: if (isRelative) {
0542: if (colinc > 1) {
0543: int newposn = column + colnum;
0544: int rem = newposn % colinc;
0545: if (rem != 0)
0546: colnum += colinc = rem;
0547: }
0548: return colnum;
0549: } else if (column <= colnum + origin)
0550: return column + origin - column;
0551: else
0552: return colinc - (column - origin) % colinc;
0553: }
0554:
0555: int indexColumn(int index) // DONE
0556: {
0557: int column = bufferStartColumn;
0558: int sectionStart = getSectionColumn();
0559: int endPosn = indexPosn(index);
0560: int op = queueTail;
0561: int todo = queueSize;
0562: while (todo > 0) {
0563: // If at end of queueInt, or it's a 1-word QUEUED_OP_DUMMY_TYPE, skip.
0564: if (op >= queueInts.length - 1)
0565: op = 0;
0566: int posn = queueInts[op + QUEUED_OP_POSN];
0567: if (posn >= endPosn)
0568: break;
0569: int type = getQueueType(op);
0570: if (type == QUEUED_OP_TAB_TYPE) {
0571: column += computeTabSize(op, sectionStart, column
0572: + posnIndex(posn));
0573: } else if (type == QUEUED_OP_NEWLINE_TYPE
0574: || type == QUEUED_OP_BLOCK_START_TYPE) {
0575: sectionStart = column
0576: + posnIndex(queueInts[op + QUEUED_OP_POSN]);
0577: }
0578: int size = getQueueSize(op);
0579: todo -= size;
0580: op += size;
0581: }
0582: return column + index;
0583: }
0584:
0585: void expandTabs(int through) // ODNE except FIXMEs
0586: {
0587: int numInsertions = 0;
0588: int additional = 0;
0589: int column = bufferStartColumn;
0590: int sectionStart = getSectionColumn();
0591: int op = queueTail;
0592: int todo = queueSize;
0593: int blocksUsed = LOGICAL_BLOCK_LENGTH * pendingBlocksCount;
0594: while (todo > 0) {
0595: if (op == queueInts.length)
0596: op = 0;
0597: if (op == through)
0598: break;
0599: int type = getQueueType(op);
0600: if (type == QUEUED_OP_TAB_TYPE) {
0601: int index = posnIndex(queueInts[op + QUEUED_OP_POSN]);
0602: int tabsize = computeTabSize(op, sectionStart, column
0603: + index);
0604: if (tabsize != 0) {
0605: // We use the blocks array for a temporary tab buffer.
0606: if (blocksUsed + 2 * numInsertions + 1 >= blocks.length) {
0607: int[] newBlocks = new int[2 * blocks.length];
0608: System.arraycopy(blocks, 0, newBlocks, 0,
0609: blocks.length);
0610: blocks = newBlocks;
0611: }
0612: blocks[blocksUsed + 2 * numInsertions] = index;
0613: blocks[blocksUsed + 2 * numInsertions + 1] = tabsize;
0614: numInsertions++;
0615: additional += tabsize;
0616: column += tabsize;
0617: }
0618: } else if (op == QUEUED_OP_NEWLINE_TYPE
0619: || op == QUEUED_OP_BLOCK_START_TYPE) {
0620: sectionStart = column
0621: + posnIndex(queueInts[op + QUEUED_OP_POSN]);
0622: }
0623: int size = getQueueSize(op);
0624: todo -= size;
0625: op += size;
0626: }
0627: if (numInsertions > 0) {
0628: int fillPtr = bufferFillPointer;
0629: int newFillPtr = fillPtr + additional;
0630: char[] buffer = this .buffer;
0631: char[] newBuffer = buffer;
0632: int length = buffer.length;
0633: int end = fillPtr;
0634: if (newFillPtr > length) {
0635: int newLength = enoughSpace(fillPtr, additional);
0636: newBuffer = new char[newLength];
0637: this .buffer = newBuffer;
0638: }
0639: bufferFillPointer = newFillPtr;
0640: bufferOffset -= additional;
0641: for (int i = numInsertions; --i >= 0;) {
0642: int srcpos = blocks[blocksUsed + 2 * i];
0643: int amount = blocks[blocksUsed + 2 * i + 1];
0644: int dstpos = srcpos + additional;
0645: System.arraycopy(buffer, srcpos, newBuffer, dstpos, end
0646: - srcpos);
0647: for (int j = dstpos - amount; j < dstpos; j++)
0648: newBuffer[j] = ' ';
0649: additional -= amount;
0650: end = srcpos;
0651: }
0652: if (newBuffer != buffer)
0653: System.arraycopy(buffer, 0, newBuffer, 0, end);
0654: }
0655: }
0656:
0657: // stuff to do the actual outputting
0658:
0659: int ensureSpaceInBuffer(int want) {
0660: char[] buffer = this .buffer;
0661: int length = buffer.length;
0662: int fillPtr = bufferFillPointer;
0663: int available = length - fillPtr;
0664: if (available > 0)
0665: return available;
0666: else if (isPrettyPrinting && fillPtr > lineLength) {
0667: if (!maybeOutput(false))
0668: outputPartialLine();
0669: return ensureSpaceInBuffer(want);
0670: } else {
0671: int newLength = enoughSpace(length, want);
0672: char[] newBuffer = new char[newLength];
0673: this .buffer = newBuffer;
0674: for (int i = fillPtr; --i >= 0;)
0675: newBuffer[i] = buffer[i];
0676: return newLength - fillPtr;
0677: }
0678: }
0679:
0680: boolean maybeOutput(boolean forceNewlines) // DONE
0681: {
0682: boolean outputAnything = false;
0683: //System.err.print("maybeOutput("+forceNewlines+"):"); System.err.flush();
0684: //dumpQueue();
0685: loop: while (queueSize > 0) {
0686: if (queueTail >= queueInts.length)
0687: queueTail = 0;
0688: int next = queueTail;
0689: int type = getQueueType(next);
0690: switch (type) {
0691: case QUEUED_OP_NEWLINE_TYPE:
0692: boolean cond;
0693: switch (queueInts[next + QUEUED_OP_NEWLINE_KIND]) {
0694: default: // LINEAR, LITERAL, or MANDATORY:
0695: cond = true;
0696: break;
0697: case NEWLINE_MISER:
0698: cond = isMisering();
0699: break;
0700: case NEWLINE_FILL:
0701: if (isMisering()
0702: || (lineNumber > getSectionStartLine())) {
0703: cond = true;
0704: break;
0705: }
0706: int end = queueInts[next
0707: + QUEUED_OP_SECTION_START_SECTION_END];
0708: if (end == 0)
0709: end = -1;
0710: else { // convert relative->absolute.
0711: end = next + end;
0712: if (end >= queueInts.length)
0713: end -= queueInts.length;
0714: }
0715: int fits = fitsOnLine(end, forceNewlines);
0716: if (fits > 0)
0717: cond = false;
0718: else if (fits < 0)
0719: cond = true;
0720: else
0721: break loop;
0722: break;
0723: }
0724: if (cond) {
0725: outputAnything = true;
0726: try {
0727: outputLine(next);
0728: } catch (IOException ex) {
0729: throw new RuntimeException(ex.toString());
0730: }
0731: }
0732: break;
0733: case QUEUED_OP_INDENTATION_TYPE:
0734: if (!isMisering()) {
0735: int kind = queueInts[next
0736: + QUEUED_OP_INDENTATION_KIND];
0737: int indent = queueInts[next
0738: + QUEUED_OP_INDENTATION_AMOUNT];
0739: if (kind == QUEUED_OP_INDENTATION_BLOCK)
0740: indent += getStartColumn();
0741: else
0742: indent += posnColumn(queueInts[next
0743: + QUEUED_OP_POSN]);
0744: setIndentation(indent);
0745: }
0746: break;
0747: case QUEUED_OP_BLOCK_START_TYPE:
0748: int end = queueInts[next
0749: + QUEUED_OP_SECTION_START_SECTION_END];
0750: // Convert relative offset to absolute index:
0751: end = end > 0 ? (end + next) % queueInts.length : -1;
0752: int fits = fitsOnLine(end, forceNewlines);
0753: if (fits > 0) {
0754: // Just nuke the whole logical block and make it look
0755: // like one nice long literal.
0756: int endr = queueInts[next
0757: + QUEUED_OP_BLOCK_START_BLOCK_END];
0758: // make absolute:
0759: next = (endr + next) % queueInts.length;
0760: expandTabs(next);
0761: queueTail = next;
0762: queueSize -= endr;
0763: } else if (fits < 0) {
0764: String prefix = queueStrings[next
0765: + QUEUED_OP_BLOCK_PREFIX];
0766: String suffix = queueStrings[next
0767: + QUEUED_OP_BLOCK_START_SUFFIX];
0768: reallyStartLogicalBlock(posnColumn(queueInts[next
0769: + QUEUED_OP_POSN]), prefix, suffix);
0770:
0771: } else
0772: // Don't know.
0773: break loop;
0774: break;
0775: case QUEUED_OP_BLOCK_END_TYPE:
0776: reallyEndLogicalBlock();
0777: break;
0778: case QUEUED_OP_TAB_TYPE:
0779: expandTabs(next);
0780: break;
0781: }
0782: int size = getQueueSize(queueTail);
0783: queueSize -= size;
0784: queueTail = next + size;
0785: }
0786: return outputAnything;
0787: }
0788:
0789: int miserWidth;
0790:
0791: protected int getMiserWidth() // DONE
0792: {
0793: // CommonLisp: Use *print-miser-width*.
0794: return 40;
0795: }
0796:
0797: boolean isMisering() // DONE
0798: {
0799: int mwidth = getMiserWidth();
0800: return (mwidth > 0 && lineLength - getStartColumn() <= mwidth);
0801: }
0802:
0803: int getMaxLines() {
0804: // Should be value of CommonLisp *print-lines*.
0805: return -1;
0806: }
0807:
0808: boolean printReadably() {
0809: // Should be value of CommonLisp *print-readably*.
0810: return true;
0811: }
0812:
0813: /** Return 1 if true; -1 if false; 0 if don't know. */
0814: int fitsOnLine(int sectionEnd, boolean forceNewlines) // DONE
0815: {
0816: int available = lineLength;
0817: if (!printReadably() && getMaxLines() == lineNumber) {
0818: available -= 3; // For the " ..".
0819: available -= getSuffixLength();
0820: }
0821: if (sectionEnd >= 0)
0822: return posnColumn(queueInts[sectionEnd + QUEUED_OP_POSN]) <= available ? 1
0823: : -1;
0824: if (forceNewlines)
0825: return -1;
0826: if (indexColumn(bufferFillPointer) > available)
0827: return -1;
0828: return 0; // don't know.
0829: }
0830:
0831: public void lineAbbreviationHappened() {
0832: // Hook.
0833: }
0834:
0835: /** Output a newline.
0836: * @param newline index of a newline queue item
0837: */
0838: void outputLine(int newline) throws IOException {
0839: char[] buffer = this .buffer;
0840: int kind = queueInts[newline + QUEUED_OP_NEWLINE_KIND];
0841: boolean isLiteral = kind == NEWLINE_LITERAL;
0842: int amountToConsume = posnIndex(queueInts[newline
0843: + QUEUED_OP_POSN]);
0844: int amountToPrint;
0845: if (isLiteral)
0846: amountToPrint = amountToConsume;
0847: else {
0848: // Skip trailing spaces.
0849: for (int i = amountToConsume;;) {
0850: if (--i < 0) {
0851: amountToPrint = 0;
0852: break;
0853: }
0854: if (buffer[i] != ' ') {
0855: amountToPrint = i + 1;
0856: break;
0857: }
0858: }
0859: }
0860: out.write(buffer, 0, amountToPrint);
0861: int lineNumber = this .lineNumber;
0862: lineNumber++;
0863: if (!printReadably()) {
0864: int maxLines = getMaxLines();
0865: if (maxLines > 0 && lineNumber >= maxLines) {
0866: out.write(" ..");
0867: int suffixLength = getSuffixLength();
0868: if (suffixLength != 0) {
0869: char[] suffix = this .suffix;
0870: int len = suffix.length;
0871: out.write(suffix, len - suffixLength, suffixLength);
0872: }
0873: // (throw 'line-limit-abbreviation-happened t))
0874: lineAbbreviationHappened();
0875: }
0876: }
0877: this .lineNumber = lineNumber;
0878: out.write('\n');
0879: bufferStartColumn = 0;
0880: int fillPtr = bufferFillPointer;
0881: int prefixLen = isLiteral ? getPerLinePrefixEnd()
0882: : getPrefixLength();
0883: int shift = amountToConsume - prefixLen;
0884: int newFillPtr = fillPtr - shift;
0885: char[] newBuffer = buffer;
0886: int bufferLength = buffer.length;
0887: if (newFillPtr > bufferLength) {
0888: newBuffer = new char[enoughSpace(bufferLength, newFillPtr
0889: - bufferLength)];
0890: this .buffer = newBuffer;
0891: }
0892: System.arraycopy(buffer, amountToConsume, newBuffer, prefixLen,
0893: fillPtr - amountToConsume);
0894: System.arraycopy(prefix, 0, buffer, 0, prefixLen);
0895: bufferFillPointer = newFillPtr;
0896: bufferOffset += shift;
0897: if (!isLiteral) {
0898: blocks[blockDepth + BLOCK_SECTION_COLUMN] = prefixLen;
0899: blocks[blockDepth + BLOCK_SECTION_START_LINE] = lineNumber;
0900: }
0901: }
0902:
0903: void outputPartialLine() {
0904: int fillPtr = bufferFillPointer;
0905: int tail = queueTail;
0906: int count = queueSize > 0 ? posnIndex(queueInts[tail
0907: + QUEUED_OP_POSN]) : fillPtr;
0908: int newFillPtr = fillPtr - count;
0909: if (count <= 0)
0910: throw new Error(
0911: "outputPartialLine called when nothing can be output.");
0912: try {
0913: out.write(buffer, 0, count);
0914: } catch (IOException ex) {
0915: throw new RuntimeException(ex.toString());
0916: }
0917: bufferStartColumn += count;
0918: System.arraycopy(buffer, count, buffer, 0, newFillPtr);
0919: bufferFillPointer = newFillPtr;
0920: bufferOffset += count;
0921: }
0922:
0923: public void forcePrettyOutput() throws IOException {
0924: maybeOutput(false);
0925: expandTabs(-1);
0926: //System.err.println("{FORCED:"+bufferFillPointer+"}");
0927: bufferStartColumn = getColumnNumber();
0928: out.write(buffer, 0, bufferFillPointer);
0929: bufferFillPointer = 0;
0930: //System.err.println("FLUSH: depth:"+blockDepth+" preLen:"+getPrefixLength()+" col:"+bufferStartColumn);
0931: }
0932:
0933: public void flush() {
0934: if (out == null)
0935: return;
0936: try {
0937: forcePrettyOutput();
0938: out.flush();
0939: } catch (IOException ex) {
0940: throw new RuntimeException(ex.toString());
0941: }
0942: }
0943:
0944: public void close() throws IOException {
0945: if (out != null) {
0946: forcePrettyOutput();
0947: out.close();
0948: out = null;
0949: }
0950: buffer = null;
0951: }
0952:
0953: /** Not meaningful if isPrettyPrinting. */
0954: public int getColumnNumber() {
0955: int i = bufferFillPointer;
0956: for (;;) {
0957: if (--i < 0)
0958: return bufferStartColumn + bufferFillPointer;
0959: char ch = buffer[i];
0960: if (ch == '\n' || ch == '\r')
0961: return bufferFillPointer - i;
0962: }
0963: }
0964:
0965: public void setColumnNumber(int column) {
0966: bufferStartColumn += column - getColumnNumber();
0967: }
0968:
0969: public void clearBuffer() {
0970: bufferStartColumn = 0;
0971: bufferFillPointer = 0;
0972: lineNumber = 0;
0973: bufferOffset = 0;
0974: blockDepth = LOGICAL_BLOCK_LENGTH;
0975: queueTail = 0;
0976: queueSize = 0;
0977: pendingBlocksCount = 0;
0978: }
0979:
0980: void dumpQueue() {
0981: PrintWriter out = new PrintWriter(System.err);
0982: out.println("Queue tail:" + queueTail + " size:" + queueSize);
0983: dumpQueue(queueTail, queueSize, out);
0984: out.flush();
0985: }
0986:
0987: void dumpQueue(int start, int todo, PrintWriter out) {
0988: while (todo > 0) {
0989: if (start == queueInts.length)
0990: start = 0;
0991: int type = getQueueType(start);
0992: int size = getQueueSize(start);
0993: out.print('@');
0994: out.print(start);
0995: out.print(": ");
0996: out.print("type:");
0997: out.print(type);
0998: switch (type) {
0999: case QUEUED_OP_NEWLINE_TYPE:
1000: out.print("(newline)");
1001: break;
1002: case QUEUED_OP_INDENTATION_TYPE:
1003: out.print("(indentation)");
1004: break;
1005: case QUEUED_OP_BLOCK_START_TYPE:
1006: out.print("(block-start)");
1007: break;
1008: case QUEUED_OP_BLOCK_END_TYPE:
1009: out.print("(block-end)");
1010: break;
1011: case QUEUED_OP_TAB_TYPE:
1012: out.print("(tab)");
1013: break;
1014: }
1015: out.print(" size:");
1016: out.print(size);
1017: out.print("; @");
1018: out.print(start + QUEUED_OP_POSN);
1019: out.print(": posn:");
1020: out.println(queueInts[start + QUEUED_OP_POSN]);
1021: if (type == QUEUED_OP_NEWLINE_TYPE
1022: || type == QUEUED_OP_BLOCK_START_TYPE)
1023:
1024: {
1025: out.print('@');
1026: out.print(start + QUEUED_OP_SECTION_START_DEPTH);
1027: out.print(": - depth:");
1028: out.print(queueInts[start
1029: + QUEUED_OP_SECTION_START_DEPTH]);
1030: out.print("; @");
1031: out.print(start + QUEUED_OP_SECTION_START_SECTION_END);
1032: out.print(": section-end:");
1033: out.println(queueInts[start
1034: + QUEUED_OP_SECTION_START_SECTION_END]);
1035: }
1036: switch (type) {
1037: case QUEUED_OP_BLOCK_START_TYPE:
1038: printQueueWord(start, QUEUED_OP_BLOCK_START_BLOCK_END,
1039: "block-end", out);
1040: String prefix = queueStrings[start
1041: + QUEUED_OP_BLOCK_PREFIX];
1042: printQueueStringWord(start, QUEUED_OP_BLOCK_PREFIX,
1043: "prefix", out);
1044: printQueueStringWord(start,
1045: QUEUED_OP_BLOCK_START_SUFFIX, "suffix", out);
1046: break;
1047: case QUEUED_OP_NEWLINE_TYPE:
1048: out.print('@');
1049: out.print(start + QUEUED_OP_NEWLINE_KIND);
1050: out.print(": - kind: ");
1051: int kind = queueInts[start + QUEUED_OP_NEWLINE_KIND];
1052: String skind = "???";
1053: switch (kind) {
1054: case NEWLINE_LINEAR:
1055: skind = "linear";
1056: break;
1057: case NEWLINE_LITERAL:
1058: skind = "literal";
1059: break;
1060: case NEWLINE_FILL:
1061: skind = "fill";
1062: break;
1063: case NEWLINE_MISER:
1064: skind = "miser";
1065: break;
1066: case NEWLINE_MANDATORY:
1067: skind = "mandatory";
1068: break;
1069: }
1070: out.print(kind);
1071: out.print('(');
1072: out.print(skind);
1073: out.println(')');
1074: break;
1075: default:
1076: for (int i = 2; i < size; i++)
1077: printQueueWord(start, i, "word#" + i, out);
1078: }
1079: todo -= size;
1080: start += size;
1081: }
1082: }
1083:
1084: private void printQueueWord(int start, int offset, String fname,
1085: PrintWriter out) {
1086: out.print('@');
1087: out.print(start + offset);
1088: out.print(": - ");
1089: out.print(fname);
1090: out.print(": ");
1091: out.println(queueInts[start + offset]);
1092: }
1093:
1094: private void printQueueStringWord(int start, int offset,
1095: String fname, PrintWriter out) {
1096: out.print('@');
1097: out.print(start + offset);
1098: out.print(": - ");
1099: out.print(fname);
1100: out.print(": ");
1101: String str = queueStrings[start + offset];
1102: if (str == null)
1103: out.println("null");
1104: else {
1105: out.print('\"');
1106: out.print(str);
1107: out.print('\"');
1108: out.print(" length: ");
1109: out.println(str.length());
1110: }
1111: }
1112: }
|