/** * Copyright (c) 2016 - 2018 Syncleus, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.aparapi.internal.tool; import java.awt.BasicStroke; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Polygon; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.Shape; import java.awt.Stroke; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseWheelEvent; import java.awt.geom.AffineTransform; import java.awt.geom.CubicCurve2D; import java.awt.image.BufferedImage; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.JToggleButton; import javax.swing.JToolBar; import javax.swing.SpringLayout; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import com.aparapi.Config; import com.aparapi.internal.exception.AparapiException; import com.aparapi.internal.exception.ClassParseException; import com.aparapi.internal.instruction.Instruction; import com.aparapi.internal.instruction.InstructionSet.CompositeInstruction; import com.aparapi.internal.model.ClassModel; import com.aparapi.internal.model.Entrypoint; import com.aparapi.internal.model.MethodModel; import com.aparapi.internal.tool.InstructionViewer.Form.Check; import com.aparapi.internal.tool.InstructionViewer.Form.Template; import com.aparapi.internal.tool.InstructionViewer.Form.Toggle; public class InstructionViewer implements Config.InstructionListener{ public static abstract class Form<T extends Form.Template> { public @interface OneOf { String label(); String[] options(); } public interface Template{ } @Retention(RetentionPolicy.RUNTIME) public @interface List { Class<?> value(); } @Retention(RetentionPolicy.RUNTIME) public @interface Toggle { String label(); String on(); String off(); } @Retention(RetentionPolicy.RUNTIME) public @interface Check { String label(); } public final static int INSET = 5; private final T template; JPanel panel; private final SpringLayout layout = new SpringLayout(); void setBoolean(Field _field, boolean _value) { try { _field.setBoolean(template, _value); } catch (final IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (final IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } } boolean getBoolean(Field _field) { try { return (_field.getBoolean(template)); } catch (final IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (final IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } return (false); } Object get(Field _field) { try { return (_field.get(template)); } catch (final IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (final IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } return (null); } public Form(T _template) { template = _template; panel = new JPanel(layout); JComponent last = panel; final Map<Field, JLabel> fieldToLabelMap = new LinkedHashMap<Field, JLabel>(); Field fieldWithWidestLabel = null; int fieldWithWidestLabelWidth = 0; // we need to know the widest Label so create the labels in one pass for (final Field field : template.getClass().getFields()) { String labelString = null; final Check checkAnnotation = field.getAnnotation(Check.class); if (checkAnnotation != null) { labelString = checkAnnotation.label(); } else { final Toggle toggleAnnotation = field.getAnnotation(Toggle.class); if (toggleAnnotation != null) { labelString = toggleAnnotation.label(); } } if (labelString != null) { final JLabel label = new JLabel(labelString); panel.add(label); fieldToLabelMap.put(field, label); if (labelString.length() > fieldWithWidestLabelWidth) { fieldWithWidestLabel = field; fieldWithWidestLabelWidth = labelString.length(); } } } for (final Field field : fieldToLabelMap.keySet()) { layout.putConstraint(SpringLayout.NORTH, fieldToLabelMap.get(field), INSET, (last == panel) ? SpringLayout.NORTH : SpringLayout.SOUTH, last); layout.putConstraint(SpringLayout.WEST, fieldToLabelMap.get(field), INSET, SpringLayout.WEST, panel); JComponent newComponent = null; if (field.getType().isAssignableFrom(Boolean.TYPE)) { final Field booleanField = field; final Toggle toggleAnnotation = field.getAnnotation(Toggle.class); if (toggleAnnotation != null) { final String toggleButtonOnLabel = toggleAnnotation.on(); final String toggleButtonOffLabel = toggleAnnotation.off(); final String toggleButtonLabel = getBoolean(field) ? toggleButtonOnLabel : toggleButtonOffLabel; final JToggleButton toggleButton = new JToggleButton(toggleButtonLabel, getBoolean(field)); toggleButton.addActionListener(new ActionListener(){ @Override public void actionPerformed(ActionEvent _actionEvent) { final JToggleButton toggleButton = ((JToggleButton) _actionEvent.getSource()); // System.out.println("toggle toggle "+toggleButton); if (toggleButton.getText().equals(toggleButtonOnLabel)) { toggleButton.setText(toggleButtonOffLabel); setBoolean(booleanField, false); } else { toggleButton.setText(toggleButtonOnLabel); setBoolean(booleanField, true); } sync(); } }); newComponent = toggleButton; } final Check checkAnnotation = field.getAnnotation(Check.class); if (checkAnnotation != null) { final JCheckBox checkBox = new JCheckBox(); checkBox.setSelected(getBoolean(field)); checkBox.addChangeListener(new ChangeListener(){ @Override public void stateChanged(ChangeEvent _changeEvent) { final JCheckBox checkBox = ((JCheckBox) _changeEvent.getSource()); // System.out.println("check toggle "+checkBox); setBoolean(booleanField, checkBox.isSelected()); sync(); } }); newComponent = checkBox; } } if (newComponent != null) { panel.add(newComponent); layout.putConstraint(SpringLayout.NORTH, newComponent, INSET, (last == panel) ? SpringLayout.NORTH : SpringLayout.SOUTH, last); layout.putConstraint(SpringLayout.WEST, newComponent, INSET, SpringLayout.EAST, fieldToLabelMap.get(fieldWithWidestLabel)); layout.putConstraint(SpringLayout.EAST, newComponent, INSET, SpringLayout.EAST, panel); } last = newComponent; } layout.layoutContainer(panel); } public abstract void sync(); public Component getPanel() { return (panel); } } public static final int VMARGIN = 2; public static final int HMARGIN = 2; public static final int HGAPROOT = 100; public static final int HGAP = 40; public static final int VGAP = 20; public static final int ARROWGAP = 5; public static final int EDGEGAP = 20; public static final int CURVEBOW = 20; public static class Options implements Template{ @Toggle(label = "Fold", on = "On", off = "Off") public boolean fold = true; @Check(label = "Fan Edges") public boolean edgeFan = true; @Check(label = "Curves") public boolean edgeCurve = false; @Check(label = "PC") public boolean showPc = true; @Check(label = "Bytecode Labels") public boolean verboseBytecodeLabels = false; @Check(label = "Collapse All") public boolean collapseAll = false; /* @Check(label = "Show expressions")*/public boolean showExpressions = false; } private static class XY{ public XY(double _x, double _y) { x = _x; y = _y; } double x, y; } private static class View{ AffineTransform offGraphicsTransform = new AffineTransform(); private double scale = 1; private double x; private double y; public double translatex(int _screenx) { return ((_screenx - offGraphicsTransform.getTranslateX()) / offGraphicsTransform.getScaleX()); } public double screenx() { return ((offGraphicsTransform.getScaleX() * x) + offGraphicsTransform.getTranslateX()); } public double translatey(int _screeny) { return ((_screeny - offGraphicsTransform.getTranslateY()) / offGraphicsTransform.getScaleY()); } public double screeny() { return ((offGraphicsTransform.getScaleY() * y) + offGraphicsTransform.getTranslateY()); } } private final JPanel container; private BufferedImage offscreen; private Dimension offscreensize; private Graphics2D offgraphics; public void dirty() { dirty = true; container.repaint(); } private boolean dirty = false; private final View view = new View(); private XY dragStart = null; public synchronized void draw(Graphics _g) { final Dimension containerSize = container.getSize(); if (dirty || (offscreen == null) || (containerSize.width != offscreensize.width) || (containerSize.height != offscreensize.height)) { offscreensize = new Dimension(containerSize.width, containerSize.height); offscreen = (BufferedImage) container.createImage(offscreensize.width, offscreensize.height); if (offgraphics != null) { offgraphics.dispose(); } offgraphics = offscreen.createGraphics(); offgraphics.setFont(container.getFont()); offgraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); offgraphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); offgraphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); // offgraphics.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE); offgraphics.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON); final AffineTransform offGraphicsTransform = new AffineTransform(); offgraphics.setTransform(offGraphicsTransform); offgraphics.setColor(container.getBackground()); offgraphics.fillRect(0, 0, (offscreensize.width), (offscreensize.height)); offGraphicsTransform.setToTranslation(view.screenx(), view.screeny()); offGraphicsTransform.scale(view.scale, view.scale); offgraphics.setTransform(offGraphicsTransform); view.offGraphicsTransform = offGraphicsTransform; dirty = false; } else { offgraphics.setColor(container.getBackground()); offgraphics.fillRect(0, 0, (offscreensize.width), (offscreensize.height)); } render(offgraphics); _g.drawImage(offscreen, 0, 0, null); } public Component getContainer() { return (container); } public void text(Graphics2D _g, String _text, double _x, double _y) { final FontMetrics fm = _g.getFontMetrics(); _g.drawString(_text, (int) _x, (int) ((_y - fm.getAscent()) + fm.getHeight())); } public void text(Graphics2D _g, Color _color, String _text, double _x, double _y) { final Color color = _g.getColor(); _g.setColor(_color); text(_g, _text, _x, _y); _g.setColor(color); } public void line(Graphics2D _g, Stroke _stroke, double _x1, double _y1, double _x2, double _y2) { final Stroke stroke = _g.getStroke(); _g.setStroke(_stroke); line(_g, _x1, _y1, _x2, _y2); _g.setStroke(stroke); } public void stroke(Graphics2D _g, Stroke _stroke, Shape _rect) { final Stroke stroke = _g.getStroke(); _g.setStroke(_stroke); draw(_g, _rect); _g.setStroke(stroke); } public void fill(Graphics2D _g, Color _color, Shape _rect) { final Color color = _g.getColor(); _g.setColor(_color); fill(_g, _rect); _g.setColor(color); } public void fillStroke(Graphics2D _g, Color _fillColor, Color _strokeColor, Stroke _stroke, Shape _rect) { final Color color = _g.getColor(); _g.setColor(_fillColor); fill(_g, _rect); _g.setColor(_strokeColor); stroke(_g, _stroke, _rect); _g.setColor(color); } public void line(Graphics2D _g, double _x1, double _y1, double _x2, double _y2) { _g.drawLine((int) _x1, (int) _y1, (int) _x2, (int) _y2); } public void draw(Graphics2D _g, Shape _rectangle) { _g.draw(_rectangle); } public void fill(Graphics2D _g, Shape _rectangle) { _g.fill(_rectangle); } public Options config = new Options(); final private Color unselectedColor = Color.WHITE; final private Color selectedColor = Color.gray.brighter(); private final Stroke thickStroke = new BasicStroke((float) 2.0); private final Stroke thinStroke = new BasicStroke((float) 1.0); private final Stroke outlineStroke = new BasicStroke((float) 0.5); public Polygon arrowHeadOut = new Polygon(); { arrowHeadOut.addPoint(8, -4); arrowHeadOut.addPoint(0, 0); arrowHeadOut.addPoint(8, 4); arrowHeadOut.addPoint(8, -4); } Polygon arrowHeadIn = new Polygon(); { arrowHeadIn.addPoint(0, -4); arrowHeadIn.addPoint(8, 0); arrowHeadIn.addPoint(0, 4); arrowHeadIn.addPoint(0, -4); } public class InstructionView{ private final Instruction instruction; private Shape shape; public Instruction branchTarget; public Instruction collapsedBranchTarget; public String label; public boolean dim; public InstructionView(Instruction _instruction) { instruction = _instruction; } } private final Map<Instruction, InstructionView> locationToInstructionViewMap = new HashMap<Instruction, InstructionView>(); InstructionView getInstructionView(Instruction _instruction) { InstructionView instructionView = locationToInstructionViewMap.get(_instruction); if (instructionView == null) { locationToInstructionViewMap.put(_instruction, instructionView = new InstructionView(_instruction)); } return (instructionView); } double foldPlace(Graphics2D _g, InstructionView _instructionView, double _x, double _y, boolean _dim) { _instructionView.dim = _dim; final FontMetrics fm = _g.getFontMetrics(); _instructionView.label = InstructionHelper.getLabel(_instructionView.instruction, config.showPc, config.showExpressions, config.verboseBytecodeLabels); final int w = fm.stringWidth(_instructionView.label) + HMARGIN; final int h = fm.getHeight() + VMARGIN; double y = _y; final double x = _x + w + (_instructionView.instruction.getRootExpr() == _instructionView.instruction ? HGAP : HGAP); if (!config.collapseAll && !config.showExpressions) { for (Instruction e = _instructionView.instruction.getFirstChild(); e != null; e = e.getNextExpr()) { y = foldPlace(_g, getInstructionView(e), x, y, _dim); if (e != _instructionView.instruction.getLastChild()) { y += VGAP; } } } final double top = ((y + _y) / 2) - (h / 2); _instructionView.shape = new Rectangle((int) _x, (int) top, w, h); return (Math.max(_y, y)); } void foldRender(Graphics2D _g, InstructionView _instructionView) { final Instruction instruction = _instructionView.instruction; if (!config.collapseAll && !config.showExpressions) { for (Instruction e = instruction.getFirstChild(); e != null; e = e.getNextExpr()) { foldRender(_g, getInstructionView(e)); } } if (_instructionView.dim) { _g.setColor(unselectedColor); } else { _g.setColor(selectedColor); } _g.fill(_instructionView.shape); _g.setColor(Color.black); _g.setStroke(outlineStroke); _g.draw(_instructionView.shape); text(_g, _instructionView.label, _instructionView.shape.getBounds().getCenterX() - (_instructionView.shape.getBounds().getWidth() / 2), _instructionView.shape.getBounds().getCenterY()); if (!config.collapseAll && !config.showExpressions) { if (config.edgeFan) { for (Instruction e = instruction.getFirstChild(); e != null; e = e.getNextExpr()) { final InstructionView iv = getInstructionView(e); final double x1 = _instructionView.shape.getBounds().getMaxX() + ARROWGAP; final double y1 = _instructionView.shape.getBounds().getCenterY(); final double x2 = iv.shape.getBounds().getMinX() - 5; final double y2 = iv.shape.getBounds().getCenterY(); if (config.edgeCurve) { _g.draw(new CubicCurve2D.Double(x1, y1, x1 + CURVEBOW, y1, x2 - CURVEBOW, y2, x2, y2)); } else { final double dx = (x1 - x2); final double dy = (y1 - y2); final AffineTransform transform = _g.getTransform(); final double hypot = Math.hypot(dy, dx); final double angle = Math.atan2(dy, dx); _g.translate(x2, y2); _g.rotate(angle); line(_g, thickStroke, 0, 0, hypot, 0); _g.fillPolygon(arrowHeadOut); _g.setTransform(transform); } } } else { _g.setStroke(thickStroke); if ((instruction.getFirstChild() != null) && (instruction.getFirstChild() != instruction.getLastChild())) { // >1 children final InstructionView iv0 = getInstructionView(instruction.getFirstChild()); final InstructionView ivn = getInstructionView(instruction.getLastChild()); final double midx = (_instructionView.shape.getBounds().getMaxX() + iv0.shape.getBounds().getMinX()) / 2; line(_g, midx, iv0.shape.getBounds().getCenterY(), midx, ivn.shape.getBounds().getCenterY()); line(_g, _instructionView.shape.getBounds().getMaxX() + ARROWGAP, _instructionView.shape.getBounds().getCenterY(), midx, _instructionView.shape.getBounds().getCenterY()); for (Instruction e = instruction.getFirstChild(); e != null; e = e.getNextExpr()) { final InstructionView iv = getInstructionView(e); line(_g, midx, iv.shape.getBounds().getCenterY(), iv.shape.getBounds().getMinX() - ARROWGAP, iv.shape.getBounds() .getCenterY()); } } else if (instruction.getFirstChild() != null) { // 1 child final InstructionView iv = getInstructionView(instruction.getFirstChild()); line(_g, _instructionView.shape.getBounds().getMaxX() + ARROWGAP, _instructionView.shape.getBounds().getCenterY(), iv.shape.getBounds().getMinX() - ARROWGAP, iv.shape.getBounds().getCenterY()); } } } } double flatPlace(Graphics2D _g, InstructionView _instructionView, double _x, double _y) { final FontMetrics fm = _g.getFontMetrics(); final Instruction instruction = _instructionView.instruction; _instructionView.label = InstructionHelper.getLabel(instruction, config.showPc, config.showExpressions, config.verboseBytecodeLabels); final int h = fm.getHeight() + 2; final double top = (_y / 2) - (h / 2); _instructionView.shape = new Rectangle((int) _x, (int) top, fm.stringWidth(_instructionView.label) + 2, h); return (_y + h); } void flatRender(Graphics2D _g, InstructionView _instructionView) { _g.setColor(unselectedColor); _g.fill(_instructionView.shape); _g.setColor(Color.black); stroke(_g, outlineStroke, _instructionView.shape); text(_g, _instructionView.label, _instructionView.shape.getBounds().getCenterX() - (_instructionView.shape.getBounds().getWidth() / 2), _instructionView.shape.getBounds().getCenterY()); } ClassModel classModel = null; public InstructionViewer(Color _background, String _name) { try { classModel = ClassModel.createClassModel(Class.forName(_name)); } catch (final ClassParseException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (final ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } container = new JPanel(){ /** * */ private static final long serialVersionUID = 1L; @Override public void paintComponent(Graphics g) { draw(g); } }; container.setBackground(_background); final MouseAdapter mouseAdaptor = new MouseAdapter(){ @Override public void mouseEntered(MouseEvent e) { container.requestFocusInWindow(); } @Override public void mouseDragged(MouseEvent e) { // System.out.println("dragged"); if (dragStart != null) { view.x = view.translatex(e.getX()) - dragStart.x; view.y = view.translatey(e.getY()) - dragStart.y; dirty(); } } @Override public void mousePressed(MouseEvent e) { if (e.getButton() == 1) { dragStart = new XY(view.translatex(e.getX()), view.translatey(e.getY())); dirty(); } else if (e.getButton() == 3) { if (select(view.translatex(e.getX()), view.translatey(e.getY()))) { dirty(); } } } @Override public void mouseReleased(MouseEvent e) { dragStart = null; // container.repaint(); } @Override public void mouseWheelMoved(MouseWheelEvent e) { view.scale += e.getWheelRotation() / 10.0; dirty(); } }; final KeyAdapter keyAdaptor = new KeyAdapter(){ @Override public void keyTyped(KeyEvent arg0) { if ((arg0.getKeyChar() == '-') || (arg0.getKeyChar() == '+')) { if (arg0.getKeyChar() == '-') { view.scale -= .1; } else { view.scale += .1; } dirty(); } } }; container.addMouseMotionListener(mouseAdaptor); container.addMouseListener(mouseAdaptor); container.addMouseWheelListener(mouseAdaptor); container.addKeyListener(keyAdaptor); container.repaint(); } public InstructionViewer() { final JFrame frame = new JFrame(); final Color background = Color.WHITE; final JPanel panel = new JPanel(new BorderLayout()); final JMenuBar menuBar = new JMenuBar(); final JMenu fileMenu = new JMenu("File"); fileMenu.setMnemonic(KeyEvent.VK_F); final ActionListener closeActionListener = new ActionListener(){ @Override public void actionPerformed(ActionEvent arg0) { System.exit(1); } }; final ActionListener nextActionListener = new ActionListener(){ @Override public void actionPerformed(ActionEvent arg0) { doorbell.ring(); } }; final JMenuItem closeMenuItem = new JMenuItem("Close"); closeMenuItem.setMnemonic(KeyEvent.VK_C); closeMenuItem.addActionListener(closeActionListener); fileMenu.add(closeMenuItem); menuBar.add(fileMenu); menuBar.setEnabled(true); frame.setJMenuBar(menuBar); // http://java.sun.com/docs/books/tutorial/uiswing/components/toolbar.html final JToolBar toolBar = new JToolBar(); final JButton closeButton = new JButton("Close"); closeButton.addActionListener(closeActionListener); toolBar.add(closeButton); final JButton nextButton = new JButton("Next"); nextButton.addActionListener(nextActionListener); toolBar.add(nextButton); panel.add(BorderLayout.PAGE_START, toolBar); container = new JPanel(){ /** * */ private static final long serialVersionUID = 1L; @Override public void paintComponent(Graphics g) { draw(g); } }; container.setBackground(Color.WHITE); final MouseAdapter mouseAdaptor = new MouseAdapter(){ @Override public void mouseEntered(MouseEvent e) { container.requestFocusInWindow(); } @Override public void mouseDragged(MouseEvent e) { // System.out.println("dragged"); if (dragStart != null) { view.x = view.translatex(e.getX()) - dragStart.x; view.y = view.translatey(e.getY()) - dragStart.y; dirty(); } } @Override public void mousePressed(MouseEvent e) { if (e.getButton() == 1) { dragStart = new XY(view.translatex(e.getX()), view.translatey(e.getY())); dirty(); } else if (e.getButton() == 3) { if (select(view.translatex(e.getX()), view.translatey(e.getY()))) { dirty(); } } } @Override public void mouseReleased(MouseEvent e) { dragStart = null; // container.repaint(); } @Override public void mouseWheelMoved(MouseWheelEvent e) { view.scale += e.getWheelRotation() / 10.0; dirty(); } }; final KeyAdapter keyAdaptor = new KeyAdapter(){ @Override public void keyTyped(KeyEvent arg0) { if ((arg0.getKeyChar() == '-') || (arg0.getKeyChar() == '+')) { if (arg0.getKeyChar() == '-') { view.scale -= .1; } else { view.scale += .1; } dirty(); } } }; container.addMouseMotionListener(mouseAdaptor); container.addMouseListener(mouseAdaptor); container.addMouseWheelListener(mouseAdaptor); container.addKeyListener(keyAdaptor); container.repaint(); panel.add(BorderLayout.CENTER, container); final JPanel controls = new JPanel(new BorderLayout()); final Form<Options> form = new Form<Options>(config){ @Override public void sync() { dirty(); } }; controls.add(form.getPanel()); controls.setPreferredSize(new Dimension(200, 500)); panel.add(BorderLayout.EAST, controls); frame.setBackground(background); frame.getContentPane().add(panel); frame.setPreferredSize(new Dimension(1024, 1000)); frame.pack(); frame.setVisible(true); } public boolean select(double _x, double _y) { for (Instruction l = first; l != null; l = l.getNextPC()) { final InstructionView iv = getInstructionView(l); if ((iv.shape != null) && iv.shape.contains(_x, _y)) { return (true); } } return (false); } public void render(Graphics2D _g) { if (first != null) { if (config.fold) { double y = 100; final Instruction firstRoot = first.getRootExpr(); final List<InstructionView> instructionViews = new ArrayList<InstructionView>(); Instruction lastInstruction = null; for (Instruction instruction = firstRoot; instruction != null; instruction = instruction.getNextExpr()) { final InstructionView iv = getInstructionView(instruction); iv.dim = false; y = foldPlace(_g, iv, 100, y, false) + VGAP; instructionViews.add(iv); lastInstruction = instruction; } lastInstruction.getRootExpr(); while (lastInstruction instanceof CompositeInstruction) { lastInstruction = lastInstruction.getLastChild(); } for (Instruction instruction = lastInstruction.getNextPC(); instruction != null; instruction = instruction.getNextPC()) { final InstructionView iv = getInstructionView(instruction); iv.dim = true; y = foldPlace(_g, iv, 100, y, true) + VGAP; instructionViews.add(iv); } _g.setColor(Color.black); for (final InstructionView instructionView : instructionViews) { if (instructionView.instruction.isBranch()) { final Instruction rootFromInstruction = instructionView.instruction; final Instruction rootToInstruction = instructionView.instruction.asBranch().getTarget(); final InstructionView fromIv = getInstructionView(rootFromInstruction); final InstructionView toIv = getInstructionView(rootToInstruction); edge(_g, Color.BLACK, fromIv, toIv, null, null); } } InstructionView last = null; for (final InstructionView instructionView : instructionViews) { foldRender(_g, instructionView); if (last != null) { line(_g, thickStroke, 120, last.shape.getBounds().getMaxY(), 120, instructionView.shape.getBounds().getMinY()); } foldRender(_g, instructionView); last = instructionView; } } else { double y = 100; for (Instruction l = first; l != null; l = l.getNextPC()) { y = flatPlace(_g, getInstructionView(l), 100, y) + VGAP; } _g.setColor(Color.black); for (Instruction l = first; l != null; l = l.getNextPC()) { if (l.isBranch()) { final Instruction rootFromInstruction = l; final Instruction rootToInstruction = l.asBranch().getTarget(); final InstructionView fromIv = getInstructionView(rootFromInstruction); final InstructionView toIv = getInstructionView(rootToInstruction); edge(_g, Color.BLACK, fromIv, toIv, null, null); } } InstructionView last = null; for (Instruction l = first; l != null; l = l.getNextPC()) { final InstructionView iv = getInstructionView(l); if (last != null) { line(_g, thickStroke, 120, last.shape.getBounds().getMaxY(), 120, iv.shape.getBounds().getMinY()); } flatRender(_g, iv); last = iv; } } } } public void edge(Graphics2D _g, Color _color, InstructionView _branch, InstructionView _target, String _endLabel, String _startLabel) { final int delta = _target.instruction.getThisPC() - _branch.instruction.getThisPC(); final int adjust = 7 + Math.abs(delta); final double y1 = (int) _branch.shape.getBounds().getMaxY(); if (_target.shape != null) { _g.setStroke(thinStroke); final Color old = _g.getColor(); _g.setColor(_color); final double y2 = (int) _target.shape.getBounds().getMinY(); if (delta > 0) { final double x1 = (int) _branch.shape.getBounds().getMinX() - EDGEGAP; final double x2 = (int) _target.shape.getBounds().getMinX() - EDGEGAP; _g.draw(new CubicCurve2D.Double(x1, y1, x1 - adjust, y1, x1 - adjust, y2, x2, y2)); final AffineTransform transform = _g.getTransform(); _g.translate(x2 - 5, y2); _g.fillPolygon(arrowHeadIn); _g.setTransform(transform); } else { final double x1 = (int) _branch.shape.getBounds().getMaxX() + EDGEGAP; final double x2 = (int) _target.shape.getBounds().getMaxX() + EDGEGAP; _g.draw(new CubicCurve2D.Double(x1, y1, Math.max(x1, x2) + adjust, y1, Math.max(x1, x2) + adjust, y2, x2, y2)); final AffineTransform transform = _g.getTransform(); _g.translate(x2 - 5, y2); _g.fillPolygon(arrowHeadOut); _g.setTransform(transform); } _g.setColor(old); } } volatile Instruction first = null; volatile Instruction current = null; @Override public void showAndTell(String message, Instruction head, Instruction _instruction) { if (first == null) { first = head; } current = _instruction; dirty(); doorbell.snooze(); } public static class DoorBell{ volatile boolean notified = false; public synchronized void snooze() { while (!notified) { try { this.wait(); } catch (final InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } notified = false; } public synchronized void ring() { notified = true; notify(); } } public static DoorBell doorbell = new DoorBell(); public static void main(String[] _args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, UnsupportedLookAndFeelException, AparapiException { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); final JFrame frame = new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); final Color background = Color.WHITE; final JPanel panel = new JPanel(new BorderLayout()); final JMenuBar menuBar = new JMenuBar(); final JMenu fileMenu = new JMenu("File"); fileMenu.setMnemonic(KeyEvent.VK_F); final ActionListener closeActionListener = new ActionListener(){ @Override public void actionPerformed(ActionEvent arg0) { System.exit(1); } }; final ActionListener nextActionListener = new ActionListener(){ @Override public void actionPerformed(ActionEvent arg0) { doorbell.ring(); } }; final JMenuItem closeMenuItem = new JMenuItem("Close"); closeMenuItem.setMnemonic(KeyEvent.VK_C); closeMenuItem.addActionListener(closeActionListener); fileMenu.add(closeMenuItem); menuBar.add(fileMenu); menuBar.setEnabled(true); frame.setJMenuBar(menuBar); final InstructionViewer instructionViewer = new InstructionViewer(background, _args[0]); Config.instructionListener = instructionViewer; // http://java.sun.com/docs/books/tutorial/uiswing/components/toolbar.html final JToolBar toolBar = new JToolBar(); final JButton closeButton = new JButton("Close"); closeButton.addActionListener(closeActionListener); toolBar.add(closeButton); final JButton nextButton = new JButton("Next"); nextButton.addActionListener(nextActionListener); toolBar.add(nextButton); panel.add(BorderLayout.PAGE_START, toolBar); panel.add(BorderLayout.CENTER, instructionViewer.getContainer()); final JPanel controls = new JPanel(new BorderLayout()); final Form<Options> form = new Form<Options>(instructionViewer.config){ @Override public void sync() { instructionViewer.dirty(); } }; controls.add(form.getPanel()); controls.setPreferredSize(new Dimension(200, 500)); panel.add(BorderLayout.EAST, controls); frame.setBackground(background); frame.getContentPane().add(panel); frame.setPreferredSize(new Dimension(800, 1000)); frame.pack(); frame.setVisible(true); (new Thread(new Runnable(){ @Override public void run() { Entrypoint entrypoint; try { entrypoint = instructionViewer.classModel.getEntrypoint(); final MethodModel method = entrypoint.getMethodModel(); } catch (final AparapiException e) { // TODO Auto-generated catch block e.printStackTrace(); } } })).start(); } }