| 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
java.io.Writer , resp. a
java.lang.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, line 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.begin(true,2).print("while (i>0) {").brk(1,0)
.print("i--;").brk(1,0)
.print("j++;").brk(1,-2)
.print("{").end();
The call to
Layouter.begin(boolean,int) starts a consistent block,
increasing the indentation level by 2. The
Layouter.print(String) methods gives some actual text to be output. The call to
Layouter.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
with .
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
Layouter.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("...[").begin(true,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
Layouter.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
Layouter.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
Layouter.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
Layouter.beginC() is shorthand for
begin(true,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.
Also, most methods can throw an
java.io.IOException .
This only happens if a called method in the backend throws an
IOException, in which case that exception is passed through to the
caller of the Layouter method.
author: Martin Giese See Also: Backend |