de.uka.ilkd.pp
Class Layouter<Exc extends java.lang.Exception>

java.lang.Object
  extended by de.uka.ilkd.pp.Layouter<Exc>
Type Parameters:
Exc - The type of exceptions that might be thrown by the backend.

public class Layouter<Exc extends java.lang.Exception>
extends java.lang.Object

This class pretty-prints information using line breaks and indentation. For instance, it can be used to print

   while (i>0) {
     i--;
     j++;
   }
 
instead of
   while (i>0) { i
   --; j++;}
 
if a maximum line width of 15 characters is chosen.

The formatted output is directed to a backend which might write it to an I/O stream, append it to the text of a GUI componenet or store it in a string. The Backend interface encapsulates the concept of backend. Apart from handling the output, the backend is also asked for the available line width and for the amount of space needed to print a string. This makes it possible to include e.g. HTML markup in the output which does not take up any space. There are two convenience implementations WriterBackend and StringBackend, which write the output to a Writer, resp. a String.

The layouter internally keeps track of a current indentation level. Think of nicely indented Java source code. Then the indentation level at any point is the number of blank columns to be inserted at the begining of the next line if you inserted a line break. To increase the indentation level of parts of the text, the input to the layouter is separated into blocks. The indentation level changes when a block is begun, and it is reset to its previous value when a block is ended. Of course, blocks maybe nested.

In order to break text among several lines, the layouter needs to be told where line breaks are allowed. A break is a position in the text where there is either a line break (with appropriate indentation) or a number of spaces, if enough material fits in one line. In order to handle the indentation level properly, breaks should only occur inside blocks. There are in fact two kinds of blocks: consistent and inconsistent ones. In a consistent block, lines are broken either at all or at none of the breaks. In an inconsistent block, as much material as possible is put on one line before it is broken.

Consider the program above. It should be printed either as

   while (i>0) { i--; j++; }
 
or, if there is not enough space on the line, as
   while (i>0) {
     i--;
     j++;
   }
 
Given a Layouter object l, we could say:
 l.beginC(2).print("while (i>0) {").brk(1,0)
  .print("i--;").brk(1,0)
  .print("j++;").brk(1,-2)
  .print("{").end();
 
The call to beginC(int) starts a consistent block, increasing the indentation level by 2. The print(String) methods gives some actual text to be output. The call to brk(int,int) inserts a break. The first argument means that one space should be printed at this position if the line is not broken. The second argument is an offset to be added to the indentation level for the next line, if the line is broken. The effect of this parameter can be seen in the call brk(1,-2). The offset of -2 outdents the last line by 2 positions, which aligns the closing brace with the while.

If the lines in a block are broken, one sometimes wants to insert spaces up to the current indentation level at a certain position without allowing a line break there. This can be done using the ind(int,int) method. For instance, one wants to output either

   ...[Good and Bad and Ugly]...
 
or
   ...[    Good
       and Bad
       and Ugly]...
 
Note the four spaces required before Good. We do this by opening a block which sets the indentation level to the column where the G ends up and outdenting the lines with the and:
   l.print("...[").beginC(4).ind(0,0).print("Good").brk(1,-4)
    .print("and ").print("Bad").brk(1,-4)
    .print("and ").print("Ugly").end().print("]...");
 
Again, the first argument to ind(int,int) is a number of spaces to print if the block we are in is printed on one line. The second argument is an offset to be added to the current indentation level to determine the column to which we should skip.

When all text has been sent to a Layouter and all blocks have been ended, the close() method should be closed. This sends all pending output to the backend and invokes the Backend.close() method, which usually closes I/O streams, etc.

Some applications need to keep track of where certain parts of the input text end up in the output. For this purpose, the Layouter class provides the mark(Object) method.

The public methods of this class may be divided into two categories: A small number of primitive methods, as described above, and a host of convenience methods which simplify calling the primitive ones for often-used arguments. For instance, a call to beginC() is shorthand for beginC(ind), where ind is the default indentation selected when the Layouter was constructed.

Most of the methods can throw an UnbalancedBlocksException, which indicates that the sequence of method calls was illegal, i.e. more blocks were ended than begun, the Layouter is closed before all blocks are ended, a break occurs outside of any block, etc.

The backend might throw exceptions of the type indicated by the type parameter Exc. Such exceptions get passed through to the caller of the Layouter. Note that since text usually gets buffered before it is sent to the backend, exceptions thrown by calls to Layouter methods might not be caused directly by that method call but by an earlier one getting forwarded to the backend.

