package com.jpaulmorrison.graphics;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.JFrame;
public class DiagramBuilder {
public static void buildDiag(String input, JFrame frame, Diagram diag) {
/***********************************************************************
*
* This method scans off the XML-like network definition. Most of the
* logic takes place when endtags are encountered. There are two
* situations where things get more complex: a) when a connection
* (arrow) has embedded bends, and b) when an enclosure block has
* embedded subnetports
*
*/
DrawFBP driver = diag.driver;
Integer errNo = new Integer(0);
BabelParser2 bp = new BabelParser2(input, errNo);
HashMap<String, HashMap<String, String>> tagInfo = new HashMap<String, HashMap<String, String>>();
createTables(tagInfo);
boolean done = false;
Arrow thisArrow = null;
String starttag = "";
String data = null;
String saveData = null;
// Object object;
HashMap<String, String> item = new HashMap<String, String>();
boolean atomic = false;
HashMap<String, String> curFl = null;
boolean debugging = false;
//boolean debugging = true;
// 3 stacks
Stack<String> names = new Stack<String>(); // names (atomic and
// non-atomic)
Stack<HashMap<String, String>> fldLists = new Stack<HashMap<String, String>>();
// field lists
Stack<String> clsNames = new Stack<String>(); // class names
boolean endsw; // if true, it is an end tag
boolean control = false; // if true, it is a comment or control tag
boolean arrowBuilt = false;
Enclosure cEncl = null;
SubnetPort snp = null;
String endtag = null;
//String type;
diag.clickToGrid = true;
driver.grid.setSelected(diag.clickToGrid);
while (true) { // skip blanks, CRs or tabs
if (!(bp.tb('o')))
break;
}
if (!bp.tc('<', 'o')) {
MyOptionPane.showMessageDialog(frame, "1st non-blank char not '<'", MyOptionPane.ERROR_MESSAGE);
// terminate();
return;
}
while (true) { // main loop - we should be just after a <
if (bp.tc('/', 'o'))
endsw = true;
else
endsw = false;
if (bp.tc('!', 'o'))
control = true;
if (bp.tc('?', 'o'))
control = true;
while (true) { // scan off a symbol within <>
if (bp.tc('>', 'o'))
break;
bp.tu();
}
// at end, we can use getOutStr to get output into String (sym)
if (!control) {
starttag = bp.getOutStr(); // get this symbol - check if ends in /
if (starttag.startsWith("drawfbp_file"))
starttag = "drawfbp_file";
if (starttag.charAt(starttag.length() - 1) == '/') { // stand-alone field
// (no data)
endsw = true;
starttag = starttag.substring(0, starttag.length() - 1); // drop final
endtag = starttag;
// slash from symbol
names.push(starttag);
data = null;
atomic = true;
}
if (!endsw) {
names.push(starttag);
String t = starttag;
endtag = null;
if (curFl != null) {
t = (String) curFl.get(starttag);
if (t == null) {
MyOptionPane.showMessageDialog(frame, "Field '"
+ starttag + "' not defined for this class", MyOptionPane.ERROR_MESSAGE);
return;
}
if (t.charAt(0) == '*') // test if it's atomic
// this is set on if the value in curFl is an
// asterisk, meaning that tag just has a value,
// not a list
atomic = true; // indicate atomic
}
if (!atomic) { // end switch not on, and not atomic
// get permissible tags within this one
curFl = (HashMap<String, String>) tagInfo.get(starttag);
// set current field list
fldLists.push(curFl); // push it on stack
clsNames.push(starttag); // push class name
if (debugging)
System.out.println("Starting class " + starttag);
if (starttag.equals("connection")) {
Arrow arrow = new Arrow(diag);
thisArrow = arrow;
arrowBuilt = false;
} else if (starttag.equals("bends")) {
Integer aid = new Integer(item.get("id"));
diag.arrows.put(aid, thisArrow);
thisArrow.buildArrow(item);
arrowBuilt = true;
} else if (starttag.equals("subnetport")) {
snp = new SubnetPort();
} else if (starttag.equals("subnetports")) {
cEncl = new Enclosure(diag);
cEncl.buildEncl(item);
}
item.clear();
}
} else { // if endsw is on...
endtag = (String) names.pop();
if (debugging)
System.out.println(endtag + " popped");
if (!starttag.equals(endtag)) {
if (starttag.equals("net")) // from code before 2.13.4
return;
MyOptionPane
.showMessageDialog(frame, "Tags don't match: " + starttag + " - " + endtag, MyOptionPane.ERROR_MESSAGE);
return;
}
if (atomic) { // i.e. a leaf in the XML tree
if (data == null) {
// patterns as follows:
// <xx> </xx> OR <yy/>
if (debugging)
System.out.println("Stand-alone tag: " + endtag);
if (starttag.equals("endsatline") ||
starttag.equals("substreamsensitive") ||
starttag.equals("multiplex") ||
starttag.equals("invisible") ||
starttag.equals("clicktogrid") ||
starttag.equals("dropoldest"))
item.put(starttag, "true");
} else { // <xx> data </xx>
saveData = new String(data);
if (endtag.equals("desc") /* || endtag.equals("description") */) {
diag.desc = saveData;
diag.desc = diag.desc.replace('\n', ' ');
if (diag.desc.equals(""))
diag.desc = " ";
}
//if (tag.equals("title")) {
// diag.title = saveData;
//}
else if (endtag.equals("complang")) {
if (saveData.equals("NoFlo"))
saveData = "JSON"; // transitional!
diag.diagLang = driver
.findGLFromLabel(saveData);
}
else if (endtag.equals("generatedCodeFileName")) {
// diag.generatedCodeFileName = saveData; //
// there may be some in old diagrams, so do
// nothing!
}
else if (endtag.equals("clicktogrid")) {
diag.clickToGrid = saveData.equals("true");
}
else
item.put(endtag, saveData);
if (debugging)
System.out.println("Data at " + endtag + ": "
+ saveData);
data = null;
}
atomic = false;
}
else { // if not atomic and endsw is on - we're processing the end tag of a tag pair
if (endtag.equals("block")) {
Block block = null;
String type;
if (cEncl != null) {
block = cEncl;
type = Block.Types.ENCL_BLOCK;
cEncl = null;
} else {
type = item.get("type");
if (null == type) {
MyOptionPane.showMessageDialog(frame,
"No block type specified", MyOptionPane.ERROR_MESSAGE);
block = new ProcessBlock(diag);
} else if (type.equals(Block.Types.PROCESS_BLOCK)) {
block = new ProcessBlock(diag);
} else if (type
.equals(Block.Types.REPORT_BLOCK)) {
block = new ReportBlock(diag);
} else if (type.equals(Block.Types.FILE_BLOCK)) {
block = new FileBlock(diag);
} else if (type
.equals(Block.Types.EXTPORT_IN_BLOCK)
|| type.equals(Block.Types.EXTPORT_OUT_BLOCK)
|| type.equals(Block.Types.EXTPORT_OUTIN_BLOCK)) {
block = new ExtPortBlock(diag);
if (type
.equals(Block.Types.EXTPORT_OUTIN_BLOCK)) {
block.width = 2 * block.width;
}
block.type = type;
String sbs = item.get("substreamsensitive");
if (sbs != null && sbs.equals("true")){
if (block instanceof ExtPortBlock) {
ExtPortBlock eb = (ExtPortBlock) block;
eb.substreamSensitive = true;
}
if (snp != null)
snp.substreamSensitive = true;
}
} else if (type.equals(Block.Types.IIP_BLOCK)) {
block = new IIPBlock(diag);
} else if (type
.equals(Block.Types.LEGEND_BLOCK)) {
block = new LegendBlock(diag);
} else if (type
.equals(Block.Types.PERSON_BLOCK)) {
block = new PersonBlock(diag);
} else if (type.equals(Block.Types.ENCL_BLOCK)) {
block = new Enclosure(diag);
} else {
MyOptionPane.showMessageDialog(frame,
"Undefined block type", MyOptionPane.ERROR_MESSAGE);
block = new ProcessBlock(diag);
}
block.buildBlockFromXML(item);
block.calcEdges();
}
String s = item.get("multiplex");
block.multiplex = s != null && s.equals("true");
s = item.get("invisible");
block.visible = true;
if (s != null)
block.visible = s.equals("false");
diag.blocks.put(new Integer(block.id), block);
} else if (endtag.equals("connection")) {
if (!arrowBuilt) {
Integer aid = new Integer(item.get("id"));
diag.arrows.put(aid, thisArrow);
thisArrow.buildArrow(item);
}
thisArrow = null;
} else if (endtag.equals("bend")) {
if (thisArrow.bends == null)
thisArrow.bends = new LinkedList<Bend>();
Bend bend = new Bend();
bend.buildBend(item);
thisArrow.bends.add(bend);
} else if (endtag.equals("subnetport")) {
snp.buildBlockFromXML(item);
if (item.get("substreamsensitive").equals("true"))
snp.substreamSensitive = true;
cEncl.subnetPorts.add(snp);
snp = null;
}
clsNames.pop();
fldLists.pop();
if (!fldLists.empty())
curFl = (HashMap<String, String>) fldLists.peek();
else
curFl = null;
}
}
}
// if we have just read an end tag, a non-atomic name, or control
// (!?),
// skip following blanks to next non-blank (should be a <)
if (endsw || !atomic || control) {
while (true) { // skip blanks and tabs
if (!(bp.tb('o')))
break;
}
bp.eraseOutput(); // make sure no symbol outstanding
control = false;
}
// the effect of this is that data will include leading and trailing
// blanks
// or tabs, if any
while (true) {
if (bp.tc('\\', 'o')) { // back-slash followed by angle bracket
// -> angle bracket only
if (bp.tc('>'))
continue;
if (bp.tc('<'))
continue;
bp.w('\\');
continue;
}
if (bp.tc('<', 'o'))
break; // look for left bracket
// here we have found something that is not a < - it could be
// end of file, or it could be data
if (!bp.tu()) {
// at this point, we have processed the whole input stream
if (!names.empty())
MyOptionPane.showMessageDialog(frame, "Tags remaining: " + names.elementAt(0), MyOptionPane.WARNING_MESSAGE);
done = true;
break;
}
}
if (done)
break;
data = bp.getOutStr();
if (data != null) {
data = data.trim();
if (data.equals("null")) // .drw builder occasionally inserting "null"
data = "";
Pattern p = Pattern.compile("\\s*");
Matcher m = p.matcher(data);
if (!(data.equals("")) && !m.matches())
if (endsw || !atomic)
MyOptionPane.showMessageDialog(frame,
"Characters found not preceded by field start tag: \""
+ data + "\"", MyOptionPane.ERROR_MESSAGE);
}
} // end of loop
//if (sym.equals("clicktogrid")) {
// diag.clickToGrid = true;
// //driver.grid.setSelected(diag.clickToGrid);
// item.put(sym, "true");
//}
if (curFl != null)
MyOptionPane.showMessageDialog(frame,
"Tags not completely processed", MyOptionPane.WARNING_MESSAGE);
// check type compatibility for all arrows
// for (Arrow a: currentDiag.arrows.values()){
// checkCompatibility(a);
// }
//for (Block b : diag.blocks.values()) {
// b.checkArrows();
//}
if (diag.diagLang == null) {
diag.diagLang = driver.defaultCompLang;
diag.changed = true;
}
//else {
// driver.properties.put("defaultCompLang", diag.compLang.label);
// driver.propertiesChanged = true;
//}
driver.jtf.setText("Diagram Language: " + diag.diagLang.showLangs());
frame.repaint();
}
public static void createTables(
HashMap<String, HashMap<String, String>> tagInfo) {
HashMap<String, String> fl1 = new HashMap<String, String>();
fl1.put("title", "*"); // deprecated
//fl1.put("drawfbp_file", "LinkedList");
fl1.put("net", "LinkedList");
fl1.put("desc", "*");
fl1.put("complang", "*");
fl1.put("clicktogrid", "*");
fl1.put("genCodeFileName", "*"); //deprecated
// fl1.put("scalingFactor", "*");
fl1.put("generatedCodeFileName", "*"); // deprecated
fl1.put("genCodeFileNames", "*"); // deprecated
fl1.put("blocks", "LinkedList");
fl1.put("connections", "LinkedList");
fl1.put("description", "*"); // deprecated
HashMap<String, String> fl2 = new HashMap<String, String>();
fl2.put("block", "Block");
// fields in Block
HashMap<String, String> fl3 = new HashMap<String, String>();
fl3.put("x", "*");
fl3.put("y", "*");
fl3.put("id", "*");
fl3.put("description", "*");
fl3.put("codefilename", "*");
fl3.put("diagramfilename", "*");
fl3.put("compname", "*"); // deprecated
fl3.put("blockclassname", "*");
fl3.put("type", "*");
fl3.put("width", "*");
fl3.put("height", "*");
fl3.put("multiplex", "*");
fl3.put("mpxfactor", "*");
fl3.put("substreamsensitive", "*");
fl3.put("invisible", "*");
fl3.put("issubnet", "*");
fl3.put("subnetports", "LinkedList");
HashMap<String, String> fl4 = new HashMap<String, String>();
fl4.put("connection", "LinkedList");
// fields in Connection (Arrow)
HashMap<String, String> fl5 = new HashMap<String, String>();
fl5.put("fromx", "*");
fl5.put("fromy", "*");
fl5.put("tox", "*");
fl5.put("toy", "*");
fl5.put("fromid", "*");
fl5.put("toid", "*");
fl5.put("id", "*");
fl5.put("fromside", "*"); // deprecated
fl5.put("toside", "*"); // deprecated
fl5.put("upstreamport", "*");
fl5.put("downstreamport", "*");
fl5.put("dropoldest", "*");
fl5.put("capacity", "*");
fl5.put("endsatline", "*");
fl5.put("bends", "LinkedList");
HashMap<String, String> fl6 = new HashMap<String, String>();
fl6.put("bend", "Bend");
HashMap<String, String> fl7 = new HashMap<String, String>();
fl7.put("x", "*");
fl7.put("y", "*");
HashMap<String, String> fl8 = new HashMap<String, String>();
fl8.put("subnetport", "SubnetPort");
// field in SubnetPort
HashMap<String, String> fl9 = new HashMap<String, String>();
fl9.put("y", "*");
fl9.put("name", "*");
fl9.put("side", "*");
fl9.put("substreamsensitive", "*");
tagInfo.put("net", fl1);
tagInfo.put("drawfbp_file", fl1);
tagInfo.put("blocks", fl2);
tagInfo.put("block", fl3);
tagInfo.put("connections", fl4);
tagInfo.put("connection", fl5);
tagInfo.put("bends", fl6);
tagInfo.put("bend", fl7);
tagInfo.put("subnetports", fl8);
tagInfo.put("subnetport", fl9);
}
}