public class Layouter
extends java.lang.Object
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, 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
begin(boolean,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
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
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
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
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 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.
Backend
Modifier and Type | Class and Description |
---|---|
private class |
Layouter.BreakToken
A token corresponding to a
brk call. |
private class |
Layouter.CloseBlockToken
A token corresponding to an
end call. |
private class |
Layouter.IndentationToken
A token corresponding to an
ind call. |
private class |
Layouter.MarkToken
A token corresponding to a
mark call. |
private class |
Layouter.OpenBlockToken
A token corresponding to a
begin call. |
private class |
Layouter.SizeCalculatingToken
Superclass of tokens which calculate their followingSize.
|
private class |
Layouter.StreamToken
A stream token.
|
private class |
Layouter.StringToken
A token corresponding to a
print call. |
Modifier and Type | Field and Description |
---|---|
private Backend |
back
The backend
|
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 constructors.
|
private int |
defaultInd
A default indentation value used for blocks.
|
private java.util.List<Layouter.StreamToken> |
delimStack
A stack of
OpenBlockToken s and
BreakToken s in stream , waiting for
their size to be determined. |
private int |
largeSize
The size assigned to things which are guaranteed not to fit on a
line.
|
private Printer |
out
The Printer used for output.
|
private java.util.List<Layouter.StreamToken> |
stream
The list of scanned tokens not yet output.
|
private int |
totalOutput
Total size of strings and blanks sent to the Printer
out . |
private int |
totalSize
Total size of received strings and blanks, if they were
printed in one line.
|
Constructor and Description |
---|
Layouter(Backend back,
int indentation)
Construts a newly allocated Layouter which will send output to
the given
Backend and has the given default indentation. |
Layouter(java.io.Writer writer)
Convenience constructor for a Layouter with a
WriterBackend . |
Layouter(java.io.Writer writer,
int lineWidth)
Convenience constructor for a Layouter with a
WriterBackend . |
Layouter(java.io.Writer writer,
int lineWidth,
int indentation)
Convenience constructor for a Layouter with a
WriterBackend . |
Modifier and Type | Method and Description |
---|---|
private void |
advanceLeft()
Send tokens from
stream |
Layouter |
begin(boolean consistent)
Begin a block with default indentation.
|
Layouter |
begin(boolean consistent,
int indent)
Begin a block.
|
Layouter |
beginC()
Begin a consistent block.
|
Layouter |
beginC(int indent)
Begin a consistent block.
|
Layouter |
beginI()
Begin an inconsistent block.
|
Layouter |
beginI(int indent)
Begin an inconsistent block.
|
Layouter |
brk()
Print a break with zero offset and width one.
|
Layouter |
brk(int width)
Print a break with zero offset.
|
Layouter |
brk(int width,
int offset)
Print a break.
|
void |
close()
Close the Layouter.
|
private Layouter.StreamToken |
dequeue()
Get the front token from the stream.
|
Layouter |
end()
Ends the innermost block.
|
private void |
enqueue(Layouter.StreamToken t)
Put a StreamToken into the stream (at the end).
|
Layouter |
flush()
Output any information currently kept in buffers.
|
Layouter |
ind()
Indent with zero offset and zero width.
|
Layouter |
ind(int width,
int offset)
Indent to a current indentation level if surrounding block is
broken.
|
Layouter |
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 |
nl()
Print a break with zero offset and large width.
|
private Layouter.StreamToken |
pop()
Pop the topmost Token from the delimStack
|
private Layouter.StreamToken |
popBottom()
Remove and return the token from the bottom of the
delimStack
|
Layouter |
pre(java.lang.String s)
Layout prefromated text.
|
Layouter |
print(java.lang.String s)
Output text material.
|
private void |
push(Layouter.StreamToken t)
Push an OpenBlockToken or BreakToken onto the delimStack
|
private Layouter.StreamToken |
top()
Return the top of the delimStack, without popping it.
|
private Backend back
private Printer out
private java.util.List<Layouter.StreamToken> stream
private java.util.List<Layouter.StreamToken> delimStack
OpenBlockToken
s and
BreakToken
s in stream
, waiting for
their size to be determined.private int totalSize
private int totalOutput
out
.
Subtract this from totalOutput
and you get the space
needed to print what is still buffered in stream
private int largeSize
private int defaultInd
public static final int DEFAULT_LINE_WIDTH
public static final int DEFAULT_INDENTATION
public Layouter(Backend back, int indentation)
Backend
and has the given default indentation.back
- the Backendindentation
- the default indentationpublic Layouter(java.io.Writer writer)
WriterBackend
.
The line width is taken to be DEFAULT_LINE_WIDTH
, and the
default indentation DEFAULT_INDENTATION
.writer
- the Writer
the Backend is going to usepublic Layouter(java.io.Writer writer, int lineWidth)
WriterBackend
.
The default indentation is taken from DEFAULT_INDENTATION
.writer
- the Writer
the Backend is going to uselineWidth
- the maximum lineWidth the Backend is going to usepublic Layouter(java.io.Writer writer, int lineWidth, int indentation)
WriterBackend
.writer
- the Writer
the Backend is going to uselineWidth
- the maximum lineWidth the Backend is going to useindentation
- the default indentationpublic Layouter print(java.lang.String s) throws java.io.IOException
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.s
- the String to print.java.io.IOException
public Layouter begin(boolean consistent, int indent)
consistent
is set, breaks
are either all broken or all not broken. The indentation
level is increased by indent
.consistent
- true
for consistent blockindent
- increment to indentation levelpublic Layouter end() throws java.io.IOException
java.io.IOException
public Layouter brk(int width, int offset) throws java.io.IOException
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
.width
- space to insert if not brokenoffset
- offset relative to current indentation leveljava.io.IOException
public Layouter ind(int width, int offset) throws java.io.IOException
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.width
- space to insert if not brokenoffset
- offset relative to current indentation leveljava.io.IOException
public Layouter mark(java.lang.Object o)
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.o
- an object to be passed through to the backend.public Layouter flush() throws java.io.IOException
flush
or
close()
then.java.io.IOException
public void close() throws java.io.IOException
Backend.close()
method of the backend is called, which
closes any open I/O streams, etc.java.io.IOException
public Layouter beginI()
public Layouter beginC()
public Layouter beginI(int indent)
indent
to the indentation level.indent
- the indentation for this blockpublic Layouter beginC(int indent)
indent
to the indentation level.indent
- the indentation for this blockpublic Layouter begin(boolean consistent)
consistent
- true
for consistent blockpublic Layouter brk(int width) throws java.io.IOException
width
- space to insert if not brokenjava.io.IOException
public Layouter brk() throws java.io.IOException
java.io.IOException
public Layouter nl() throws java.io.IOException
java.io.IOException
public Layouter ind() throws java.io.IOException
java.io.IOException
public Layouter pre(java.lang.String s) throws java.io.IOException
s
(separated by \n) gets printed as a string and newlines become
forced breaks.s
- the pre-formatted stringjava.io.IOException
private void push(Layouter.StreamToken t)
private Layouter.StreamToken pop()
private Layouter.StreamToken popBottom()
private Layouter.StreamToken top()
private void enqueue(Layouter.StreamToken t)
private Layouter.StreamToken dequeue()
private void advanceLeft() throws java.io.IOException
stream to out
as long
as there are tokens left and their size is known.
java.io.IOException