The algorithm used is essentially the classical one from Derek C. Oppen: Prettyprinting, TOPLAS volume 2 number 4, ACM, 1980, pp. 465-483, with some minor extensions. It has the property that if the input contains enough actual text, i.e. not just arbitrarily long sequences of calls to beginC, mark, etc., then pretty-printing uses constant space, and time linear in the size of the input. In fact, output will begin before the whole input has been given, so this class can be used to pretty-print a stream of data.

Author:
Martin Giese
See Also:
Backend

Field Summary
static int DEFAULT_INDENTATION
          = 2 : The default indentation for some of the convenience constructors
static int DEFAULT_LINE_WIDTH
          = 80 : The line width for some of the convenience factories.
 
Constructor Summary
Layouter(Backend<Exc> back, int indentation)
          Construts a newly allocated Layouter which will send output to the given Backend and has the given default indentation.
 
Method Summary
 Layouter<Exc> begin(boolean consistent)
          Begin a block with default indentation.
 Layouter<Exc> begin(boolean consistent, int indent)
          Begin a block.
 Layouter<Exc> beginC()
          Begin a consistent block.
 Layouter<Exc> beginC(int indent)
          Begin a consistent block.
 Layouter<Exc> beginI()
          Begin an inconsistent block.
 Layouter<Exc> beginI(int indent)
          Begin an inconsistent block.
 Layouter<Exc> brk()
          Print a break with zero offset and width one.
 Layouter<Exc> brk(int width)
          Print a break with zero offset.
 Layouter<Exc> brk(int width, int offset)
          Print a break.
 void close()
          Close the Layouter.
 Layouter<Exc> end()
          Ends the innermost block.
 Layouter<Exc> flush()
          Output any information currently kept in buffers.
static Layouter<java.io.IOException> getWriterLayouter(java.io.Writer writer)
          Factory method for a Layouter with a WriterBackend.
static Layouter<java.io.IOException> getWriterLayouter(java.io.Writer writer, int lineWidth)
          Factory method for a Layouter with a WriterBackend.
static Layouter<java.io.IOException> getWriterLayouter(java.io.Writer writer, int lineWidth, int indentation)
          Factory method for a Layouter with a WriterBackend.
 Layouter<Exc> ind()
          Indent with zero offset and zero width.
 Layouter<Exc> ind(int width, int offset)
          Indent to a current indentation level if surrounding block is broken.
 Layouter<Exc> mark(java.lang.Object o)
          This leads to a call of the Backend.mark(Object) method of the backend, when the material preceding the call to mark has been printed to the backend, including any inserted line breaks and indentation.
 Layouter<Exc> nl()
          Print a break with zero offset and large width.
 Layouter<Exc> pre(java.lang.String s)
          Layout prefromated text.
 Layouter<Exc> print(java.lang.String s)
          Output text material.
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Field Detail

DEFAULT_LINE_WIDTH

public static final int DEFAULT_LINE_WIDTH
= 80 : The line width for some of the convenience factories.

See Also:
Constant Field Values

DEFAULT_INDENTATION

public static final int DEFAULT_INDENTATION
= 2 : The default indentation for some of the convenience constructors

See Also:
Constant Field Values
Constructor Detail

Layouter

public Layouter(Backend<Exc> back,
                int indentation)
Construts a newly allocated Layouter which will send output to the given Backend and has the given default indentation.

Parameters:
back - the Backend
indentation - the default indentation
Method Detail

getWriterLayouter

public static Layouter<java.io.IOException> getWriterLayouter(java.io.Writer writer)
Factory method for a Layouter with a WriterBackend. The line width is taken to be DEFAULT_LINE_WIDTH, and the default indentation DEFAULT_INDENTATION.

Parameters:
writer - the Writer the Backend is going to use

getWriterLayouter

public static Layouter<java.io.IOException> getWriterLayouter(java.io.Writer writer,
                                                              int lineWidth)
Factory method for a Layouter with a WriterBackend. The default indentation is taken from DEFAULT_INDENTATION.

Parameters:
writer - the Writer the Backend is going to use
lineWidth - the maximum lineWidth the Backend is going to use

getWriterLayouter

public static Layouter<java.io.IOException> getWriterLayouter(java.io.Writer writer,
                                                              int lineWidth,
                                                              int indentation)
Factory method for a Layouter with a WriterBackend.

Parameters:
writer - the Writer the Backend is going to use
lineWidth - the maximum lineWidth the Backend is going to use
indentation - the default indentation

print

public Layouter<Exc> print(java.lang.String s)
                                                throws Exc extends java.lang.Exception
Output text material. The string s should not contain newline characters. If you have a string with newline characters, and want to retain its formatting, consider using the pre(String s) method. The Layouter will not insert any line breaks in such a string.

