package Sequenic.P2 ;
import java.util.* ;
/**
* Simple library for pretty printing text. A PP object represents
* essentially a list of strings. The method {@link #render render} can be used
* to render this object to a single string formatted in a certain
* way.
*
*
Logically the strings to be pretty printed are grouped
* hierarchically into groups that have to be printed side by side,
* and groups that have to be printed above the other.
*
*
*
Internally, PP is structured recursively. It has
* right and under pointers that again point to PP
* objects. The PP at 'right' is to be printed right to the current
* PP, and the PP at 'under' is to be printed below the current
* PP. There is also a field line which contains a single
* string (which should not contain a newline). It represents the
* single line of text belonging to the currect PP.
*
*
The field indent specifies an indentation offset for the PP (and
* all PP's below it).
*
*
In addition to the redering method, this library also provides
* some methods for constructing PPs.
*
*
Example: see source code.
*
* @author Wishnu Prasetya (wishnu@cs.uu.nl)
*
*/
public class PP {
/**
* Offset indentation for this PP.
*/
private int indent = 0 ;
/**
* A single line of string which is the content of this PP.
*/
private String line = "" ;
/**
* The PP to be printed right to this PP.
*/
private PP right = null ;
/**
* The PP to be printed below this PP.
*/
private PP under = null ;
/**
* Creating an empty PP.
*/
public PP() { }
/**
* Accumulate reachable nodes in visited.
*/
private void reachableNodes(Collection visited) {
if (visited.contains(this)) return ;
visited.add(this) ;
if (right != null) right.reachableNodes(visited) ;
if (under != null) under.reachableNodes(visited) ;
}
private Collection reachableNodes() {
Collection U = new LinkedList() ;
reachableNodes(U) ;
return U ;
}
/**
* Return true if there is some node in pp which is reachable from this PP.
*/
private boolean canReach(PP pp) {
Collection U = reachableNodes() ;
Collection V = pp.reachableNodes() ;
V.retainAll(U) ;
return ! V.isEmpty() ;
}
private boolean isCyclic(Collection visited) {
if (visited.contains(this)) return false ;
visited.add(this) ;
if (right != null && right.isCyclic(visited)) return true ;
if (under != null && under.isCyclic(visited)) return true ;
visited.remove(this);
return false ;
}
private boolean isCyclic() {
return isCyclic(new LinkedList()) ;
}
/**
* Class invariant, requiring this PP to be non-cyclic.
*/
public boolean classinv(){ return ! isCyclic() ; }
private String renderWorker(String output, int horOffset, boolean aside) {
if (indent<0) indent = 0 ; // ignore negative indentation
if (line != null) {
if (aside) output = output + space(indent) + line ;
else output = output + space(horOffset + indent) + line ;
}
if (right != null)
output = right.renderWorker(output, horOffset + indent + line.length(), true) ;
if (under != null) {
output = output + "\n" ;
output = under.renderWorker(output,horOffset + indent,false) ;
}
return output ;
}
/**
* To render this PP to a formatted string. The parameter is the
* initial indentation.
*/
public String render(int horOffset) {
String output = "" ;
return renderWorker(output,horOffset,false) ;
}
/**
* A specification.
*/
public String render_spec(int horOffset) {
assert ! isCyclic() : "PRE" ;
return render(horOffset) ;
}
/**
* To make a PP containing a single line of string as its only content.
*/
static public PP text(String s) {
PP pp = new PP() ;
pp.line = s ;
return pp ;
}
private static String space(int n) {
String s = "" ;
for ( ; 0