0001: /*
0002: * @(#)DefaultGraphModelFileFormatXML.java 1.0 17.02.2003
0003: *
0004: * Copyright (C) 2003 sven.luzar
0005: *
0006: * This program is free software; you can redistribute it and/or
0007: * modify it under the terms of the GNU General Public License
0008: * as published by the Free Software Foundation; either version 2
0009: * of the License, or (at your option) any later version.
0010: *
0011: * This program is distributed in the hope that it will be useful,
0012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
0014: * GNU General Public License for more details.
0015: *
0016: * You should have received a copy of the GNU General Public License
0017: * along with this program; if not, write to the Free Software
0018: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
0019: *
0020: */
0021:
0022: package org.lucane.applications.whiteboard.graph;
0023:
0024: import java.awt.Color;
0025: import java.awt.Dimension;
0026: import java.awt.Font;
0027: import java.awt.Point;
0028: import java.awt.Rectangle;
0029: import java.awt.geom.Point2D;
0030: import java.io.BufferedOutputStream;
0031: import java.io.File;
0032: import java.io.FileOutputStream;
0033: import java.io.InputStream;
0034: import java.io.OutputStream;
0035: import java.net.URL;
0036: import java.net.URLDecoder;
0037: import java.net.URLEncoder;
0038: import java.util.HashSet;
0039: import java.util.Hashtable;
0040: import java.util.Iterator;
0041: import java.util.LinkedList;
0042: import java.util.List;
0043: import java.util.Map;
0044: import java.util.Set;
0045: import java.util.Stack;
0046: import java.util.StringTokenizer;
0047:
0048: import javax.swing.BorderFactory;
0049: import javax.swing.Icon;
0050: import javax.swing.ImageIcon;
0051: import javax.swing.JCheckBox;
0052: import javax.swing.JComponent;
0053: import javax.swing.border.BevelBorder;
0054: import javax.swing.border.Border;
0055: import javax.swing.border.LineBorder;
0056: import javax.swing.filechooser.FileFilter;
0057: import javax.xml.parsers.DocumentBuilder;
0058: import javax.xml.parsers.DocumentBuilderFactory;
0059:
0060: import org.jgraph.JGraph;
0061: import org.jgraph.graph.AttributeMap;
0062: import org.jgraph.graph.ConnectionSet;
0063: import org.jgraph.graph.DefaultEdge;
0064: import org.jgraph.graph.DefaultGraphCell;
0065: import org.jgraph.graph.DefaultPort;
0066: import org.jgraph.graph.Edge;
0067: import org.jgraph.graph.GraphConstants;
0068: import org.jgraph.graph.GraphModel;
0069: import org.lucane.applications.whiteboard.graph.cells.DiamondCell;
0070: import org.lucane.applications.whiteboard.graph.cells.EllipseCell;
0071: import org.lucane.applications.whiteboard.graph.cells.ImageCell;
0072: import org.lucane.applications.whiteboard.graph.cells.RoundRectangleCell;
0073: import org.lucane.applications.whiteboard.graph.cells.TextCell;
0074: import org.w3c.dom.Document;
0075: import org.w3c.dom.Node;
0076:
0077: /**File format for the default graph model.
0078: * The file format writes a XML
0079: * file with the graph as content.
0080: *
0081: * @author luzar
0082: * @version 1.0
0083: */
0084: public class DefaultGraphModelFileFormatXML {
0085:
0086: private static DefaultGraphModelFileFormatXML _instance = new DefaultGraphModelFileFormatXML();
0087:
0088: public static final String EMPTY = new String("Empty");
0089:
0090: public static final String PARENT = new String("Parent");
0091:
0092: protected static Map cells = new Hashtable();
0093:
0094: protected static Map attrs = new Hashtable();
0095:
0096: protected static Map objs = new Hashtable();
0097:
0098: protected static List delayedAttributes;
0099:
0100: protected static List connectionSetIDs;
0101:
0102: protected Map cellMap = new Hashtable();
0103:
0104: protected AttributeCollection attrCol = new AttributeCollection();
0105:
0106: protected Map userObjectMap = new Hashtable();
0107:
0108: /** file filter for this file format
0109: */
0110: FileFilter fileFilter;
0111:
0112: /** accessory component. A checkbox for the
0113: * zipped output or unzipped output
0114: *
0115: */
0116: JComponent compZipSelect;
0117:
0118: /** a const value for the key at the properties hashtable
0119: */
0120: public static final String COMPRESS_WITH_ZIP = "CompressWithZip";
0121:
0122: public static DefaultGraphModelFileFormatXML instance() {
0123: if (null == _instance) {
0124: _instance = new DefaultGraphModelFileFormatXML();
0125: }
0126: return _instance;
0127: }
0128:
0129: /**
0130: * Constructor for DefaultGraphModelFileFormatXML.
0131: */
0132: protected DefaultGraphModelFileFormatXML() {
0133: fileFilter = new FileFilter() {
0134: /**
0135: * @see javax.swing.filechooser.FileFilter#accept(File)
0136: */
0137: public boolean accept(File f) {
0138: if (f == null)
0139: return false;
0140: if (f.getName() == null)
0141: return false;
0142: if (f.getName().endsWith(".jgx"))
0143: return true;
0144: if (f.isDirectory())
0145: return true;
0146:
0147: return false;
0148: }
0149:
0150: /**
0151: * @see javax.swing.filechooser.FileFilter#getDescription()
0152: */
0153: public String getDescription() {
0154: return "FileDesc.JGraphpadDiagramXml";
0155: }
0156: };
0157: compZipSelect = new JCheckBox("zipCompress");
0158: }
0159:
0160: /** returns <tt>pad_xml</tt>
0161: */
0162: public String getFileExtension() {
0163: return "jgx";
0164: }
0165:
0166: /** Returns a file filter for the <tt>pad_xml</tt> extension.
0167: *
0168: * @see org.jgraph.pad.GraphModelFileFormat#getFileFilter()
0169: */
0170: public FileFilter getFileFilter() {
0171: return fileFilter;
0172: }
0173:
0174: /** Returns null
0175: * @see org.jgraph.pad.GraphModelFileFormat#getReadAccessory()
0176: */
0177: public JComponent getReadAccessory() {
0178: return null;
0179: }
0180:
0181: /** Returns the compZipSelect object.
0182: *
0183: * @see #compZipSelect
0184: * @see org.jgraph.pad.GraphModelFileFormat#getWriteAccessory()
0185: */
0186: public JComponent getWriteAccessory() {
0187: return null; //compZipSelect;
0188: }
0189:
0190: /**
0191: * Writes the graph as XML file
0192: *
0193: * @see org.jgraph.pad.GraphModelFileFormat#write(String, Hashtable, GPGraph, GraphModel)
0194: */
0195: public void write(URL file, JGraph gpGraph, GraphModel graphModel)
0196: throws Exception {
0197:
0198: // don't try / catch this command
0199: // sothat we get error messages at the
0200: // frontend.
0201: // e.g. I you have not permissions to
0202: // write a file you should get an error message
0203:
0204: OutputStream out = null;
0205: out = new FileOutputStream(file.getFile());
0206: out = new BufferedOutputStream(out);
0207: out.write(toString(gpGraph).getBytes());
0208: out.flush();
0209: out.close();
0210: }
0211:
0212: /**
0213: * Puts the value from the checkbox into the properties hashtable
0214: *
0215: * @see org.jgraph.pad.GraphModelFileFormat#getWriteProperties(JComponent)
0216: */
0217: public Hashtable getWriteProperties(JComponent accessory) {
0218: Hashtable properties = new Hashtable();
0219: if (!(accessory instanceof JCheckBox)) {
0220: return properties;
0221: }
0222: properties.put(COMPRESS_WITH_ZIP, new Boolean(
0223: ((JCheckBox) accessory).isSelected()));
0224: return properties;
0225: }
0226:
0227: /**Reads the File form the XML input stream.
0228: * Tempts to load from a zipped input stream.
0229: * If this procedure fails the method tempts
0230: * to load without the zipped option.
0231: *
0232: * @see org.jgraph.pad.GraphModelFileFormat#read(String, Hashtable, GPGraph)
0233: */
0234: public GraphModel read(URL file, Hashtable properties,
0235: JGraph gpGraph) throws Exception {
0236:
0237: try {
0238: read(file.openStream(), gpGraph);
0239: } catch (Exception ex2) {
0240: if (file.toString().toLowerCase().startsWith("http")
0241: || file.toString().toLowerCase().startsWith("ftp")) {
0242: // do nothing create an empty graph
0243: } else {
0244: throw ex2;
0245: }
0246: }
0247:
0248: return gpGraph.getModel();
0249: }
0250:
0251: /**Returns null
0252: *
0253: * @see org.jgraph.pad.GraphModelFileFormat#getReadProperties(JComponent)
0254: */
0255: public Hashtable getReadProperties(JComponent accessory) {
0256: return null;
0257: }
0258:
0259: //
0260: // Read
0261: //
0262:
0263: public static void read(InputStream in, JGraph graph)
0264: throws Exception {
0265: GraphModel model = graph.getModel();
0266: // Create a DocumentBuilderFactory
0267: DocumentBuilderFactory dbf = DocumentBuilderFactory
0268: .newInstance();
0269: // Create a DocumentBuilder
0270: DocumentBuilder db = dbf.newDocumentBuilder();
0271: // Parse the input file to get a Document object
0272: Document doc = db.parse(in);
0273: // Get the first child (the jgx-element)
0274: Node modelNode = null;
0275: Node objsNode = null;
0276: Node attrsNode = null;
0277: Node viewNode = null;
0278:
0279: delayedAttributes = new LinkedList();
0280: connectionSetIDs = new LinkedList();
0281:
0282: for (int i = 0; i < doc.getDocumentElement().getChildNodes()
0283: .getLength(); i++) {
0284: Node node = doc.getDocumentElement().getChildNodes()
0285: .item(i);
0286: if (node.getNodeName().toLowerCase().equals("model")) {
0287: modelNode = node;
0288: } else if (node.getNodeName().toLowerCase().equals("user")) {
0289: objsNode = node;
0290: } else if (node.getNodeName().toLowerCase().equals("attrs")) {
0291: attrsNode = node;
0292: } else if (node.getNodeName().toLowerCase().equals("view")) {
0293: viewNode = node;
0294: }
0295: }
0296: objs = decodeUserObjects(objsNode);
0297: attrs = parseAttrs(attrsNode);
0298: attrs = augmentAttrs(attrs);
0299: Map settings = decodeMap(viewNode, false, false);
0300: ConnectionSet cs = new ConnectionSet();
0301: Hashtable cells = new Hashtable();
0302: DefaultGraphCell[] insert = parseChildren(modelNode, cells, cs);
0303:
0304: // Create ConnectionSet
0305: Iterator it = connectionSetIDs.iterator();
0306: while (it.hasNext()) {
0307: ConnectionID cid = (ConnectionID) it.next();
0308: Object cell = cid.getCell();
0309: String tid = cid.getTargetID();
0310: if (tid != null) {
0311: Object port = cells.get(tid);
0312: if (port != null) {
0313: cs.connect(cell, port, cid.isSource());
0314: }
0315: }
0316: }
0317:
0318: // Create AttributeMap
0319: Map nested = new Hashtable();
0320: it = delayedAttributes.iterator();
0321: while (it.hasNext()) {
0322: DelayedAttributeID att = (DelayedAttributeID) it.next();
0323: Map attr = (Map) attrs.get(att.getMapID());
0324: if (attr != null) {
0325: AttributeMap attr_temp = new AttributeMap(attr);
0326: attr = (Map) attr_temp.clone();
0327: if (att.getBounds() != null)
0328: GraphConstants.setBounds(attr, att.getBounds());
0329: if (att.getPoints() != null)
0330: GraphConstants.setPoints(attr, att.getPoints());
0331: nested.put(att.getCell(), attr);
0332: }
0333: }
0334:
0335: // Apply settings to graph
0336: applySettings(settings, graph);
0337:
0338: // Insert the cells (View stores attributes)
0339: model.insert(insert, nested, cs, null, null);
0340: }
0341:
0342: public static void applySettings(Map s, JGraph graph) {
0343: Object tmp;
0344:
0345: tmp = s.get("editable");
0346: if (tmp != null)
0347: graph.setEditable(new Boolean(tmp.toString())
0348: .booleanValue());
0349:
0350: tmp = s.get("bendable");
0351: if (tmp != null)
0352: graph.setBendable(new Boolean(tmp.toString())
0353: .booleanValue());
0354:
0355: tmp = s.get("cloneable");
0356: if (tmp != null)
0357: graph.setCloneable(new Boolean(tmp.toString())
0358: .booleanValue());
0359:
0360: tmp = s.get("connectable");
0361: if (tmp != null)
0362: graph.setConnectable(new Boolean(tmp.toString())
0363: .booleanValue());
0364:
0365: tmp = s.get("disconnectable");
0366: if (tmp != null)
0367: graph.setDisconnectable(new Boolean(tmp.toString())
0368: .booleanValue());
0369:
0370: tmp = s.get("disconnectOnMove");
0371: if (tmp != null)
0372: graph.setDisconnectOnMove(new Boolean(tmp.toString())
0373: .booleanValue());
0374:
0375: tmp = s.get("doubleBuffered");
0376: if (tmp != null)
0377: graph.setDoubleBuffered(new Boolean(tmp.toString())
0378: .booleanValue());
0379:
0380: tmp = s.get("dragEnabled");
0381: if (tmp != null)
0382: graph.setDragEnabled(new Boolean(tmp.toString())
0383: .booleanValue());
0384:
0385: tmp = s.get("dropEnabled");
0386: if (tmp != null)
0387: graph.setDropEnabled(new Boolean(tmp.toString())
0388: .booleanValue());
0389:
0390: tmp = s.get("moveable");
0391: if (tmp != null)
0392: graph.setMoveable(new Boolean(tmp.toString())
0393: .booleanValue());
0394:
0395: tmp = s.get("sizeable");
0396: if (tmp != null)
0397: graph.setSizeable(new Boolean(tmp.toString())
0398: .booleanValue());
0399:
0400: tmp = s.get("selectNewCells");
0401: if (tmp != null)
0402: graph.setSelectNewCells(new Boolean(tmp.toString())
0403: .booleanValue());
0404:
0405: tmp = s.get("gridVisible");
0406: if (tmp != null)
0407: graph.setGridVisible(new Boolean(tmp.toString())
0408: .booleanValue());
0409:
0410: tmp = s.get("gridEnabled");
0411: if (tmp != null)
0412: graph.setGridEnabled(new Boolean(tmp.toString())
0413: .booleanValue());
0414:
0415: tmp = s.get("gridSize");
0416: if (tmp != null)
0417: graph.setGridSize(Double.parseDouble(tmp.toString()));
0418:
0419: tmp = s.get("gridMode");
0420: if (tmp != null)
0421: graph.setGridMode(Integer.parseInt(tmp.toString()));
0422:
0423: tmp = s.get("scale");
0424: if (tmp != null)
0425: graph.setScale(Double.parseDouble(tmp.toString()));
0426:
0427: tmp = s.get("antiAlias");
0428: if (tmp != null)
0429: graph.setAntiAliased(new Boolean(tmp.toString())
0430: .booleanValue());
0431:
0432: }
0433:
0434: public static Map augmentAttrs(Map attrs) {
0435: Map newAttrs = new Hashtable();
0436: Iterator it = attrs.entrySet().iterator();
0437: while (it.hasNext()) {
0438: Map.Entry entry = (Map.Entry) it.next();
0439: Object key = entry.getKey();
0440: Map map = (Map) entry.getValue();
0441: Stack s = new Stack();
0442: s.add(map);
0443: Object parentID = map.get(PARENT);
0444: Object hook = null;
0445: while (parentID != null) {
0446: hook = attrs.get(parentID);
0447: s.add(hook);
0448: parentID = ((Map) hook).get(PARENT);
0449: }
0450: Map newMap = new Hashtable();
0451: while (!s.isEmpty()) {
0452: newMap.putAll((Map) s.pop());
0453: }
0454: newMap.remove(PARENT);
0455: // Remove Empty values
0456: Iterator it2 = newMap.entrySet().iterator();
0457: while (it2.hasNext()) {
0458: entry = (Map.Entry) it2.next();
0459: if (entry.getValue() == EMPTY)
0460: it2.remove();
0461: }
0462: newAttrs.put(key, newMap);
0463: }
0464: return newAttrs;
0465: }
0466:
0467: public static DefaultGraphCell parseCell(Node node,
0468: Hashtable cells, ConnectionSet cs) {
0469: DefaultGraphCell cell = null;
0470: if (node.getNodeName().toLowerCase().equals("a")) {
0471: Node key = node.getAttributes().getNamedItem("id");
0472: Node type = node.getAttributes().getNamedItem("class");
0473: if (key != null && type != null) {
0474: Node value = node.getAttributes().getNamedItem("val");
0475: Object userObject = "";
0476: if (value != null)
0477: userObject = objs.get(value.getNodeValue());
0478: cell = createCell(type.getNodeValue(), userObject);
0479:
0480: if (cell != null) {
0481: cells.put(key.getNodeValue(), cell);
0482:
0483: DefaultGraphCell[] children = parseChildren(node,
0484: cells, cs);
0485: for (int i = 0; i < children.length; i++)
0486: cell.add(children[i]);
0487:
0488: Node source = node.getAttributes().getNamedItem(
0489: "src");
0490: Node target = node.getAttributes().getNamedItem(
0491: "tgt");
0492: if (source != null) {
0493: ConnectionID cid = new ConnectionID(cell,
0494: source.getNodeValue(), true);
0495: connectionSetIDs.add(cid);
0496: }
0497: if (target != null) {
0498: ConnectionID cid = new ConnectionID(cell,
0499: target.getNodeValue(), false);
0500: connectionSetIDs.add(cid);
0501: }
0502:
0503: Node boundsNode = node.getAttributes()
0504: .getNamedItem("rect");
0505: Rectangle bounds = null;
0506: if (boundsNode != null) {
0507: Object rectangle = decodeValue(Rectangle.class,
0508: boundsNode.getNodeValue());
0509: if (rectangle instanceof Rectangle)
0510: bounds = (Rectangle) rectangle;
0511: }
0512:
0513: Node pointsNode = node.getAttributes()
0514: .getNamedItem("pts");
0515: List points = null;
0516: if (pointsNode != null) {
0517: Object pointList = decodeValue(List.class,
0518: pointsNode.getNodeValue());
0519: if (pointList instanceof List)
0520: points = (List) pointList;
0521: }
0522:
0523: Node attr = node.getAttributes().getNamedItem(
0524: "attr");
0525: String mapID = null;
0526: if (attr != null)
0527: mapID = attr.getNodeValue();
0528:
0529: if (mapID != null)
0530: delayedAttributes.add(new DelayedAttributeID(
0531: cell, bounds, points, mapID));
0532: }
0533: }
0534: }
0535: return cell;
0536: }
0537:
0538: public static DefaultGraphCell[] parseChildren(Node node,
0539: Hashtable cells, ConnectionSet cs) {
0540: List list = new LinkedList();
0541: for (int i = 0; i < node.getChildNodes().getLength(); i++) {
0542: Node child = node.getChildNodes().item(i);
0543: DefaultGraphCell cell = parseCell(child, cells, cs);
0544: if (cell != null)
0545: list.add(cell);
0546: }
0547: DefaultGraphCell[] dgc = new DefaultGraphCell[list.size()];
0548: list.toArray(dgc);
0549: return dgc;
0550: }
0551:
0552: public static Map parseAttrs(Node node) {
0553: Hashtable map = new Hashtable();
0554: for (int i = 0; i < node.getChildNodes().getLength(); i++) {
0555: Node child = node.getChildNodes().item(i);
0556: if (child.getNodeName().toLowerCase().equals("map")) {
0557: Node key = child.getAttributes().getNamedItem("id");
0558: Node pid = child.getAttributes().getNamedItem("pid");
0559: Map attrs = decodeMap(child, true, false);
0560: if (key != null && attrs.size() > 0) {
0561: if (pid != null)
0562: attrs.put(PARENT, pid.getNodeValue());
0563: map.put(key.getNodeValue(), attrs);
0564: }
0565: }
0566: }
0567: return map;
0568: }
0569:
0570: /**
0571: * Returns an attributeMap for the specified position and color.
0572: */
0573: public static Map createDefaultAttributes() {
0574: // Create an AttributeMap
0575: AttributeMap map = new AttributeMap();
0576: // Set a Black Line Border (the Border-Attribute must be Null!)
0577: GraphConstants.setBorderColor(map, Color.black);
0578: // Return the Map
0579: return map;
0580: }
0581:
0582: public static class DelayedAttributeID {
0583:
0584: protected Object cell;
0585:
0586: protected Rectangle bounds;
0587:
0588: protected List points;
0589:
0590: protected String mapID;
0591:
0592: public DelayedAttributeID(Object cell, Rectangle bounds,
0593: List points, String mapID) {
0594: this .cell = cell;
0595: this .bounds = bounds;
0596: this .points = points;
0597: this .mapID = mapID;
0598: }
0599:
0600: /**
0601: * @return
0602: */
0603: public Object getCell() {
0604: return cell;
0605: }
0606:
0607: /**
0608: * @return
0609: */
0610: public Rectangle getBounds() {
0611: return bounds;
0612: }
0613:
0614: /**
0615: * @return
0616: */
0617: public String getMapID() {
0618: return mapID;
0619: }
0620:
0621: /**
0622: * @return
0623: */
0624: public List getPoints() {
0625: return points;
0626: }
0627:
0628: /**
0629: * @param rectangle
0630: */
0631: public void setBounds(Rectangle rectangle) {
0632: bounds = rectangle;
0633: }
0634:
0635: /**
0636: * @param object
0637: */
0638: public void setCell(Object object) {
0639: cell = object;
0640: }
0641:
0642: /**
0643: * @param string
0644: */
0645: public void setMapID(String string) {
0646: mapID = string;
0647: }
0648:
0649: /**
0650: * @param list
0651: */
0652: public void setPoints(List list) {
0653: points = list;
0654: }
0655:
0656: }
0657:
0658: public static class ConnectionID {
0659:
0660: protected Object cell;
0661:
0662: protected String targetID;
0663:
0664: protected boolean source;
0665:
0666: public ConnectionID(Object cell, String targetID, boolean source) {
0667: this .cell = cell;
0668: this .targetID = targetID;
0669: this .source = source;
0670: }
0671:
0672: /**
0673: * @return
0674: */
0675: public Object getCell() {
0676: return cell;
0677: }
0678:
0679: /**
0680: * @return
0681: */
0682: public boolean isSource() {
0683: return source;
0684: }
0685:
0686: /**
0687: * @return
0688: */
0689: public String getTargetID() {
0690: return targetID;
0691: }
0692:
0693: /**
0694: * @param object
0695: */
0696: public void setCell(Object object) {
0697: cell = object;
0698: }
0699:
0700: /**
0701: * @param b
0702: */
0703: public void setSource(boolean b) {
0704: source = b;
0705: }
0706:
0707: /**
0708: * @param string
0709: */
0710: public void setTargetID(String string) {
0711: targetID = string;
0712: }
0713:
0714: }
0715:
0716: //
0717: // Write
0718: //
0719:
0720: public String toString(JGraph graph) {
0721: userObjectMap.clear();
0722: cellMap.clear();
0723: attrs.clear();
0724:
0725: String xml = "<jgx-1.01>\n";
0726:
0727: GraphModel model = graph.getModel();
0728: xml += "<model>\n";
0729: xml += outputModel(model, "\t", null);
0730: xml += "</model>\n";
0731:
0732: xml += "<attrs>\n";
0733: xml += outputAttributes("\t");
0734: xml += "</attrs>\n";
0735:
0736: xml += "<user>\n";
0737: xml += encodeUserObjects("\t", userObjectMap);
0738: xml += "</user>\n";
0739:
0740: xml += "<view>\n";
0741: xml += outputView(graph, "\t");
0742: xml += "</view>\n";
0743:
0744: // Close main tags
0745: xml += "</jgx-1.01>\n";
0746: return xml;
0747: }
0748:
0749: public String outputView(JGraph graph, String indent) {
0750: String xml = indent + "<a key=\"editable\" val=\""
0751: + graph.isEditable() + "\"/>\n" + indent
0752: + "<a key=\"bendable\" val=\"" + graph.isBendable()
0753: + "\"/>\n" + indent + "<a key=\"cloneable\" val=\""
0754: + graph.isCloneable() + "\"/>\n" + indent
0755: + "<a key=\"connectable\" val=\""
0756: + graph.isConnectable() + "\"/>\n" + indent
0757: + "<a key=\"disconnectable\" val=\""
0758: + graph.isDisconnectable() + "\"/>\n" + indent
0759: + "<a key=\"disconnectOnMove\" val=\""
0760: + graph.isDisconnectOnMove() + "\"/>\n" + indent
0761: + "<a key=\"doubleBuffered\" val=\""
0762: + graph.isDoubleBuffered() + "\"/>\n" + indent
0763: + "<a key=\"dragEnabled\" val=\""
0764: + graph.isDragEnabled() + "\"/>\n" + indent
0765: + "<a key=\"dropEnabled\" val=\""
0766: + graph.isDropEnabled() + "\"/>\n" + indent
0767: + "<a key=\"moveable\" val=\"" + graph.isMoveable()
0768: + "\"/>\n" + indent + "<a key=\"sizeable\" val=\""
0769: + graph.isSizeable() + "\"/>\n" + indent
0770: + "<a key=\"selectNewCells\" val=\""
0771: + graph.isSelectNewCells() + "\"/>\n" + indent
0772: + "<a key=\"gridVisible\" val=\""
0773: + graph.isGridVisible() + "\"/>\n" + indent
0774: + "<a key=\"gridEnabled\" val=\""
0775: + graph.isGridEnabled() + "\"/>\n" + indent
0776: + "<a key=\"gridSize\" val=\"" + graph.getGridSize()
0777: + "\"/>\n" + indent + "<a key=\"gridMode\" val=\""
0778: + graph.getGridMode() + "\"/>\n" + indent
0779: + "<a key=\"scale\" val=\"" + graph.getScale()
0780: + "\"/>\n" + indent + "<a key=\"antiAlias\" val=\""
0781: + graph.isAntiAliased() + "\"/>\n";
0782: return xml;
0783: }
0784:
0785: public String outputModel(GraphModel model, String indent,
0786: Object parent) {
0787: String xml = new String("");
0788: int max = (parent != null) ? model.getChildCount(parent)
0789: : model.getRootCount();
0790: for (int i = 0; i < max; i++) {
0791: Object cell = (parent != null) ? model.getChild(parent, i)
0792: : model.getRootAt(i);
0793: if (cell != null)
0794: xml += outputCell(indent, model, cell);
0795: }
0796: return xml;
0797: }
0798:
0799: public String outputCell(String indent, GraphModel model,
0800: Object cell) {
0801: Map map = new Hashtable(model.getAttributes(cell));
0802: Rectangle r = (Rectangle) map.remove(GraphConstants.BOUNDS);
0803: Object value = map.remove(GraphConstants.VALUE);
0804: if (GraphConstants.getFont(map).equals(
0805: GraphConstants.DEFAULTFONT))
0806: map.remove(GraphConstants.FONT);
0807: Object source = model.getSource(cell);
0808: Object target = model.getTarget(cell);
0809: if (GraphConstants.getRouting(map) != null)
0810: map.remove(GraphConstants.POINTS);
0811: String sourceID = "";
0812: String targetID = "";
0813: if (source != null)
0814: sourceID = " src=\"" + getID(source) + "\"";
0815: if (target != null)
0816: targetID = " tgt=\"" + getID(target) + "\"";
0817: String bounds = "";
0818: String valueS = "";
0819: if (r != null && !model.isEdge(cell) && !model.isPort(cell)
0820: && !r.equals(DefaultGraphCell.defaultBounds))
0821: bounds = " rect=\"" + encodeValue(r) + "\"";
0822: List p = GraphConstants.getPoints(map);
0823: map.remove(GraphConstants.POINTS);
0824: String points = "";
0825: if (p != null) {
0826: String tmp = encodeValue(p);
0827: if (tmp.length() > 0)
0828: points = " pts=\"" + tmp + "\"";
0829: }
0830: if (value != null)
0831: valueS = " val=\"" + getUserObjectID(value) + "\"";
0832: String attrID = "";
0833: if (map.size() > 0)
0834: attrID = " attr=\"" + attrCol.addMap(map) + "\"";
0835: String xml = new String(indent + "<a class=\"" + getType(cell)
0836: + "\" id=\"" + getID(cell) + "\"" + valueS + sourceID
0837: + targetID + bounds + points + attrID);
0838: if (model.getChildCount(cell) > 0)
0839: xml += ">\n" + outputModel(model, indent + "\t", cell)
0840: + indent + "</a>\n";
0841: else
0842: xml += "/>\n";
0843: return xml;
0844: }
0845:
0846: public int getUserObjectID(Object object) {
0847: Integer index = (Integer) userObjectMap.get(object);
0848: if (index != null)
0849: return index.intValue();
0850: index = new Integer(userObjectMap.size() + 1);
0851: userObjectMap.put(object, index);
0852: return index.intValue();
0853: }
0854:
0855: public int getID(Object object) {
0856: Integer index = (Integer) cellMap.get(object);
0857: if (index != null)
0858: return index.intValue();
0859: index = new Integer(cellMap.size() + 1);
0860: cellMap.put(object, index);
0861: return index.intValue();
0862: }
0863:
0864: public String outputAttributes(String indent) {
0865: Set set = new HashSet();
0866: set.add(PARENT);
0867: set.add(GraphConstants.BOUNDS);
0868: set.add(GraphConstants.POINTS);
0869:
0870: String xml = new String();
0871: for (int i = 0; i < attrCol.maps.size(); i++) {
0872: Map map = (Map) attrCol.maps.get(i);
0873: Object hook = map.get(PARENT);
0874: String hookS = "";
0875: if (hook != null)
0876: hookS = " pid=\"" + attrCol.maps.indexOf(hook) + "\"";
0877: xml += indent + "<map id=\"" + i + "\"" + hookS + ">\n";
0878: xml += encodeMap(indent + "\t", map, false, set, false);
0879: xml += indent + "</map>\n";
0880: }
0881: return xml;
0882: }
0883:
0884: public class AttributeCollection {
0885:
0886: public List maps = new LinkedList();
0887:
0888: public int addMap(Map attr) {
0889: Iterator it = maps.iterator();
0890: Map storeMap = new Hashtable(attr);
0891: Map hook = storeMap;
0892: while (it.hasNext()) {
0893: Map ref = (Map) it.next();
0894: Map diff = diffMap(ref, attr);
0895: if (diff.size() < storeMap.size()) {
0896: hook = ref;
0897: storeMap = diff;
0898: }
0899: }
0900: if (storeMap.size() == 0 && hook != storeMap)
0901: return maps.indexOf(hook);
0902: if (hook != storeMap)
0903: storeMap.put(PARENT, hook);
0904: maps.add(storeMap);
0905: return maps.indexOf(storeMap);
0906: }
0907:
0908: public void clear() {
0909: maps.clear();
0910: }
0911:
0912: /**
0913: * Returns a new map that contains all (key, value)-pairs
0914: * of <code>newState</code> where either key is not used
0915: * or value is different for key in <code>oldState</code>.
0916: * In other words, this method removes the common entries
0917: * from oldState and newState, and returns the "difference"
0918: * between the two.
0919: *
0920: * This method never returns null.
0921: */
0922: public Map diffMap(Map oldState, Map newState) {
0923: // Augment oldState
0924: Stack s = new Stack();
0925: s.add(oldState);
0926: Object hook = oldState.get(PARENT);
0927: while (hook instanceof Map) {
0928: s.add(hook);
0929: hook = ((Map) hook).get(PARENT);
0930: }
0931: oldState = new Hashtable();
0932: while (!s.isEmpty()) {
0933: oldState.putAll((Map) s.pop());
0934: }
0935: Map diff = new Hashtable();
0936: Iterator it = newState.entrySet().iterator();
0937: while (it.hasNext()) {
0938: Map.Entry entry = (Map.Entry) it.next();
0939: Object key = entry.getKey();
0940: Object oldValue = oldState.remove(key);
0941: if (key != PARENT) {
0942: Object newValue = entry.getValue();
0943: if (oldValue == null || !oldValue.equals(newValue))
0944: diff.put(key, newValue);
0945: }
0946: }
0947: it = oldState.keySet().iterator();
0948: while (it.hasNext()) {
0949: Object key = it.next();
0950: if (!oldState.get(key).equals(""))
0951: diff.put(key, "");
0952: }
0953: diff.remove(PARENT);
0954: return diff;
0955: }
0956:
0957: }
0958:
0959: //
0960: // Codec
0961: //
0962:
0963: public static String[] knownKeys = new String[] {
0964: GraphConstants.ABSOLUTE, GraphConstants.AUTOSIZE,
0965: GraphConstants.BACKGROUND, GraphConstants.BEGINFILL,
0966: GraphConstants.BEGINSIZE, GraphConstants.BENDABLE,
0967: GraphConstants.BORDER, GraphConstants.BORDERCOLOR,
0968: GraphConstants.BOUNDS, GraphConstants.CONNECTABLE,
0969: GraphConstants.DASHPATTERN, GraphConstants.DISCONNECTABLE,
0970: GraphConstants.EDITABLE, GraphConstants.ENDFILL,
0971: GraphConstants.ENDSIZE, GraphConstants.FONT,
0972: GraphConstants.FOREGROUND,
0973: GraphConstants.HORIZONTAL_ALIGNMENT,
0974: GraphConstants.VERTICAL_ALIGNMENT, GraphConstants.ICON,
0975: GraphConstants.LABELPOSITION, GraphConstants.LINEBEGIN,
0976: GraphConstants.LINECOLOR, GraphConstants.LINEEND,
0977: GraphConstants.LINESTYLE, GraphConstants.LINEWIDTH,
0978: GraphConstants.MOVEABLE, GraphConstants.OFFSET,
0979: GraphConstants.OPAQUE, GraphConstants.POINTS,
0980: GraphConstants.ROUTING, GraphConstants.SIZE,
0981: GraphConstants.SIZEABLE, GraphConstants.VALUE };
0982:
0983: public static Class[] keyTypes = new Class[] { Boolean.class,
0984: Boolean.class, Color.class, Boolean.class, Integer.class,
0985: Boolean.class, Border.class, Color.class, Rectangle.class,
0986: Boolean.class, float[].class, Boolean.class, Boolean.class,
0987: Boolean.class, Integer.class, Font.class, Color.class,
0988: Integer.class, Integer.class, Icon.class, Point.class,
0989: Integer.class, Color.class, Integer.class, Integer.class,
0990: Float.class, Boolean.class, Point.class, Boolean.class,
0991: List.class, Edge.Routing.class, Dimension.class,
0992: Boolean.class, Object.class };
0993:
0994: public static String encodeMap(String indent, Map attributes,
0995: boolean invert, Set excludeAttributes,
0996: boolean URLencodeValues) {
0997: String xml = new String("");
0998: Iterator it = attributes.entrySet().iterator();
0999: while (it.hasNext()) {
1000: Map.Entry entry = (Map.Entry) it.next();
1001: Object key = entry.getKey();
1002: if (excludeAttributes == null
1003: || !excludeAttributes.contains(key)) {
1004: Object value = entry.getValue();
1005: if (invert) {
1006: Object tmp = key;
1007: key = value;
1008: value = tmp;
1009: }
1010: if (URLencodeValues) {
1011: try {
1012: key = URLEncoder
1013: .encode(key.toString(), "UTF-8");
1014: value = URLEncoder.encode(encodeValue(value),
1015: "UTF-8");
1016: } catch (Exception e) {
1017: System.err.println(e.getMessage());
1018: }
1019: }
1020: xml += indent + "<a key=\"" + encodeKey(key.toString())
1021: + "\" val=\"" + encodeValue(value) + "\"/>\n";
1022: }
1023: }
1024: return xml;
1025: }
1026:
1027: public static String encodeUserObjects(String indent,
1028: Map userObjects) {
1029: String xml = new String("");
1030: Iterator it = userObjects.entrySet().iterator();
1031: while (it.hasNext()) {
1032: Map.Entry entry = (Map.Entry) it.next();
1033: Object key = entry.getValue();
1034: Object value = entry.getKey();
1035: /*if (value instanceof GPUserObject) {
1036: xml += indent
1037: + "<a key=\""
1038: + encodeKey(key.toString())
1039: + "\"";
1040: Map map = ((GPUserObject) value).getProperties();
1041: xml += ">\n" + encodeMap(indent+"\t", map, false, null, true)
1042: + indent + "</a>\n";
1043: } else */{
1044: try {
1045: value = URLEncoder.encode(encodeValue(value),
1046: "UTF-8");
1047: } catch (Exception e) {
1048: System.err.println(e.getMessage());
1049: }
1050: xml += indent + "<a key=\"" + encodeKey(key.toString())
1051: + "\" val=\"" + value + "\"/>\n";
1052: }
1053: }
1054: return xml;
1055: }
1056:
1057: public static String encodeKey(String key) {
1058: // for (int i = 0; i < knownKeys.length; i++)
1059: // if (key.equals(knownKeys[i]))
1060: // return Integer.toString(i);
1061: return key;
1062: }
1063:
1064: public static String encodeValue(Object value) {
1065: String ret = "";
1066: if (value instanceof Rectangle) {
1067: Rectangle r = (Rectangle) value;
1068: ret = r.x + "," + r.y + "," + r.width + "," + r.height;
1069: } else if (value instanceof List) { // TODO: non-points
1070: List list = (List) value;
1071: String s = "";
1072: for (int i = 0; i < list.size(); i++) {
1073: if (list.get(i) instanceof Point2D) {
1074: Point2D pt = (Point2D) list.get(i);
1075: s = s + pt.getX() + "," + pt.getY() + ",";
1076: }
1077: }
1078: ret = (s.length() > 0) ? s.substring(0, s.length() - 1) : s;
1079: } else if (value instanceof Font) {
1080: Font font = (Font) value;
1081: ret = font.getName() + "," + font.getSize() + ","
1082: + font.getStyle();
1083: } else if (value instanceof Color) {
1084: Color color = (Color) value;
1085: ret = Integer.toString(color.getRed()) + ","
1086: + Integer.toString(color.getGreen()) + ","
1087: + Integer.toString(color.getBlue());
1088: } else if (value instanceof Point) {
1089: Point point = (Point) value;
1090: ret = point.x + "," + point.y;
1091: } else if (value instanceof float[]) {
1092: float[] f = (float[]) value;
1093: String s = "";
1094: for (int i = 0; i < f.length; i++) {
1095: s = s + Float.toString(f[i]) + ",";
1096: }
1097: ret = s.substring(0, s.length() - 1);
1098: } else if (value instanceof Border) {
1099: if (value instanceof LineBorder) {
1100: LineBorder lb = (LineBorder) value;
1101: ret = "L," + lb.getLineColor().getRGB() + ","
1102: + lb.getThickness();
1103: } else if (value instanceof BevelBorder) {
1104: BevelBorder bb = (BevelBorder) value;
1105: ret = "B," + bb.getBevelType();
1106: }
1107: } /*else if (value instanceof ImageIconBean) {
1108: ImageIconBean icon = (ImageIconBean) value;
1109: ret = icon.getFileName();
1110: }*/else if (value instanceof Edge.Routing) {
1111: if (value instanceof DefaultEdge.DefaultRouting)
1112: ret = "simple";
1113: } else if (value != null)
1114: ret = value.toString();
1115: return ret;
1116: }
1117:
1118: public static Map decodeMap(Node node, boolean useKnownKeys,
1119: boolean URLdecodeValues) {
1120: Hashtable map = new Hashtable();
1121: for (int i = 0; i < node.getChildNodes().getLength(); i++) {
1122: Node child = node.getChildNodes().item(i);
1123: if (child.getNodeName().toLowerCase().equals("a")) {
1124: Node key = child.getAttributes().getNamedItem("key");
1125: Node value = child.getAttributes().getNamedItem("val");
1126: if (key != null && value != null) {
1127: String keyVal = key.getNodeValue().toString();
1128: Object valueS = value.getNodeValue().toString();
1129: if (useKnownKeys) {
1130: int index = -1;
1131: for (int j = 0; j < knownKeys.length; j++)
1132: if (keyVal.equals(knownKeys[j]))
1133: index = j;
1134: if (index != -1)
1135: valueS = decodeValue(keyTypes[index],
1136: valueS.toString());
1137: } else if (URLdecodeValues) {
1138:
1139: try {
1140: keyVal = URLDecoder.decode(keyVal
1141: .toString(), "UTF-8");
1142: valueS = URLDecoder.decode(valueS
1143: .toString(), "UTF-8");
1144: } catch (Exception e) {
1145: System.err.println(e.getMessage());
1146: }
1147: }
1148: if (valueS != null)
1149: map.put(keyVal, valueS);
1150: }
1151: }
1152: }
1153: return map;
1154: }
1155:
1156: public static Map decodeUserObjects(Node node) {
1157: Hashtable map = new Hashtable();
1158: for (int i = 0; i < node.getChildNodes().getLength(); i++) {
1159: Node child = node.getChildNodes().item(i);
1160: if (child.getNodeName().toLowerCase().equals("a")) {
1161: Node key = child.getAttributes().getNamedItem("key");
1162: Node value = child.getAttributes().getNamedItem("val");
1163: if (key != null) {
1164: String keyVal = key.getNodeValue().toString();
1165: if (value != null) {
1166: Object valueS = value.getNodeValue().toString();
1167: if (valueS != null) {
1168: try {
1169: valueS = URLDecoder.decode(valueS
1170: .toString(), "UTF-8");
1171: } catch (Exception e) {
1172: System.err.println(e.getMessage());
1173: }
1174: map.put(keyVal, valueS);
1175: }
1176: } else {
1177: Map properties = decodeMap(child, false, true);
1178: if (properties != null)
1179: map.put(keyVal, properties);
1180: }
1181: }
1182: }
1183: }
1184: return map;
1185: }
1186:
1187: public static String[] tokenize(String s, String token) {
1188: StringTokenizer tokenizer = new StringTokenizer(s, token);
1189: String[] tok = new String[tokenizer.countTokens()];
1190: int i = 0;
1191: while (tokenizer.hasMoreElements()) {
1192: tok[i++] = tokenizer.nextToken();
1193: }
1194: return tok;
1195: }
1196:
1197: public static Object decodeValue(Class key, String value) {
1198: if (key != String.class && key != Object.class
1199: && (value == null || value.equals("")))
1200: return EMPTY;
1201: if (key == Rectangle.class) {
1202: String[] tok = tokenize(value, ",");
1203: if (tok.length == 4) {
1204: int x = Integer.parseInt(tok[0]);
1205: int y = Integer.parseInt(tok[1]);
1206: int w = Integer.parseInt(tok[2]);
1207: int h = Integer.parseInt(tok[3]);
1208: return new Rectangle(x, y, w, h);
1209: }
1210: } else if (key == List.class) { // FIX: Do not assume Points!
1211: List list = new LinkedList();
1212: String[] tok = tokenize(value, ",");
1213: for (int i = 0; i < tok.length; i = i + 2) {
1214: double x = Double.parseDouble(tok[i]);
1215: double y = Double.parseDouble(tok[i + 1]);
1216: AttributeMap dummyMap = new AttributeMap();
1217: Point2D point = dummyMap.createPoint(x, y);
1218: list.add(point);
1219: }
1220: return list;
1221: } else if (key == Font.class) {
1222: String[] tok = tokenize(value, ",");
1223: if (tok.length == 3) {
1224: String name = tok[0];
1225: int size = Integer.parseInt(tok[1]);
1226: int style = Integer.parseInt(tok[2]);
1227: return new Font(name, style, size);
1228: }
1229: } else if (key == Color.class) {
1230: String[] tok = tokenize(value, ",");
1231: if (tok.length == 3) {
1232: int r = Integer.parseInt(tok[0]);
1233: int g = Integer.parseInt(tok[1]);
1234: int b = Integer.parseInt(tok[2]);
1235: return new Color(r, g, b);
1236: }
1237: return new Color(Integer.parseInt(value));
1238: } else if (key == Point.class) {
1239: String[] tok = tokenize(value, ",");
1240: if (tok.length == 2) {
1241: int x = Integer.parseInt(tok[0]);
1242: int y = Integer.parseInt(tok[1]);
1243: return new Point(x, y);
1244: }
1245: } else if (key == float[].class) {
1246: String[] tok = tokenize(value, ",");
1247: float[] f = new float[tok.length];
1248: for (int i = 0; i < tok.length; i++)
1249: f[i] = Float.parseFloat(tok[i]);
1250: return f;
1251: } else if (key == Integer.class) {
1252: return new Integer(value);
1253: } else if (key == Border.class) {
1254: String[] tok = tokenize(value, ",");
1255: if (tok[0].equals("L")) { // LineBorder
1256: Color c = new Color(Integer.parseInt(tok[1]));
1257: int thickness = Integer.parseInt(tok[2]);
1258: return BorderFactory.createLineBorder(c, thickness);
1259: } else if (tok[0].equals("B")) { // BevelBorder
1260: int type = Integer.parseInt(tok[1]);
1261: return BorderFactory.createBevelBorder(type);
1262: }
1263: return BorderFactory.createLineBorder(Color.black, 1);
1264: } else if (key == Boolean.class) {
1265: return new Boolean(value);
1266: } else if (key == Float.class) {
1267: return new Float(value);
1268: } else if (key == Icon.class) {
1269: return new ImageIcon(value);
1270: } else if (key == Edge.Routing.class) {
1271: if (value.equals("simple"))
1272: return GraphConstants.ROUTING_SIMPLE;
1273: }
1274: return value;
1275: }
1276:
1277: //
1278: // Cell Factory
1279: //
1280:
1281: public static DefaultGraphCell createCell(String type,
1282: Object userObject) {
1283: //if (userObject instanceof Map)
1284: // userObject = new GPUserObject((Map) userObject);
1285: if (type.equals("rect"))
1286: return new DefaultGraphCell(userObject);
1287: else if (type.equals("text"))
1288: return new TextCell(userObject);
1289: else if (type.equals("diamond"))
1290: return new DiamondCell(userObject);
1291: else if (type.equals("roundrect"))
1292: return new RoundRectangleCell(userObject);
1293: else if (type.equals("ellipse"))
1294: return new EllipseCell(userObject);
1295: else if (type.equals("image")) //TODO check if the image file is still here
1296: return new ImageCell(userObject);
1297: else if (type.equals("port"))
1298: return new DefaultPort(userObject);
1299: else if (type.equals("edge"))
1300: return new DefaultEdge(userObject);
1301: return null;
1302: }
1303:
1304: public static String getType(Object cell) {
1305: if (cell instanceof DefaultPort)
1306: return "port";
1307: else if (cell instanceof TextCell)
1308: return "text";
1309: else if (cell instanceof DiamondCell)
1310: return "diamond";
1311: else if (cell instanceof RoundRectangleCell)
1312: return "roundrect";
1313: else if (cell instanceof EllipseCell)
1314: return "ellipse";
1315: else if (cell instanceof ImageCell)
1316: return "image";
1317: else if (cell instanceof DefaultEdge)
1318: return "edge";
1319: return "rect";
1320: }
1321:
1322: }
|