/*FreeMind - A Program for creating and viewing Mindmaps *Copyright (C) 2000-2001 Joerg Mueller <joergmueller@bigfoot.com> *See COPYING for Details * *This program is free software; you can redistribute it and/or *modify it under the terms of the GNU General Public License *as published by the Free Software Foundation; either version 2 *of the License, or (at your option) any later version. * *This program is distributed in the hope that it will be useful, *but WITHOUT ANY WARRANTY; without even the implied warranty of *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *GNU General Public License for more details. * *You should have received a copy of the GNU General Public License *along with this program; if not, write to the Free Software *Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package freemind.modes; import java.awt.Font; import java.util.HashMap; import java.util.Iterator; import java.util.Vector; import freemind.extensions.PermanentNodeHook; import freemind.extensions.PermanentNodeHookSubstituteUnknown; import freemind.main.FreeMindMain; import freemind.main.Tools; import freemind.main.XMLElement; import freemind.modes.attributes.Attribute; import freemind.modes.attributes.AttributeRegistry; import freemind.modes.attributes.AttributeTableLayoutModel; public abstract class XMLElementAdapter extends XMLElement { // Logging: protected static java.util.logging.Logger logger; private Object userObject = null; protected FreeMindMain frame; private NodeAdapter mapChild = null; private HashMap nodeAttributes = new HashMap(); // Font attributes private String fontName; private int fontStyle = 0; private int fontSize = 0; // Icon attributes private String iconName; // arrow link attributes: protected Vector mArrowLinkAdapters; protected HashMap /* id -> target */mIdToTarget; public static final String XML_NODE_TEXT = "TEXT"; public static final String XML_NODE = "node"; public static final String XML_NODE_ATTRIBUTE = "attribute"; public static final String XML_NODE_ATTRIBUTE_LAYOUT = "attribute_layout"; public static final String XML_NODE_ATTRIBUTE_REGISTRY = "attribute_registry"; public static final String XML_NODE_REGISTERED_ATTRIBUTE_NAME = "attribute_name"; public static final String XML_NODE_REGISTERED_ATTRIBUTE_VALUE = "attribute_value"; // public static final String XML_NODE_CLASS_PREFIX = XML_NODE+"_"; public static final String XML_NODE_CLASS = "AA_NODE_CLASS"; public static final String XML_NODE_ADDITIONAL_INFO = "ADDITIONAL_INFO"; public static final String XML_NODE_ENCRYPTED_CONTENT = "ENCRYPTED_CONTENT"; public static final String XML_NODE_HISTORY_CREATED_AT = "CREATED"; public static final String XML_NODE_HISTORY_LAST_MODIFIED_AT = "MODIFIED"; public static final String XML_NODE_XHTML_TYPE_TAG = "TYPE"; public static final String XML_NODE_XHTML_TYPE_NODE = "NODE"; public static final String XML_NODE_XHTML_TYPE_NOTE = "NOTE"; private String attributeName; private String attributeValue; private int attributeNameWidth = AttributeTableLayoutModel.DEFAULT_COLUMN_WIDTH; private int attributeValueWidth = AttributeTableLayoutModel.DEFAULT_COLUMN_WIDTH; protected final ModeController mModeController; // Overhead methods public XMLElementAdapter(ModeController modeController) { this(modeController, new Vector(), new HashMap()); } protected XMLElementAdapter(ModeController modeController, Vector arrowLinkAdapters, HashMap IDToTarget) { this.mModeController = modeController; this.frame = modeController.getFrame(); this.mArrowLinkAdapters = arrowLinkAdapters; this.mIdToTarget = IDToTarget; if (logger == null) { logger = frame.getLogger(this.getClass().getName()); } } /** abstract method to create elements of my type (factory). */ abstract protected XMLElement createAnotherElement(); abstract protected NodeAdapter createNodeAdapter(FreeMindMain frame, String nodeClass); abstract protected EdgeAdapter createEdgeAdapter(NodeAdapter node, FreeMindMain frame); abstract protected CloudAdapter createCloudAdapter(NodeAdapter node, FreeMindMain frame); abstract protected ArrowLinkAdapter createArrowLinkAdapter( NodeAdapter source, NodeAdapter target, FreeMindMain frame); abstract protected ArrowLinkTarget createArrowLinkTarget( NodeAdapter source, NodeAdapter target, FreeMindMain frame); abstract protected NodeAdapter createEncryptedNode(String additionalInfo); protected FreeMindMain getFrame() { return frame; } public Object getUserObject() { return userObject; } protected void setUserObject(Object obj) { userObject = obj; } public NodeAdapter getMapChild() { return mapChild; } // Real parsing methods public void setName(String name) { super.setName(name); // Create user object based on name if (name.equals(XML_NODE)) { userObject = createNodeAdapter(frame, null); nodeAttributes.clear(); } else if (name.equals("edge")) { userObject = createEdgeAdapter(null, frame); } else if (name.equals("cloud")) { userObject = createCloudAdapter(null, frame); } else if (name.equals("arrowlink")) { userObject = createArrowLinkAdapter(null, null, frame); } else if (name.equals("linktarget")) { userObject = createArrowLinkTarget(null, null, frame); } else if (name.equals("font")) { userObject = null; } else if (name.equals(XML_NODE_ATTRIBUTE)) { userObject = null; } else if (name.equals(XML_NODE_ATTRIBUTE_LAYOUT)) { userObject = null; } else if (name.equals("map")) { userObject = null; } else if (name.equals(XML_NODE_ATTRIBUTE_REGISTRY)) { userObject = null; } else if (name.equals(XML_NODE_REGISTERED_ATTRIBUTE_NAME)) { userObject = null; } else if (name.equals(XML_NODE_REGISTERED_ATTRIBUTE_VALUE)) { userObject = null; } else if (name.equals("icon")) { userObject = null; } else if (name.equals("hook")) { // we gather the xml element and send it to the hook after // completion. userObject = new XMLElement(); } else { userObject = new XMLElement(); // for childs of hooks } } public void addChild(XMLElement child) { if (getName().equals("map")) { mapChild = (NodeAdapter) child.getUserObject(); return; } if (userObject instanceof XMLElement) { // ((XMLElement) userObject).addChild(child); super.addChild(child); return; } if (userObject instanceof NodeAdapter) { NodeAdapter node = (NodeAdapter) userObject; if (child.getUserObject() instanceof NodeAdapter) { node.insert((NodeAdapter) child.getUserObject(), -1); } // to the end without preferable... (PN) // node.getRealChildCount()); } else if (child.getUserObject() instanceof EdgeAdapter) { EdgeAdapter edge = (EdgeAdapter) child.getUserObject(); edge.setTarget(node); node.setEdge(edge); } else if (child.getUserObject() instanceof CloudAdapter) { CloudAdapter cloud = (CloudAdapter) child.getUserObject(); cloud.setTarget(node); node.setCloud(cloud); } else if (child.getUserObject() instanceof ArrowLinkTarget) { // ArrowLinkTarget is derived from ArrowLinkAdapter, so it must // be checked first. ArrowLinkTarget arrowLinkTarget = (ArrowLinkTarget) child .getUserObject(); arrowLinkTarget.setTarget(node); mArrowLinkAdapters.add(arrowLinkTarget); } else if (child.getUserObject() instanceof ArrowLinkAdapter) { ArrowLinkAdapter arrowLink = (ArrowLinkAdapter) child .getUserObject(); arrowLink.setSource(node); // annotate this link: (later processed by caller.). // System.out.println("arrowLink="+arrowLink); mArrowLinkAdapters.add(arrowLink); } else if (child.getName().equals("font")) { node.setFont((Font) child.getUserObject()); } else if (child.getName().equals(XML_NODE_ATTRIBUTE)) { node.createAttributeTableModel(); node.getAttributes().addRowNoUndo( (Attribute) child.getUserObject()); } else if (child.getName().equals(XML_NODE_ATTRIBUTE_LAYOUT)) { node.createAttributeTableModel(); AttributeTableLayoutModel layout = node.getAttributes() .getLayout(); layout.setColumnWidth(0, ((XMLElementAdapter) child).attributeNameWidth); layout.setColumnWidth(1, ((XMLElementAdapter) child).attributeValueWidth); } else if (child.getName().equals("icon")) { node.addIcon((MindIcon) child.getUserObject(), MindIcon.LAST); } else if (child.getName().equals(XML_NODE_XHTML_CONTENT_TAG)) { String xmlText = ((XMLElement) child).getContent(); Object typeAttribute = child .getAttribute(XML_NODE_XHTML_TYPE_TAG); if (typeAttribute == null || XML_NODE_XHTML_TYPE_NODE.equals(typeAttribute)) { // output: logger.finest("Setting node html content to:" + xmlText); node.setXmlText(xmlText); } else { logger.finest("Setting note html content to:" + xmlText); node.setXmlNoteText(xmlText); } } else if (child.getName().equals("hook")) { XMLElement xml = (XMLElement) child/* .getUserObject() */; String loadName = (String) xml.getAttribute("NAME"); PermanentNodeHook hook = null; try { // loadName=loadName.replace('/', File.separatorChar); /* * The next code snippet is an exception. Normally, hooks * have to be created via the ModeController. DO NOT COPY. */ hook = (PermanentNodeHook) mModeController.getHookFactory() .createNodeHook(loadName); // this is a bad hack. Don't make use of this data unless // you know exactly what you are doing. hook.setNode(node); } catch (Exception e) { freemind.main.Resources.getInstance().logException(e); hook = new PermanentNodeHookSubstituteUnknown(loadName); } hook.loadFrom(xml); node.addHook(hook); } return; } if (child instanceof XMLElementAdapter && getName().equals(XML_NODE_REGISTERED_ATTRIBUTE_NAME) && child.getName().equals(XML_NODE_REGISTERED_ATTRIBUTE_VALUE)) { Attribute attribute = new Attribute(attributeName, ((XMLElementAdapter) child).attributeValue); AttributeRegistry r = getMap().getRegistry().getAttributes(); r.registry(attribute); } } public void setAttribute(String name, Object value) { // We take advantage of precondition that value != null. String sValue = value.toString(); if (ignoreCase) { name = name.toUpperCase(); } if (userObject instanceof XMLElement) { // ((XMLElement) userObject).setAttribute(name, value); super.setAttribute(name, value); // and to myself, as I am also an // xml element. return; } if (userObject instanceof NodeAdapter) { // NodeAdapter node = (NodeAdapter) userObject; userObject = setNodeAttribute(name, sValue, node); nodeAttributes.put(name, sValue); return; } if (userObject instanceof EdgeAdapter) { EdgeAdapter edge = (EdgeAdapter) userObject; if (name.equals("STYLE")) { edge.setStyle(sValue); } else if (name.equals("COLOR")) { edge.setColor(Tools.xmlToColor(sValue)); } else if (name.equals("WIDTH")) { if (sValue.equals(EdgeAdapter.EDGE_WIDTH_THIN_STRING)) { edge.setWidth(EdgeAdapter.WIDTH_THIN); } else { edge.setWidth(Integer.parseInt(sValue)); } } return; } if (userObject instanceof CloudAdapter) { CloudAdapter cloud = (CloudAdapter) userObject; if (name.equals("STYLE")) { cloud.setStyle(sValue); } else if (name.equals("COLOR")) { cloud.setColor(Tools.xmlToColor(sValue)); } else if (name.equals("WIDTH")) { cloud.setWidth(Integer.parseInt(sValue)); } return; } if (userObject instanceof ArrowLinkAdapter) { ArrowLinkAdapter arrowLink = (ArrowLinkAdapter) userObject; if (name.equals("STYLE")) { arrowLink.setStyle(sValue); } else if (name.equals("ID")) { arrowLink.setUniqueId(sValue); } else if (name.equals("COLOR")) { arrowLink.setColor(Tools.xmlToColor(sValue)); } else if (name.equals("DESTINATION")) { arrowLink.setDestinationLabel(sValue); } else if (name.equals("REFERENCETEXT")) { arrowLink.setReferenceText((sValue)); } else if (name.equals("STARTINCLINATION")) { arrowLink.setStartInclination(Tools.xmlToPoint(sValue)); } else if (name.equals("ENDINCLINATION")) { arrowLink.setEndInclination(Tools.xmlToPoint(sValue)); } else if (name.equals("STARTARROW")) { arrowLink.setStartArrow(sValue); } else if (name.equals("ENDARROW")) { arrowLink.setEndArrow(sValue); } else if (name.equals("WIDTH")) { arrowLink.setWidth(Integer.parseInt(sValue)); } if (userObject instanceof ArrowLinkTarget) { ArrowLinkTarget arrowLinkTarget = (ArrowLinkTarget) userObject; if (name.equals("SOURCE")) { arrowLinkTarget.setSourceLabel(sValue); } } return; } if (getName().equals("font")) { if (name.equals("SIZE")) { fontSize = Integer.parseInt(sValue); } else if (name.equals("NAME")) { fontName = sValue; } // Styling else if (sValue.equals("true")) { if (name.equals("BOLD")) { fontStyle += Font.BOLD; } else if (name.equals("ITALIC")) { fontStyle += Font.ITALIC; } } } /* icons */ if (getName().equals("icon")) { if (name.equals("BUILTIN")) { iconName = sValue; } } /* attributes */ else if (getName().equals(XML_NODE_ATTRIBUTE)) { if (name.equals("NAME")) { attributeName = sValue; } else if (name.equals("VALUE")) { attributeValue = sValue; } } else if (getName().equals(XML_NODE_ATTRIBUTE_LAYOUT)) { if (name.equals("NAME_WIDTH")) { attributeNameWidth = Integer.parseInt(sValue); } else if (name.equals("VALUE_WIDTH")) { attributeValueWidth = Integer.parseInt(sValue); } } else if (getName().equals(XML_NODE_ATTRIBUTE_REGISTRY)) { if (name.equals("RESTRICTED")) { getMap().getRegistry().getAttributes().setRestricted(true); } if (name.equals("SHOW_ATTRIBUTES")) { mModeController.getController().setAttributeViewType(getMap(), sValue); } if (name.equals("FONT_SIZE")) { try { int size = Integer.parseInt(sValue); getMap().getRegistry().getAttributes().setFontSize(size); } catch (NumberFormatException ex) { } } } else if (getName().equals(XML_NODE_REGISTERED_ATTRIBUTE_NAME)) { if (name.equals("NAME")) { attributeName = sValue; getMap().getRegistry().getAttributes().registry(attributeName); } else { super.setAttribute(name, sValue); } } else if (getName().equals(XML_NODE_REGISTERED_ATTRIBUTE_VALUE)) { if (name.equals("VALUE")) { attributeValue = sValue; } } } private NodeAdapter setNodeAttribute(String name, String sValue, NodeAdapter node) { if (name.equals(XML_NODE_TEXT)) { logger.finest("Setting node text content to:" + sValue); node.setUserObject(sValue); } else if (name.equals(XML_NODE_ENCRYPTED_CONTENT)) { // we change the node implementation to EncryptedMindMapNode. node = createEncryptedNode(sValue); } else if (name.equals(XML_NODE_HISTORY_CREATED_AT)) { if (node.getHistoryInformation() == null) { node.setHistoryInformation(new HistoryInformation()); } node.getHistoryInformation().setCreatedAt(Tools.xmlToDate(sValue)); } else if (name.equals(XML_NODE_HISTORY_LAST_MODIFIED_AT)) { if (node.getHistoryInformation() == null) { node.setHistoryInformation(new HistoryInformation()); } node.getHistoryInformation().setLastModifiedAt( Tools.xmlToDate(sValue)); } else if (name.equals("FOLDED")) { if (sValue.equals("true")) { node.setFolded(true); } } else if (name.equals("POSITION")) { // fc, 17.12.2003: Remove the left/right bug. node.setLeft(sValue.equals("left")); } else if (name.equals("COLOR")) { if (sValue.length() == 7) { node.setColor(Tools.xmlToColor(sValue)); } } else if (name.equals("BACKGROUND_COLOR")) { if (sValue.length() == 7) { node.setBackgroundColor(Tools.xmlToColor(sValue)); } } else if (name.equals("LINK")) { node.setLink(sValue); } else if (name.equals("STYLE")) { node.setStyle(sValue); } else if (name.equals("ID")) { // do not set label but annotate in list: // System.out.println("(sValue, node) = " + sValue + ", "+ node); mIdToTarget.put(sValue, node); } else if (name.equals("VSHIFT")) { node.setShiftY(Integer.parseInt(sValue)); } else if (name.equals("VGAP")) { node.setVGap(Integer.parseInt(sValue)); } else if (name.equals("HGAP")) { node.setHGap(Integer.parseInt(sValue)); } return node; } /** * Sets all attributes that were formely applied to the current userObject * to a given (new) node. Thus, the instance of a node can be changed after * the creation. (At the moment, relevant for encrypted nodes). */ protected void copyAttributesToNode(NodeAdapter node) { // reactivate all settings from nodeAttributes: for (Iterator i = nodeAttributes.keySet().iterator(); i.hasNext();) { String key = (String) i.next(); // to avoid self reference: setNodeAttribute(key, (String) nodeAttributes.get(key), node); } } protected void completeElement() { if (getName().equals(XML_NODE)) { // unify map child behaviour: if (mapChild == null) { mapChild = (NodeAdapter) userObject; } return; } if (getName().equals("font")) { userObject = mModeController.getController().getFontThroughMap( new Font(fontName, fontStyle, fontSize)); return; } /* icons */ if (getName().equals("icon")) { userObject = MindIcon.factory(iconName); return; } /* attributes */ if (getName().equals(XML_NODE_ATTRIBUTE)) { userObject = new Attribute(attributeName, attributeValue); return; } if (getName().equals(XML_NODE_REGISTERED_ATTRIBUTE_NAME)) { if (null != getAttribute("VISIBLE")) { getMap().getRegistry().getAttributes() .getElement(attributeName).setVisibility(true); } if (null != getAttribute("RESTRICTED")) { getMap().getRegistry().getAttributes() .getElement(attributeName).setRestriction(true); } return; } } /** * Completes the links within the getMap(). They are registered in the * registry. * * Case I: Source+Destination are pasted (Ia: cut, Ib: copy) Case II: Source * is pasted, Destination remains unchanged in the map (IIa: cut, IIb: copy) * Case III: Destination is pasted, Source remains unchanged in the map * (IIIa: cut, IIIb: copy) */ public void processUnfinishedLinks(MindMapLinkRegistry registry) { // add labels to the nodes: for (Iterator i1 = mIdToTarget.keySet().iterator(); i1.hasNext();) { String key = (String) i1.next(); NodeAdapter target1 = (NodeAdapter) mIdToTarget.get(key); /* * key is the proposed name for the target, is changed by the * registry, if already present. */ registry.registerLinkTarget(target1, key); } // complete arrow links with right labels: for (int i = 0; i < mArrowLinkAdapters.size(); ++i) { Object arrowObject = mArrowLinkAdapters.get(i); if (arrowObject instanceof ArrowLinkTarget) { ArrowLinkTarget linkTarget = (ArrowLinkTarget) arrowObject; // do the same as for ArrowLinkAdapter and start to search for the source. String oldId = linkTarget.getSourceLabel(); MindMapNode source = (MindMapNode) registry.getTargetForId(oldId); // find oldId in target list: if (mIdToTarget.containsKey(oldId)) { // link source present in the paste as well and has probably // been renamed (case I), do nothing, as the source does // all. continue; } else if (source == null) { // link source is in nowhere-land logger.severe("Cannot find the label " + oldId + " in the map. The link target " + linkTarget + " is not restored."); continue; } // link source remains in the map (case III) // set the source: linkTarget.setSource(source); // here, it is getting more complex: case IIIa: the arrowLink // has to be recreated. // case IIIb: the arrowLink has to be doubled! First, // distinguish between a and b. MindMapNode target = linkTarget.getTarget(); String targetCurrentId = registry.getLabel(target); if (!mIdToTarget.containsKey(targetCurrentId)) { // the id of target has changed, we have case IIIb. MindMapLink link = registry.getLinkForId(linkTarget .getUniqueId()); if (link == null) { logger.severe("Cannot find the label " + linkTarget.getUniqueId() + " for the link in the map. The link target " + linkTarget + " is not restored."); continue; } else { // double the link. MindMapLink clone = (MindMapLink) link.clone(); clone.setTarget(target); ((ArrowLinkAdapter) clone) .setDestinationLabel(targetCurrentId); // add the new arrowLink and give it a new id registry.registerLink(clone); linkTarget.setUniqueId(clone.getUniqueId()); } } else { // case IIIa: The link must only be re-added: // change from linkTarget to ArrowLinkAdapter and add it: ArrowLinkAdapter linkAdapter = linkTarget.createArrowLinkAdapter(registry); registry.registerLink(linkAdapter); } } else if (arrowObject instanceof ArrowLinkAdapter) { ArrowLinkAdapter arrowLink = (ArrowLinkAdapter) arrowObject; // here, the source is in the paste, and the destination is now // searched: String oldId = arrowLink.getDestinationLabel(); NodeAdapter target = null; String newId = null; // find oldId in target list: if (mIdToTarget.containsKey(oldId)) { // link target present in the paste as well and has probably // been renamed (case I) target = (NodeAdapter) mIdToTarget.get(oldId); newId = registry.getLabel(target); } else if (registry.getTargetForId(oldId) != null) { // link target remains in the map (case II) target = (NodeAdapter) registry.getTargetForId(oldId); newId = oldId; } else { // link target is in nowhere-land logger.severe("Cannot find the label " + oldId + " in the map. The link " + arrowLink + " is not restored."); continue; } // set the new ID: arrowLink.setDestinationLabel(newId); // set the target: arrowLink.setTarget(target); // add the arrowLink: registry.registerLink(arrowLink); } } } protected MindMap getMap() { return mModeController.getMap(); } public HashMap getIDToTarget() { return mIdToTarget; } public void setIDToTarget(HashMap pToTarget) { mIdToTarget = pToTarget; } }