package com.applang.components; import com.applang.UserContext; import com.applang.berichtsheft.plugin.BerichtsheftPlugin; import java.awt.Component; import java.awt.Dimension; import java.awt.Point; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.beans.PropertyChangeEvent; import java.io.File; import java.io.StringReader; import java.util.Map; import java.util.Vector; import javax.swing.JButton; import javax.swing.JDialog; import javax.swing.JMenu; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPopupMenu; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.JTextArea; import javax.swing.SwingUtilities; import javax.swing.event.PopupMenuEvent; import javax.swing.event.TableModelEvent; import javax.swing.event.TableModelListener; import javax.swing.filechooser.FileNameExtensionFilter; import javax.swing.table.DefaultTableModel; import org.apache.velocity.runtime.parser.Token; import org.apache.velocity.runtime.parser.node.Node; import org.apache.velocity.runtime.parser.node.SimpleNode; import android.util.Log; import static com.applang.Util.*; import static com.applang.SwingUtil.*; import static com.applang.VelocityUtil.*; import static com.applang.PluginUtils.*; public class ASTViewer extends ActionPanel { public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { final TextToggle textToggle = new TextToggle(); textToggle.createBufferedTextArea("velocity", "/modes/velocity_pure.xml"); String title = "Velocity AST tool"; ASTViewer viewer = new ASTViewer(textToggle, null, title); ActionPanel.createAndShowGUI(title, new Dimension(600, 300), Behavior.EXIT_ON_CLOSE, viewer, new Function<Component>() { public Component apply(Object...params) { return textToggle.getUIComponent(); } }); } }); } enum ActionType implements CustomActionType { DELETE (0, "manager.action-DELETE"), INSERT (1, "manager.action-INSERT"), MODIFY (2, "manager.action-MODIFY"), SET (3, "set"), FOREACH (4, "foreach"), TOGGLE (6, "manager.action-TOGGLE2"), STRUCT (7, "manager.action-STRUCT"), VMFILE (8, "manager.action-VMFILE"), ACTIONS (9, "Actions"); // needs to stay last ! private final int index; private final String resourceName; ActionType(int index, String resourceName) { this.index = index; this.resourceName = resourceName; } @Override public String resourceName() { return resourceName; } @Override public int index() { return index; } @Override public String iconName() { return getProperty(resourceName + ".icon"); } @Override public String description() { return getProperty(resourceName.concat(".label")); } @Override public String name(int state) { return getProperty(resourceName.concat(".label") + "." + state); } } public File vmFile; class ManipAction extends CustomAction { public ManipAction(ActionType type) { super(type); if (type.equals(ActionType.TOGGLE)) { putValue(NAME, type.name(1)); } } public ManipAction(String text) { super(text); } @Override protected void action_Performed(ActionEvent ae) { ActionType type = (ActionType)getType(); if (type == null) type = ActionType.INSERT; switch (type) { case VMFILE: vmFile = getVm(); if (fileExists(vmFile)) setText(contentsFromFile(vmFile)); else setText(""); break; case TOGGLE: toggle(this, getTextToggle().getTextEdit().toggler); break; case STRUCT: if (fileExists(vmFile)) structure(0); break; default: map = map(popupEvent); if (map != null) { Object node = map.get(NODE); if (type.index == MODIFY) { setSelection(node, false); } else if (type.index == DELETE) { setSelection(node, false); getTextToggle().setSelectedText(null); } else if (type.index == INSERT) { setSelection(node, true); } if (Visitor.nodeGroup(node) == DIRECTIVE) { switch (type) { case SET: break; case FOREACH: break; default: break; } } } break; } } } boolean isSciptView() { return getTextToggle().getTextArea2() == null; } private void setSelection(Object node, boolean noSpan) { if (isSciptView()) { int[] span = Visitor.span(node); span = getTextOffsets(getTextToggle().getText(), span); getTextToggle().setSelection(span[0], noSpan ? -1 : span[1]); } } public void structure(int mode) { title = vmFile.getName(); script = getText(); showAST(); } class ASTModel extends DefaultTableModel { public Vector<Vector<String>> data = new Vector<Vector<String>>(); public Vector<String> columns = new Vector<String>(); Job<Object[]> checkout = new Job<Object[]>() { public void perform(Object[] objects, Object[] params) throws Exception { int indents = (Integer) objects[0]; Node node = (Node) objects[1]; int blocks = Visitor.blockDepth(node); Token t = (Token) objects[2]; Vector<String> row = new Vector<String>(); if (detail) { row.addElement(indents + "_" + blocks); row.addElement("-- lost+found --"); row.addElement(Visitor.formatLC(Visitor.getBeginToken(t))); row.addElement(Visitor.formatLC(Visitor.getEndToken(t))); row.addElement(t.image); } else { row.addElement(indentedLine(t.image, indentor, indents, blocks)); row.addElement(""); } data.addElement(row); maps.addElement(nodeMap(node, indents, t)); } }; boolean detail = true; public boolean isDetail() { return detail; } boolean essentials = true; public boolean isEssentials() { return essentials; } public ASTModel(String text, Object...params) { detail = param_Boolean(detail, 0, params); essentials = param_Boolean(essentials, 1, params); if (detail) { columns.addElement("level"); columns.addElement("class"); columns.addElement("begin"); columns.addElement("end"); columns.addElement("tokens"); } else { columns.addElement("info"); columns.addElement("category"); } try { document = parse( new StringReader(text), ""); Visitor.walk(document, new Function<Object>() { public Object apply(Object...params) { Node node = param(null, 0, params); Visitor.visitLostAndFound(checkout, null, Visitor.beginLC(node)); int indents = param_Integer(0, 1, params); if (!Visitor.isProcessNode(node)) { if (!essentials || Visitor.isEssential.apply(node)) { Vector<String> row = new Vector<String>(); int blocks = Visitor.blockDepth(node); if (detail) { row.addElement(indents + "_" + blocks); row.addElement(node.getClass().getSimpleName()); row.addElement(Visitor.formatLC(Visitor.beginLC(node))); row.addElement(Visitor.formatLC(Visitor.endLC(node))); row.addElement(Visitor.tokens(node)); } else { String line = indentedLine( Visitor.nodeInfo(node), indentor, indents, blocks); row.addElement(line); row.addElement(Visitor.nodeCategory(node)); } data.addElement(row); maps.addElement(nodeMap(node, indents, null)); } } return null; } }); Visitor.visitLostAndFound(checkout, null, Integer.MAX_VALUE, 0); } catch (Exception e) { Log.e(TAG, "ASTModel", e); } setDataVector(data, columns); } public SimpleNode document = null; public Vector<ValMap> maps = new Vector<ValMap>(); @SuppressWarnings("rawtypes") public Vector getColumnIdentifiers() { return this.columnIdentifiers; } public void setColumnWidths(JTable table) { int cols = getColumnIdentifiers().size(); setColumnWidthsAsPercentages(table, cols > 2 ? new double[]{0.10, 0.20, 0.10, 0.10, 0.50} : new double[]{0.80, 0.20}); } } private JTable configureTable(final ASTModel model) { final JTable table = new JTable(model); model.setColumnWidths(table); table.setPreferredScrollableViewportSize(size); table.addComponentListener(new ComponentAdapter() { @Override public void componentResized(ComponentEvent e) { size = table.getSize(); } }); JPopupMenu popupMenu = newPopupMenu( menuRecord("+", INSERT, ActionType.INSERT, newMenu(ActionType.INSERT.description())), menuRecord("delete", DELETE, ActionType.DELETE, new ManipAction(ActionType.DELETE)), menuRecord("modify", MODIFY, ActionType.MODIFY, new ManipAction(ActionType.MODIFY)) ); table.addMouseListener(new PopupAdapter(popupMenu)); table.addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent e) { if (e.getClickCount() == 1) { map = map(e); if (map != null) { Object node = map.get(NODE); setSelection(node, false); } } } }); model.addTableModelListener(new TableModelListener() { public void tableChanged(TableModelEvent e) { int column = e.getColumn(); switch (e.getType()) { case TableModelEvent.UPDATE: if ("tokens".equals(model.columns.get(column))) { int row = e.getFirstRow(); ValMap map = model.maps.get(row); Node node = (Node) map.get(NODE); Object value = model.getValueAt(row, column); Visitor.update(node, value); } break; } } }); return table; } private int showText(Object...params) { JTextArea textArea = new JTextArea(); textArea.setEditable(true); textArea.setPreferredSize(size); String title = String.format("Evaluation : '%s'", this.title); UserContext.setupVelocity(null, true); textArea.setText(UserContext.toPlainText(script)); JOptionPane optionPane = new JOptionPane(new JScrollPane(textArea), JOptionPane.PLAIN_MESSAGE, JOptionPane.DEFAULT_OPTION, null, null, null); JDialog dialog = optionPane.createDialog(ASTViewer.this, title); dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); dialog.setModal(true); dialog.setResizable(true); dialog.setVisible(true); Object value = optionPane.getValue(); return value instanceof Integer ? (Integer) value : JOptionPane.CLOSED_OPTION; } private Dimension size = new Dimension(700, 200); private String title = null; private String script = null; private ValMap map = null; private int manip = -1; private int showAST(final Object...params) { manip = param_Integer(0, 0, params); final boolean detail = param_Boolean(true, 1, params); final boolean essentials = param_Boolean(true, 2, params); map = param(null, 3, params); if (manip > 0) { int option = showText(params); return option < 0 ? option : 2; } else { JButton eval = new JButton("Evaluation"); Object[] options = new Object[] { detail ? "Info" : "Detail", essentials ? "all" : "essentials", "Update", eval, }; eval.setMnemonic(KeyEvent.VK_V); eval.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { showAST(1, detail, essentials, map); } }); ASTModel model = new ASTModel(script, detail, essentials); final JTable table = configureTable(model); String title = String.format("AST (%d rows) : '%s' ", model.getDataVector().size(), this.title); int option = showOptionDialog(ASTViewer.this, scrollableViewport(table), title, Behavior.MODAL + JOptionPane.DEFAULT_OPTION, JOptionPane.PLAIN_MESSAGE, null, options, null, new Function<Boolean>() { public Boolean apply(Object... parms) { PropertyChangeEvent e = (PropertyChangeEvent) parms[0]; JOptionPane optionPane = (JOptionPane) e.getSource(); Object[] options = (Object[]) parms[1]; ASTModel model = (ASTModel) table.getModel(); boolean flag; switch (dialogResult) { case 0: flag = !model.isDetail(); table.setModel(new ASTModel(script, flag, model.isEssentials())); options[0] = flag ? "Info" : "Detail"; break; case 1: flag = !model.isEssentials(); table.setModel(new ASTModel(script, model.isDetail(), flag)); options[1] = flag ? "all" : "essentials"; break; case 2: script = Visitor.tail(model.document); table.setModel(new ASTModel(script, model.isDetail(), model.isEssentials())); break; default: return false; } optionPane.setOptions(options); optionPane.updateUI(); return true; } } ); return option; } } ValMap map(MouseEvent me) { ValMap map = null; if (me != null) { JTable table = (JTable) me.getComponent(); if (table != null) { Point pt = new Point(me.getX(), me.getY()); int row = table.rowAtPoint(pt); table.setRowSelectionInterval(row, row); ASTModel model = (ASTModel) table.getModel(); map = model.maps.get(row); } } return map; } Object[] menuRecord(final String name, final int actionIdent, ActionType type, Object action) { if (action instanceof JMenu) { JMenu menu = (JMenu) action; for (String key : UserContext.directives().keySet()) { menu.add(new JMenuItem()).setAction(new ManipAction(key)); } menu.setIcon(iconFrom(type.iconName())); } return objects(type.description(), action, name, "", null, null, null, null, new PopupMenuAdapter() { @Override public void popupMenuWillBecomeVisible(PopupMenuEvent e) { JPopupMenu popup = (JPopupMenu)e.getSource(); printContainer("popupMenuWillBecomeVisible", popup, _null()); ValMap map = map(popupEvent); if (map != null) { boolean enabled = isPossible(actionIdent, map); String s = name; if ("+-".indexOf(s) > -1) s = "\\" + s; JMenuItem menuItem = findFirstComponent(popup, s); menuItem.setEnabled(enabled); if (menuItem instanceof JMenu) { JMenu menu = (JMenu) menuItem; final Map<String, String> anweisungen = UserContext.directives(map); for (int i = 0; i < menu.getItemCount(); i++) { JMenuItem item = menu.getItem(i); String text = item.getText(); if (!anweisungen.containsKey(text)) item.setEnabled(false); } } } } } ); } public static File getVm() { final File store = new File("/home/lotharla/work/velocity/.vm"); File vm = getFileFromStore(1, "Velocity template", new FileNameExtensionFilter("vm files", "vm"), null, new Function<String[]>() { public String[] apply(Object... params) { return contentsFromFile(store).split(NEWLINE_REGEX); } }, new Job<String[]>() { public void perform(String[] fileNames, Object[] params) throws Exception { contentsToFile(store, join(NEWLINE, fileNames)); } }); return vm; } public ASTViewer(ITextComponent textArea, Object...params) { super(textArea, params); addButton(this, ActionType.VMFILE.index(), new ManipAction(ActionType.VMFILE)); addButton(this, ActionType.STRUCT.index(), new ManipAction(ActionType.STRUCT)); addToggle(this, ActionType.TOGGLE.index(), new ManipAction(ActionType.TOGGLE)); // TODO Auto-generated constructor stub } }