Parameters:
s - the String to print.
Returns:
this
Throws:
Exc extends java.lang.Exception

begin

public Layouter<Exc> begin(boolean consistent,
                           int indent)
Begin a block. If consistent is set, breaks are either all broken or all not broken. The indentation level is increased by indent.

Parameters:
consistent - true for consistent block
indent - increment to indentation level
Returns:
this

end

public Layouter<Exc> end()
                                              throws Exc extends java.lang.Exception
Ends the innermost block.

Returns:
this
Throws:
Exc extends java.lang.Exception

brk

public Layouter<Exc> brk(int width,
                         int offset)
                                              throws Exc extends java.lang.Exception
Print a break. This will print width spaces if the line is not broken at this point. If it is broken, indentation is added to the current indentation level, plus the value of offset.

Parameters:
width - space to insert if not broken
offset - offset relative to current indentation level
Returns:
this
Throws:
Exc extends java.lang.Exception

ind

public Layouter<Exc> ind(int width,
                         int offset)
                                              throws Exc extends java.lang.Exception
Indent to a current indentation level if surrounding block is broken. If the surrounding block fits on one line, insert width spaces. Otherwise, indent to the current indentation level, plus offset, unless that position has already been exceeded on the current line. If that is the case, nothing is printed. No line break is possible at this point.

Parameters:
width - space to insert if not broken
offset - offset relative to current indentation level
Returns:
this
Throws:
Exc extends java.lang.Exception

mark

public Layouter<Exc> mark(java.lang.Object o)
                                               throws Exc extends java.lang.Exception
This leads to a call of the Backend.mark(Object) method of the backend, when the material preceding the call to mark has been printed to the backend, including any inserted line breaks and indentation. The Object argument to mark is passed through unchanged to the backend and may be used by the application to pass information about the purpose of the mark.

Parameters:
o - an object to be passed through to the backend.
Returns:
this
Throws:
Exc extends java.lang.Exception

flush

public Layouter<Exc> flush()
                                                throws Exc extends java.lang.Exception
Output any information currently kept in buffers. This is essentially passed on to the backend. Note that material in blocks begun but not ended cannot be forced to the output by this method. Finish all blocks and call flush or close() then.

Returns:
this
Throws:
Exc extends java.lang.Exception

close

public void close()
           throws Exc extends java.lang.Exception
Close the Layouter. No more methods should be called after this. All blocks begun must have been ended by this point. Any pending material is written to the backend, before the Backend.close() method of the backend is called, which closes any open I/O streams, etc.

Throws:
Exc extends java.lang.Exception

beginI

public Layouter<Exc> beginI()
Begin an inconsistent block. Add this Layouter's default indentation to the indentation level.

Returns:
this

beginC

public Layouter<Exc> beginC()
Begin a consistent block. Add this Layouter's default indentation to the indentation level.

Returns:
this

beginI

public Layouter<Exc> beginI(int indent)
Begin an inconsistent block. Add indent to the indentation level.

Parameters:
indent - the indentation for this block
Returns:
this

beginC

public Layouter<Exc> beginC(int indent)
Begin a consistent block. Add indent to the indentation level.

Parameters:
indent - the indentation for this block
Returns:
this

begin

public Layouter<Exc> begin(boolean consistent)
Begin a block with default indentation. Add this Layouter's default indentation to the indentation level.

Parameters:
consistent - true for consistent block
Returns:
this

brk

public Layouter<Exc> brk(int width)
                                              throws Exc extends java.lang.Exception
Print a break with zero offset.

Parameters:
width - space to insert if not broken
Returns:
this
Throws:
Exc extends java.lang.Exception

brk

public Layouter<Exc> brk()
                                              throws Exc extends java.lang.Exception
Print a break with zero offset and width one.

Returns:
this
Throws:
Exc extends java.lang.Exception

nl

public Layouter<Exc> nl()
                                             throws Exc extends java.lang.Exception
Print a break with zero offset and large width. As the large number of spaces will never fit into one line, this amounts to a forced line break.

Returns:
this
Throws:
Exc extends java.lang.Exception

ind

public Layouter<Exc> ind()
                                              throws Exc extends java.lang.Exception
Indent with zero offset and zero width. Just indents to the current indentation level if the block is broken, and prints nothing otherwise.

Returns:
this
Throws:
Exc extends java.lang.Exception

pre

public Layouter<Exc> pre(java.lang.String s)
                                              throws Exc extends java.lang.Exception
Layout prefromated text. This amounts to a (consistent) block with indentation 0, where each line of s (separated by \n) gets printed as a string and newlines become forced breaks.

Parameters:
s - the pre-formatted string
Returns:
this
Throws:
Exc extends java.lang.Exception