Copyright © 2004 Stoney Jackson, <hjackson@wnec.edu>
Distributed under the LGPL
The Peephole Framework is an application programming interface (API) for building pretty printers and text-based visualizations that use our peephole pretty printing algorithm. This document is an overview of the major components in the framework and how they can be used to create pretty printers and visualizations.
To use the framework, you’ll need to place edu.ucdavis.pp_1.2.0.jar in your classpath. The source code is available in edu.ucdavis.pp_1.2.0-src.jar. To examine the source, extract this file using the jar utility that ships with Java SDK.
◦pp: This and its subpackages contain the core of the peephole pretty printing API.
◦toolbox: This and its subpackages provide some basic data structures and testing tools for our peephole pretty printer.
◦spf: This and its subpackages contain the core for the Source Projection Framework (i.e., visualization API).
◦edu.ucdavis.pp: This and its subpackages contain the peephole plug-in for Eclipse. You may want to refer to this for an example of how to use the peephole pretty printing framework.
◦org.eclipse.jdt: This and its subpackages are also part of our peephole plug-in, but these classes work much more closely with JDT classes than do those in edu.ucdavis.pp.
◦jjtraveler: This and its subpackages is a library for tree traversals and transformations. It is an external library that is maintained as part of the XT transformation tools, and is distributed under the GPL. Our pretty print algorithm relies on this library to perform tree traversals over its layout structures (which are trees).
3. The framework’s components
A complete peephole visualization tool performs several tasks using several components to format a peephole of code (see Figure 1). The basic components are as follows:
◦pp.PeepholePrettyPrinter: Provides pretty printing facilities for formatting peepholes from Lots.
◦pp.lot.Lot: A Lot encodes the possible layouts for a particular document or part of a document.
◦pp.lot.LotFactory: Factory for constructing Lots.
◦pp.layout.LayoutElement: A LayoutElement is the smallest displayable unit that can be rendered by a Renderer.
◦pp.layout.LayoutFactory: Factory for constructing LayoutElements from Lots.
◦pp.layout.Renderer: Renders LayoutElements to a display.
The use of these components by an application to pretty print peepholes, shown in Figure 1, is as follows. First, the application creates a layout options tree (Lot) from some source code using a LotFactory. Next, it determines a focal node in the Lot and the width of the target peephole, and gives this information to a PeepholePrettyPrinter. Then, the application formats the peephole by calling the print methods on the PeepholePrettyPrinter. The PeepholePrettyPrinter uses a LayoutFactory and a Renderer to format the Lot. The LayoutFactory is responsible for constructing LayoutElements from Lot nodes, and for supplying width measurements of Lot nodes. The Renderer is used by the PeepholePrettyPrinter render the LayoutElements to a display environment. Each of these steps and their related components are described in greater detail below.

