package brsVisitor; import brs.*; import javax.swing.*; import java.awt.*; /** * This class acts as an MVC view adapter for a BiTree to display the tree on a JComponent. The JComponent is assumed to have a null layout. * The display process clears the JComponent of all other components and then lays out JLabels on it where each label's text is the String representation * of a node's data. Note: If a JScrollPane is used to hold the JComponent, it must be validated after running showTree() method. * @dependency brsVisitor.BRSDisplayAdapter$RowColIdx uses */ public class BRSDisplayAdapter { /** * The offset from the top in pixels of the entire display. */ private int rowOffset = 0; /** * The offset from the left in pixels of the entire display. */ private int colOffset = 0; /** * The width of the label to make. */ private int labelWidth = 100; /** * The height of the label to make. */ private int labelHeight = 30; /** * The maximum depth, in rows, of the tree to be displayed. This quantitiy is calculated each time the tree is displayed. */ private int maxDepth = 0; /** * The JComponent upon which the BiTree is to be shown. */ private JComponent container; /** * An anonymous inner class IBRSAlgo that is used to traverse the BiTree, calling the showData() method at every non-null node. */ private IVisitor displayVisitor = new IVisitor() { public Object emptyCase(BiTree host, Object param) { return (null); } public final Object nonEmptyCase(BiTree host, Object param) { showData( host.getRootDat(), (RowColIdx) param); host.getLeftSubTree().execute(this, ((RowColIdx) param).nextLeft()); host.getRightSubTree().execute(this,((RowColIdx) param).nextRight()); return(null); } }; private BRSGetMaxDepthVisitor getMaxDepthVisitor = new BRSGetMaxDepthVisitor(); /** * This class is a "carrier" class that holds the row and column index for a node in a BiTree. The rows are numbered from 0 at the first row. * The columns are numbered from 0 from the left. This class is also a factory for the RowColIdx needed for the left and right sub-trees * of the present node. This is a private inner class of BRSDisplayAdapter. */ class RowColIdx { /** * The stored column index. */ int colIdx; int rowIdx; /** * Constructor for the class. * @param rowIdx The row index * @param colIdx The column index */ public RowColIdx(int rowIdx, int colIdx) { this.rowIdx = rowIdx; this.colIdx = colIdx; } /** * Accessor method for the row index. * @return */ int getRowIdx() { return rowIdx; } /** * Accessor method for the row index. * @return */ int getColIdx() { return colIdx; } /** * Factory method for the RowColIdx instance needed for the left sub-tree. The new (row, col) indices are (row+1, 2*col) * @return A new RowColIdx instance */ RowColIdx nextLeft() { return new RowColIdx(rowIdx+1,colIdx*2); } /** * Factory method for the RowColIdx instance needed for the left sub-tree. The new (row, col) indices are (row+1, 2*col+1) * @return A new RowColIdx instance */ RowColIdx nextRight() { return new RowColIdx(rowIdx+1,colIdx*2+1); } } /** * Constructor for the class that defaults the label width x height to 100 x 30 * @param container The JComponent to display the BiTree on. * @param rowOffset The vertical offset of the display in pixels. * @param colOffset The horizontal offset of the display in pixels. */ private BRSDisplayAdapter(JComponent container, int rowOffset, int colOffset) { this.container = container; this.rowOffset = rowOffset; this.colOffset = colOffset; } /** * Constructor for the class that also allows the label width and height to be specified.. * @param container The JComponent to display the BiTree on. * @param rowOffset The vertical offset of the display in pixels. * @param colOffset The horizontal offset of the display in pixels. * @param labelWidth The width of the labels * @param labelHeight The hieght of the labels. */ public BRSDisplayAdapter(JComponent container, int rowOffset, int colOffset, int labelWidth, int labelHeight) { this(container, rowOffset, colOffset); this.labelWidth = labelWidth; this.labelHeight = labelHeight; } /** * Calculates the bounds of the label for a node, given its row and column index. * @param rowColIdx The row and column index of the node. * @return A bounds Rectangle object. */ private Rectangle getBounds(RowColIdx rowColIdx) { int row = rowOffset + rowColIdx.getRowIdx()*labelHeight; int col = colOffset + ( (labelWidth*(1<<(maxDepth-1))) // = total width *(2*rowColIdx.getColIdx()+1)/(1<<(rowColIdx.getRowIdx()+1)) // = fractional center position ) - labelWidth/2; // = label width compensation return new Rectangle(col, row,labelWidth, labelHeight); } /** * Creates a JLabel whose text is the String representation of the supplied data. The bounds of * the label are calculated from the supplied RowColIdx. * @param data The data to be displayed. * @param rowColIdx The row and column index of the data's node. */ private void showData(Object data, RowColIdx rowColIdx) { JLabel label = new JLabel(); label.setHorizontalAlignment(SwingConstants.CENTER); label.setText(data.toString()); label.setBounds(getBounds(rowColIdx)); container.add(label); } /** * Displays a BiTree on a JComponent by clearing the JComponent of all other components and then laying out a JLabel for every non-null node of the tree. * @param tree */ public void displayTree(BiTree tree) { container.removeAll(); maxDepth = ((Integer)tree.execute(getMaxDepthVisitor,null)).intValue(); container.setPreferredSize( // to get scroll bars to work properly. new Dimension(colOffset+(1<<(maxDepth-1))*labelWidth, rowOffset+maxDepth*labelHeight)); tree.execute(displayVisitor, new RowColIdx(0,0)); container.validate(); } }