December Issue of EmberWings is Here!
2 days ago
some writings on software development by damir
In project tree right click on Libraries Node > Add module dependency...
# DDL Visualizer # created by Damir Tesanovic (tdamir.blogspot.com) TOKEN:keyword:("create" | "table" | "constraint" | "unique" | "primary" | "key" | "foreign" | "references" | "smallint" | "integer" | "float" | "double" | "precision" | "decimal" | "numeric" | "date" | "time" | "timestamp" | "char" | "varchar" | "blob") TOKEN:identifier:( ["a"-"z" "A"-"Z" "_"] ["a"-"z" "A"-"Z" "0"-"9" "_"]* ("." ["a"-"z" "A"-"Z" "_"] ["a"-"z" "A"-"Z" "0"-"9" "_"]*)? ) TOKEN:identifier:( "\"" [^ "\""]* "\"" ) TOKEN:operator:("<=" | "<>" | "<" | "=" | ">=" | ">" | "||" | "-" | "," | ";" | ":" | "/" | "." | "(" | ")" | "[" | "]" | "*" | "+") TOKEN:whitespace:( [" " "\t" "\n" "\r"]+ ) TOKEN:number:(["0"-"9"]+) # parser should ignore whitespaces SKIP:whitespace # definition of grammar S = (CreateTable)*; CreateTable = "create" "table" TableName TableDef; TableDef = "(" ColumnDef ("," (ColumnDef | TableConstraint))* ")"; ColumnDef = ColumnName DataType; TableConstraint = ["constraint" ConstraintName] (("unique" | "primary" "key") "(" ColumnName ("," ColumnName)* ")" | ForeignKeyClause) ; ForeignKeyClause = "foreign" "key" "(" ColumnName ("," ColumnName)* ")" ReferencesClause; ConstraintName = <identifier>; TableName = <identifier>; ColumnName = <identifier>; Integer = <number> ; ReferencesClause = "references" TableName ["(" ColumnName ("," ColumnName)* ")"]; DataType = "smallint" | "integer" | "float" | "double" "precision" | ( "decimal" | "numeric" ) [ "(" Integer [ "," Integer ] ")" ] | "date" | "time" | "timestamp" | ( "char" | "varchar" ) [ "(" Integer ")" ] | "blob" ; # error highlighting MARK:ERROR: { type:"Error"; message:"Syntax error."; } MARK:error: { type:"Error"; message:"Unexpected character."; }
<folder name="Editors"> <folder name="text"> <folder name="x-fbsql"> <file name="language.nbs" url="language.nbs"/> <folder name="Popup"> <file name="org-myorg-ddlvisualizer-ShowDiagram.shadow"> <attr name="originalFile" stringvalue="Actions/Tools/org-myorg-ddlvisualizer-ShowDiagram.instance"/> <attr name="position" intvalue="700"/> </file> </folder> </folder> </folder> </folder>We will add functionality to created action later.
package org.myorg.ddlvisualizer; import java.awt.Image; import java.awt.Point; import java.util.ArrayList; import java.util.Iterator; import org.netbeans.api.languages.ASTNode; import org.netbeans.api.visual.action.ActionFactory; import org.netbeans.api.visual.vmd.VMDGraphScene; import org.netbeans.api.visual.vmd.VMDNodeWidget; import org.netbeans.api.visual.vmd.VMDPinWidget; /** * * @author Damir Tesanovic (based on Anton Epple's DBGraphScene) */ public class DDLVisualScene extends VMDGraphScene { private static int edgeID = 1; public DDLVisualScene() { this.getActions().addAction(ActionFactory.createZoomAction()); this.getActions().addAction(ActionFactory.createPanAction()); } public void setASTNode(ASTNode node) { for (ASTNode table : getNodes(node, "CreateTable", false)) { String tableName = table.getNode("TableName").getTokenTypeIdentifier("identifier"); createNode(this, (int) (Math.random() * 800), (int) (Math.random() * 800), tableName, "Table", null); for (ASTNode col : getNodes(table, "ColumnDef", true)) { String columnName = col.getNode("ColumnName").getTokenTypeIdentifier("identifier"); createPin(this, tableName, tableName + ":" + columnName, columnName, columnName); } for (ASTNode foreignKeyClause : getNodes(table, "ForeignKeyClause", true)) { ArrayList<String> cols = new ArrayList<String>(); for (ASTNode col : getNodes(foreignKeyClause, "ColumnName", false)) { cols.add(col.getTokenTypeIdentifier("identifier")); } String refTableName = foreignKeyClause.getNode("ReferencesClause").getNode("TableName").getTokenTypeIdentifier("identifier"); ArrayList<String> refCols = new ArrayList<String>(); for (ASTNode refCol : getNodes(foreignKeyClause.getNode("ReferencesClause"), "ColumnName", false)) { refCols.add(refCol.getTokenTypeIdentifier("identifier")); } for (int i = 0; i < cols.size(); i++) { createEdge(this, refTableName + ":" + refCols.get(i), tableName + ":" + cols.get(i)); } } } this.moveTo(null); } private ArrayList<ASTNode> getNodes(ASTNode root, String nt, boolean deep) { ArrayList<ASTNode> nodes = new ArrayList<ASTNode>(); fillNodes(root, nodes, nt, deep); return nodes; } private void fillNodes(ASTNode node, ArrayList<ASTNode> out, String nt, boolean deep) { Iterator it = node.getChildren().iterator(); while (it.hasNext()) { Object elem = it.next(); if (elem instanceof ASTNode) { if (((ASTNode) elem).getNT().equals(nt)) { out.add((ASTNode) elem); } if (deep) { fillNodes((ASTNode) elem, out, nt, deep); } } } } private static String createNode(VMDGraphScene scene, int x, int y, String name, String type, java.util.List<Image> glyphs) { String nodeID = name; if (!scene.getNodes().contains(nodeID)) { VMDNodeWidget widget = (VMDNodeWidget) scene.addNode(nodeID); widget.setPreferredLocation(new Point(x, y)); widget.setNodeProperties(null, name, type, glyphs); } return nodeID; } private static void createPin(VMDGraphScene scene, String nodeID, String pinID, String name, String type) { if (!scene.getPins().contains(pinID)) { ((VMDPinWidget) scene.addPin(nodeID, pinID)).setProperties(name, null); } } private static void createEdge(VMDGraphScene scene, String sourcePinID, String targetPinID) { String edgeID = "edge" + DDLVisualScene.edgeID++; scene.addEdge(edgeID); System.out.println("createEdge " + sourcePinID + "<->" + targetPinID); scene.setEdgeSource(edgeID, sourcePinID); scene.setEdgeTarget(edgeID, targetPinID); } private void moveTo(Point point) { int index = 0; for (String node : getNodes()) { getSceneAnimator().animatePreferredLocation(findWidget(node), point != null ? point : new Point(++index * 100, index * 100)); } } }It has setASTNode(ASTNode node) method which goes through AST tree and builds diagram.
private static final String PREFERRED_ID = "VisualizerTopComponent"; private DDLVisualScene scene = new DDLVisualScene(); private VisualizerTopComponent() { initComponents(); setName(NbBundle.getMessage(VisualizerTopComponent.class, "CTL_VisualizerTopComponent")); setToolTipText(NbBundle.getMessage(VisualizerTopComponent.class, "HINT_VisualizerTopComponent")); // setIcon(Utilities.loadImage(ICON_PATH, true)); jScrollPane1.setViewportView(scene.createView()); }
Thanks to Generic Languages Framework we can easily get AST tree from document. We will pass it to scene and then show VisualizerTopComponent. Replace performAction method with following code:
protected void performAction(Node[] activatedNodes) { try { EditorCookie editorCookie = activatedNodes[0].getLookup().lookup(EditorCookie.class); Document doc = editorCookie.getDocument(); ParserManager pm = ParserManager.get(doc); VisualizerTopComponent win = VisualizerTopComponent.findInstance(); win.getScene().setASTNode(pm.getAST()); win.open(); win.requestActive(); } catch (ParseException ex) { Exceptions.printStackTrace(ex); } }
Here is sample file for testing.
create table EMPLOYEE ( EMP_NO integer, FIRST_NAME varchar, LAST_NAME varchar ) create table PROJECT ( PROJ_ID integer, PROJ_NAME varchar, PROJ_DESC varchar, TEAM_LEADER integer, constraint fkey1 foreign key (TEAM_LEADER) references EMPLOYEE (EMP_NO) ) create table EMPLOYEE_PROJECT ( EMP_NO integer, PROJ_ID integer, constraint fkey1 foreign key (EMP_NO) references EMPLOYEE (EMP_NO), constraint fkey2 foreign key (PROJ_ID) references PROJECT (PROJ_ID) )