To create a Lot, an application instantiates a LayoutFactory and uses it to construct a Lot from source code. A Lot defines the possible layouts for a document. Thus, when an application creates a Lot for a document, it is specifying its format.
Getting an instance of a LotFactory is very simple:
LotFactory f = new LotFactory();
Once an application has a LotFactory, it can begin to construct Lots. Every Lot must be constructed from a LotFactory, and every Lot in the same Lot tree must be constructed from the same LotFactory. Lot nodes have a back-reference to the LotFactory that created them. This is needed at times to dynamically create other Lot nodes during formatting.
A LotFactory has several primitive methods for constructing a Lot. They are:
◦Lot nil(): creates a Lot node representing nothing. Useful as a placeholder.
◦Lot line(): creates a Lot node representing a new line that can be flattened into a space.
◦Lot bline(): creates a Lot node representing a new line that can be flattened into nil Lots.
◦Lot hline(): creates a Lot node representing a new line that cannot be flattened.
◦Lot text(String s): creates a Lot node representing the string s. s must not contain new line characters. Use line(), bline(), or hline() to introduce new lines.
◦Lot nest(int k, Lot lot): creates a Lot that indents each line in lot by k spaces.
◦Lot cat(Lot left, Lot right): creates a Lot node that is the concatenation of left and right Lots.
◦Lot choice(Lot left, Lot right): creates a Lot that has a choice between two Lots. The first line of text produced by the left must be at least as wide as that of the right.
Using these methods, one can express any format that is expressible in our framework. The other Lot constructing methods exist for convenience; all except for link(Extension e), which exists to preserve space efficiency through lazy construction of Lot nodes. Some of the other Lot constructing methods of note are:
◦Lot link(Extension e): creates a lazy, dynamically expanding Lot that generates Lot nodes according to e.
◦Lot flatten(Lot lot): creates a Lot identical to lot, but with each line replaced by a space and each bline replaced by an nil Lot. Flatten is implemented using lazy evaluation and link.
◦Lot group(Lot lot): creates a choice between a flattened version of lot and lot itself [i.e., group(lot) = choice(flatten(lot), lot)]. Essentially, this creates an all-or-nothing policy for a group of lines and blines.
◦Lot fill(List lots): creates Lot that places as many Lots in lots on each line (separated by spaces) that will fit before starting a new line. Essentially, this is a “paragraph fill”. Fill is implemented using lazy evaluation and link.
Each Lot node also contains a reference to an arbitrary “context” Object. This allows an application to associate an arbitrary Object with a Lot node, and gives the application the ability to retrieve that Object given a Lot node. For example, the Peepeye Browser associates with a Lot node the abstract syntax tree (AST) node that the Lot node represents. Later, a Lot’s AST node is retrieved to calculate a fisheye value.
To help an application associate context Objects with Lot node, a LotFactory provides an internal stack for tracking context Objects. The current context Object is inserted into each Lot it builds. The current context Object is set using the following methods:
◦void pushContext(Object o): o is pushed onto the context stack and become the current context.
◦Object popContext(): restores the current context to what it was before the last call to pushContext(o).
An application implementing a visualization technique that wishes to use the Peephole Framework’s Source Projection Facilities (SPF) should use an spf.SpfLotFactory instead of a the basic LotFactory. An SpfLotFactory is a LotFactory, except that context objects must be of type spf.vmap.SubjectValue. This will be discussed further in the section on Creating a visualization technique.
Determining the focus and dimensions of a peephole is application specific. The Peepeye Browser let’s users select a focus from a javax.swing.JTree, and determines the dimensions of the peephole from a javax.swing.JScrollPane’s viewport. Getting the dimensions from a JScrollPane’s viewport is fairly straight forward (see Java API documentation for JScrollPane). Getting the focus from a JTree is a bit more complicated, and is described next.
The Peepeye Browser gets the AST for a file from an XML file produced by JavaML. Therefore internally, AST nodes are XML DOM nodes. The browser creates a Lot from the AST. While doing so, it remembers which Lot nodes where created from each AST node, so that the Lot can be recalled for a given AST node. Then, the browser adds the AST nodes to the JTree. When a user selects a focus from the JTree, the browser extracts the AST node, looks up the corresponding Lot node.
6.Creating and using a PeepholePrettyPrinter
The PeepholePrettyPrinter gives control over formatting peepholes. Formatting a peephole is a 3 step process:
1) Construct a PeepholePrettyPrinter.
2) Set the focus and width of the peephole.
3) Call print and state inspection methods to format the peephole.
4) Get the results from the Renderer inside the PeepholePrettyPrinter.
Lot focus;
double peepholeWidth;
double peepholeHeight;
// 1) Construct a PeepholePrettyPrinter
PeepholePrettyPrinter printer = new PeepholePrettyPrinter();
// 2) Set the focus and width of the peephole.
focus = …
peepholeWidth = …
printer.setFocus(focus);
printer.setWidth(peepholeWidth);
// 3) Call print and state inspection methods to format the peephole.
// Formats a peephole with the focus near the top.
peepholeHeight = …
double oldHeight;
double newHeight;
do {
oldHeight = printer.getHeight();
printer.printDown();
} while ( newHeight > oldHeight && newHeight < peepholeHeight );
// 4) Get results
String result = (String) printer.getRenderer().getResult();
System.our.println(result);
printer = new PeepholePrettyPrinter();
printer.setFocus(focus);
printer.setWidth(peepholeWidth);
printer.setLayoutFactory(…);
printer.setRenderer(…);
// Format a peephole width the focus near the middle.
// Format top half.
do {
oldHieght = printer.getHeight();
printer.printUp();
newHeight = printer.getHeight();
} while ( newHeight < oldHeight && newHeight < peepholeHeight / 2 );
// Format bottom half.
do {
oldHeight = printer.getHeight();
printer.printDown();
newHeight = printer.getHeight();
} while ( newHeight < oldHeight && newHeight < peepholeHeight );
result = (String) printer.getRenderer().getResult();
System.our.println(result);
7. Adapting the PeepholePrettyPrinter to a display environment
By default the PeepholePrettyPrinter formats a Lot into a plain String. This is controlled by a LayoutFactory and a Renderer. A LayoutFactory provides measurements about Lot nodes to the PrettyPrintAlgorithm during pretty printing. These measurements depend on the medium being rendered to. Similarly, a Renderer is responsible for rendering LayoutElements to a display medium. To adapt a PeepholePrettyPrinter to a display environment, a custom LayoutFactory and Renderer must be created.
To adapt a LayoutFactory, extend LayoutFactory and override the getHieght, getWidth, and isElided methods. You can also override the factory methods, which begin with “to”, to construct custom LayoutElements. To create a custom Renderer for a display environment, extend Renderer and implement all abstract methods.
Often you’ll want to adapt a PeepholePrettyPrinter to a display environment that supports common textual attributes (e.g., font, font size, bold, italic, underline, colors, etc.). To simplify the management of attributes, and to allow construction of useful visualizations that use these attributes, the Peephole Framework provides a Source Projection Facility (SPF). Adaptation to these types of environments is described in Creating a visualization technique.
Also included in the Peephole Framework is an adaptation to Java’s Swing environment. This adaptation is based uses SPF as well, thus you can construct source code visualizations. To use this adaptation,
1) Instantiate and initialize an spf.view.swing.AttributeMappingContext.
2) Instantiate an spf.view.swing.SwingOracle.
3) Instantiate a javax.swing.StyledDocument, which will receive formatted text.
4) Instantiate an spf.view.swing.SwingKit.
5) Instantiate, initialize, and use a pp.PeepholePrettyPrinter to format peepholes.
// 1) Instantiate and initialize an AttributeMappingContext.
AttributeMappingContext mapping = new AttributeMappingContext();
// 2) Instantiate a SwingOracle.
SwingOracle oracle = new SwingOracle(mapping);
// 3) Instantiate a StyledDocument.
StyledDocument document = new DefaultStyledDocument();
// 4) Instantiate a SwingKit.
SwingKit kit = new SwingKit(oracle, document) ;
// 5) Instantiate, initialize, and use a PeepholePrettyPrinter to format peepholes.
PeepholePrettyPrinter printer = new PeepholePrettyPrinter();
printer.setLayoutFactory(kit.getFactory());
printer.setRenderer(kit.getRenderer());
The Swing adaptation is built on top of SPF. Besides making visualizations easy to create, SPF also simplifies adaptation to different display environments. To create an adaptation using SPF, implement the spf.SpfOracle interface which provides width, height, and elision information for spf.vmap.SubjectValues. A SubjectValue represents the context under which some text is being rendered. The SubjectValue is used by the SpfOracle to determine the visual attributes of the text. For example, in the Peepeye Browser, SubjectValues are wrapped DOM (an XML object-oriented representation) nodes that represent AST nodes. Thus, a text’s attributes are determined by which AST node they represent. This could be used, say, to underline all keywords.