-
-
Save ethanve/f285b876e312143b2867 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package ParseTree; | |
| import javax.swing.*; | |
| import java.awt.*; | |
| import java.awt.geom.Ellipse2D; | |
| import java.util.*; | |
| import java.util.List; | |
| public class Node<T> { | |
| private double width; | |
| private double height; | |
| private static final int padding = 50; | |
| private T data; | |
| private Node<T> parent; | |
| private List<Node<T>> children; | |
| private Point location; | |
| private Color color; | |
| /** | |
| * Construct a new Node. | |
| * | |
| * @param data The data the node contains. | |
| */ | |
| public Node(T data) { | |
| this.data = data; | |
| parent = null; | |
| children = new ArrayList<>(); | |
| color = UIManager.getColor("Panel.background"); | |
| } | |
| /** | |
| * Add a node to this Node at the end of the list of children. | |
| * | |
| * @param n The node. | |
| */ | |
| public void addChild(Node<T> n) { | |
| children.add(n); | |
| n.parent = this; | |
| } | |
| /** | |
| * @return True if the node is a leaf, false otherwise. | |
| */ | |
| public boolean isLeaf() { | |
| return children.size() == 0; | |
| } | |
| /** | |
| * A list of the children of this node. | |
| * | |
| * @return | |
| */ | |
| public List<Node<T>> getChildren() { | |
| return children; | |
| } | |
| /** | |
| * @return The parent of the node. | |
| */ | |
| public Node<T> getParent() { | |
| return parent != null ? parent : null; | |
| } | |
| /** | |
| * Get a child at a particular index. | |
| * | |
| * @param i The index. | |
| * @return The node. | |
| */ | |
| public Node<T> childAtIndex(int i) throws IndexOutOfBoundsException { | |
| return children.get(i); | |
| } | |
| /** | |
| * Gets the data of the node | |
| * | |
| * @return The data of the node. | |
| */ | |
| public T getData() { | |
| return data; | |
| } | |
| /** | |
| * Gets the location of the node in the panel | |
| * | |
| * @return The data of the node. | |
| */ | |
| public Point getLocation() { | |
| return location; | |
| } | |
| /** | |
| * Sets the location of the node in the panel | |
| * | |
| * @return The data of the node. | |
| */ | |
| public void setLocation(Point location) { | |
| this.location = location; | |
| } | |
| /** | |
| * Gets the height of the node in the panel | |
| * | |
| * @return The data of the node. | |
| */ | |
| public double getHeight() { | |
| return height; | |
| } | |
| /** | |
| * Gets the width of the node in the panel | |
| * | |
| * @return The data of the node. | |
| */ | |
| public double getWidth() { | |
| return width; | |
| } | |
| /** | |
| * Gets the padding of the node in the panel | |
| * | |
| * @return The data of the node. | |
| */ | |
| public int getPadding() { | |
| return padding; | |
| } | |
| /** | |
| * Sets the color of the node in the panel | |
| * | |
| * @return The data of the node. | |
| */ | |
| public void setColor(Color color) { | |
| this.color = color; | |
| } | |
| /** | |
| * Gets the location of the node in the panel | |
| * | |
| * @return The data of the node. | |
| */ | |
| public Color getColor() { | |
| return color; | |
| } | |
| /** | |
| * Sets the height of the node in the panel | |
| * | |
| * @return The data of the node. | |
| */ | |
| public void setHeight(double height) { | |
| this.height = height; | |
| } | |
| /** | |
| * Sets the height of the node in the panel | |
| * | |
| * @return The data of the node. | |
| */ | |
| public void setWidth(double width) { | |
| this.width = width; | |
| } | |
| /** | |
| * Gets the total width of the node's children | |
| * | |
| * @return total width of the node's children | |
| */ | |
| public double getLevelWidth() { | |
| if (children.size() <= 1) { | |
| return 0; | |
| } | |
| return children.stream().mapToDouble(n -> n.getWidth() + n.getPadding()).sum(); | |
| } | |
| /** | |
| * Gets the width of the node's children ending with end | |
| * | |
| * @param end the ending | |
| * @return Width of the node's children | |
| */ | |
| public int getLevelWidth(int end) { | |
| if (children.size() <= 1) { | |
| return 0; | |
| } | |
| int sum = 0; | |
| for (int i = 0; i < end; i++) { | |
| Node n = children.get(i); | |
| sum += (n.getWidth() + n.getPadding()); | |
| } | |
| return sum; | |
| } | |
| /** | |
| * Paints the node in the panel | |
| * | |
| * @return The data of the node. | |
| */ | |
| public void paint(JComponent parent, Graphics2D g2d) { | |
| FontMetrics fm = g2d.getFontMetrics(); | |
| double width = getWidth(); | |
| double height = getHeight(); | |
| Point p = getLocation(); | |
| double x = p.getX(); | |
| double y = p.getY(); | |
| Ellipse2D node = new Ellipse2D.Double(x, y, width, height); | |
| g2d.setColor(getColor()); | |
| g2d.fill(node); | |
| g2d.setColor(Color.GRAY); | |
| g2d.draw(node); | |
| String text = this.getData().toString(); | |
| x = x + ((width - fm.stringWidth(text)) / 2); | |
| y = y + (((height - fm.getHeight()) / 2) + fm.getAscent()); | |
| g2d.drawString(text, (int) x, (int) y); | |
| } | |
| public String toString() { | |
| return this.getData().toString() + " @ " + getLocation(); | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package ParseTree; | |
| import LexicalAnalyzer.Token; | |
| import javax.swing.*; | |
| import java.awt.*; | |
| import java.awt.geom.Line2D; | |
| public class ParseTree<T> { | |
| protected Node<T> root; | |
| protected double zoomFactor = .5; | |
| /** | |
| * Set the root of the tree. | |
| * | |
| * @param n The node. | |
| */ | |
| public void setRoot(Node<T> n) { | |
| root = n; | |
| } | |
| /** | |
| * @return The root of the tree. | |
| */ | |
| public Node<T> getRoot() { | |
| return root; | |
| } | |
| public void printTree() { | |
| EventQueue.invokeLater(() -> { | |
| try { | |
| UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); | |
| } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { | |
| } | |
| JFrame frame = new JFrame("Parse Tree Visualization"); | |
| ParseTreePane panel = new ParseTreePane(); | |
| frame.add(panel, BorderLayout.CENTER); | |
| JButton buttonMinus = new JButton(" Zoom Out (-) "); | |
| JButton buttonPlus = new JButton(" Zoom In (+) "); | |
| buttonMinus.addActionListener(e -> { | |
| zoomFactor -= 0.1; | |
| buttonMinus.setEnabled(zoomFactor > 0.1); | |
| buttonPlus.setEnabled(zoomFactor < 4.0); | |
| panel.doLayout(); | |
| }); | |
| buttonPlus.addActionListener(e -> { | |
| zoomFactor += 0.1; | |
| buttonMinus.setEnabled(zoomFactor > 0.1); | |
| buttonPlus.setEnabled(zoomFactor < 4.0); | |
| panel.doLayout(); | |
| }); | |
| Box box = Box.createHorizontalBox(); | |
| box.add(Box.createHorizontalGlue()); | |
| box.add(buttonPlus); | |
| box.add(Box.createHorizontalStrut(20)); | |
| box.add(buttonMinus); | |
| box.add(Box.createHorizontalGlue()); | |
| frame.add(box, BorderLayout.SOUTH); | |
| Toolkit tk = Toolkit.getDefaultToolkit(); | |
| int xSize = ((int) tk.getScreenSize().getWidth()); | |
| int ySize = ((int) tk.getScreenSize().getHeight()); | |
| panel.setPreferredSize(new Dimension(xSize, ySize)); | |
| JScrollPane scroll = new JScrollPane(panel); | |
| frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); | |
| frame.setSize(500, 500); | |
| frame.add(scroll); | |
| frame.setLocationRelativeTo(null); | |
| frame.setVisible(true); | |
| }); | |
| } | |
| public class ParseTreePane extends JPanel { | |
| public ParseTreePane() { | |
| super(null); | |
| setNodeDimensions(root); | |
| } | |
| protected void layoutNode(Node<T> node, int x, int y) { | |
| if (node != null) { | |
| node.setLocation(new Point(x, y)); | |
| //We get the level width by adding each node's width | |
| // as well as the right padding. We then divide it by | |
| // 2, negate the parent's X value by that (start variable) | |
| // and start printing the level at that location | |
| // We then keep on adding the printed node's width | |
| // to the next node | |
| double levelWidth = node.getLevelWidth(); | |
| int children = node.getChildren().size(); | |
| int start = (x - (int) Math.ceil(levelWidth / 2)); | |
| for (int i = 0; i < children; i++) { | |
| Node n = node.childAtIndex(i); | |
| int childX = start + node.getLevelWidth(i); | |
| int childY = (int) Math.ceil(y + node.getPadding() + node.getHeight()); | |
| layoutNode(n, childX, childY); | |
| } | |
| } | |
| } | |
| /** | |
| * On init, we set all node dimensions | |
| * | |
| * @param node the node | |
| */ | |
| public void setNodeDimensions(Node<T> node) { | |
| FontMetrics fm = getFontMetrics(getFont()); | |
| double width = (fm.stringWidth(node.getData().toString()) + 30) * zoomFactor; | |
| double height = (fm.getHeight() * 2) * zoomFactor; | |
| node.setWidth(width); | |
| node.setHeight(height); | |
| node.getChildren().forEach(this::setNodeDimensions); | |
| } | |
| @Override | |
| public void doLayout() { | |
| setNodeDimensions(root); | |
| setFont(new Font("TimesRoman", Font.PLAIN, (int) Math.floor(12 * zoomFactor))); | |
| int x = getWidth() / 2; | |
| int y = (int) root.getHeight() + 10; | |
| root.setLocation(new Point(x, y)); | |
| layoutNode(root, x, y); | |
| } | |
| @Override | |
| protected void paintComponent(Graphics g) { | |
| super.paintComponent(g); | |
| if (root != null) { | |
| Graphics2D g2d = (Graphics2D) g.create(); | |
| paintConnectors(root, g2d); | |
| paintNode(root, g2d); | |
| g2d.dispose(); | |
| } | |
| } | |
| protected void paintNode(Node<T> node, Graphics2D g2d) { | |
| if (node != null && node.getLocation() != null) { | |
| node.paint(this, g2d); | |
| for (Node n : node.getChildren()) { | |
| paintNode(n, g2d); | |
| } | |
| } | |
| } | |
| protected void paintConnectors(Node<T> node, Graphics2D g2d) { | |
| if (node != null && node.getLocation() != null) { | |
| Node parent = node.getParent(); | |
| if (parent != null) { | |
| g2d.setColor(Color.GRAY); | |
| if (parent.getLocation() != null && node.getLocation() != null) { | |
| double parentX = parent.getLocation().getX() + (parent.getWidth() / 2); | |
| double parentY = parent.getLocation().getY(); | |
| double nodeX = node.getLocation().getX() + (node.getWidth() / 2); | |
| double nodeY = node.getLocation().getY(); | |
| g2d.draw(new Line2D.Double(parentX, parentY, nodeX, nodeY)); | |
| } | |
| } | |
| for (Node n : node.getChildren()) { | |
| paintConnectors(n, g2d); | |
| } | |
| } | |
| } | |
| } | |
| } | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment