/*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. */ /*$Id: MindMapLinkRegistry.java,v 1.6.18.1.16.4 2008/12/09 21:09:43 christianfoltin Exp $*/ package freemind.modes; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.ListIterator; import java.util.Vector; import freemind.main.Tools; /** * Interface for the registry, which manages the ids of nodes and the existing * links in a map. Thus, this interface is bound to a map model, because other * maps have a different registry. */ public class MindMapLinkRegistry { /** * All elements put into this sort of vectors are put into the * SourceToLinks, too. This structure is kept synchronous to the IDToLinks * structure, but reversed. * * @author foltin * @date 23.01.2012 */ private class SynchronousVector extends Vector { /* * (non-Javadoc) * * @see java.util.Vector#add(java.lang.Object) */ public synchronized boolean add(Object pE) { boolean add = super.add(pE); if (pE instanceof MindMapLink) { MindMapLink link = (MindMapLink) pE; MindMapNode source = link.getSource(); if (!mSourceToLinks.containsKey(source)) { mSourceToLinks.put(source, new Vector()); } ((Vector) mSourceToLinks.get(source)).add(pE); } return add; } /* * (non-Javadoc) * * @see java.util.Vector#removeElementAt(int) */ public synchronized void removeElementAt(int pIndex) { MindMapLink link = (MindMapLink) get(pIndex); MindMapNode source = link.getSource(); Vector vector = (Vector) mSourceToLinks.get(source); if (vector != null) { vector.remove(link); if (vector.isEmpty()) { mSourceToLinks.remove(source); } } super.removeElementAt(pIndex); } } /** source -> vector of links with same source */ protected HashMap mSourceToLinks = new HashMap(); // ////////////////////////////////////////////////////////////////////////////////////// // // Attributes ///// // ////////////////////////////////////////////////////////////////////////////////////// /** MindMapNode = Target -> ID. */ protected HashMap mTargetToId; /** MindMapNode-> ID. */ protected HashMap mIdToTarget; /** id -> vector of links whose TargetToID.get(target) == id. */ protected HashMap mIdToLinks; /** id -> link */ protected HashMap mIdToLink; /** id */ protected HashSet mLocallyLinkedIds; protected static java.util.logging.Logger logger = null; // ////////////////////////////////////////////////////////////////////////////////////// // // Methods ///// // ////////////////////////////////////////////////////////////////////////////////////// public MindMapLinkRegistry(/* MindMap map */) { if (logger == null) { logger = freemind.main.Resources.getInstance().getLogger( this.getClass().getName()); } mTargetToId = new HashMap(); mIdToTarget = new HashMap(); mIdToLinks = new HashMap(); mIdToLink = new HashMap(); mLocallyLinkedIds = new HashSet(); } /** * This can be used, if the id has to be known, before a node can be * labeled. */ public String generateUniqueID(String proposedID) { return Tools.generateID(proposedID, mIdToLinks, "ID_"); } /** * This can be used, if the id has to be known, before a link can be labled. */ public String generateUniqueLinkId(String proposedID) { return Tools.generateID(proposedID, mIdToLink, "Arrow_ID_"); } public String registerLinkTarget(MindMapNode pTarget) { return _registerLinkTarget(pTarget); } /** * The second variant of the main method. The difference is that here an ID * is proposed, but has not to be taken, though. */ public String registerLinkTarget(MindMapNode pTarget, String pProposedID) { return _registerLinkTarget(pTarget, pProposedID); } /** * The main method. Registeres a node with a new (or an existing) node-id. */ public String _registerLinkTarget(MindMapNode target) { return _registerLinkTarget(target, null); } public String _registerLinkTarget(MindMapNode target, String proposedID) { // id already exists? if (mTargetToId.containsKey(target)) { String id = (String) mTargetToId.get(target); if (id != null) return id; // blank state. // is equal to no state. } // generate new id: String newId = generateUniqueID(proposedID); mTargetToId.put(target, newId); mIdToTarget.put(newId, target); // logger.fine("Register target node:"+target+", with ID="+newID); /* * This is to allocate the link target in the IDToLinks map!. */ getAssignedLinksVector(newId); return newId; } /** * @param node * @return null, if not registered. */ public String getState(MindMapNode node) { if (mTargetToId.containsKey(node)) return (String) mTargetToId.get(node); return null; } /** * Reverses the getLabel method: searches for a node with the id given as * the argument. */ public MindMapNode getTargetForId(String ID) { final Object target = mIdToTarget.get(ID); return (MindMapNode) target; } /** @return a Vector of {@link MindMapLink}s */ private Vector getAssignedLinksVector(String newId) { String id = newId; // look, if target is already present: Vector vec; if (mIdToLinks.containsKey(id)) { vec = (Vector) mIdToLinks.get(id); } else { vec = new SynchronousVector(); mIdToLinks.put(id, vec); } // Dimitry : logger is a performance killer here // //logger.fine("getAssignedLinksVector "+vec); return vec; } /** If there are still targets registered, they are removed, too. */ public void deregisterLinkTarget(MindMapNode target) throws java.lang.IllegalArgumentException { // deregister all links : Vector links = getAllLinks(target); for (int i = links.size() - 1; i >= 0; --i) { MindMapLink link = (MindMapLink) links.get(i); deregisterLink(link); } // and process my sons: for (ListIterator e = target.childrenUnfolded(); e.hasNext();) { MindMapNode child = (MindMapNode) e.next(); deregisterLinkTarget(child); } String id = getState(target); if (id != null) { // logger.fine("Deregister target node:"+target); mTargetToId.remove(target); mIdToTarget.remove(id); mIdToLinks.remove(id); } } /** * Method to keep track of the sources associated to a target node. This * method also sets the new id to the target. Moreover, it is not required * that the target node is already registered. This will be done on the fly. */ public void registerLink(MindMapLink link) throws java.lang.IllegalArgumentException { if ((link.getSource() == null) || (link.getTarget() == null) || (link.getDestinationLabel() == null)) throw new java.lang.IllegalArgumentException( "Illegal link specification." + link); MindMapNode source = link.getSource(); MindMapNode target = link.getTarget(); logger.fine("Register link (" + link + ") from source node:" + source + " to target " + target); String id = _registerLinkTarget(target); Vector vec = getAssignedLinksVector(id); // already present? for (int i = 0; i < vec.size(); ++i) { if (vec.get(i) == link) return; } vec.add(link); String uniqueId = link.getUniqueId(); if (uniqueId == null) { ((LinkAdapter) link).setUniqueId(generateUniqueLinkId(uniqueId)); uniqueId = link.getUniqueId(); } if (mIdToLink.containsKey(uniqueId)) { if (mIdToLink.get(uniqueId) != link) { logger.warning("link with duplicated unique id found:" + link); // new id: ((LinkAdapter) link) .setUniqueId(generateUniqueLinkId(uniqueId)); } } mIdToLink.put(link.getUniqueId(), link); } public void deregisterLink(MindMapLink link) { MindMapNode target = link.getTarget(); String id = _registerLinkTarget(target); Vector vec = getAssignedLinksVector(id); for (int i = vec.size() - 1; i >= 0; --i) { // logger.fine("Test for equal node:"+source+" to vector(i) " + // vec.get(i)); if (vec.get(i) == link) { vec.removeElementAt(i); logger.info("Deregister link (" + link + ") from source node:" + link.getSource() + " to target " + target); } } mIdToLink.remove(link.getUniqueId()); } /** * Reverses the getUniqueID method: searches for a link with the id given as * the argument. */ public MindMapLink getLinkForId(String pId) { if (mIdToLink.containsKey(pId)) { return (MindMapLink) mIdToLink.get(pId); } return null; } /** * @return Returns a Vector of {@link MindMapNode}s that point to the given * target node. */ public Vector /* of MindMapNode s */getAllSources(MindMapNode target) { Vector returnValue; returnValue = new Vector(); String id = getState(target); if (id != null) { Vector vec = getAssignedLinksVector(id); for (int i = 0; i < vec.size(); ++i) { returnValue.add(((MindMapLink) vec.get(i)).getSource()); } } return returnValue; } /** @return returns all links from or to this node. */ public Vector /* of MindMapLink s */getAllLinks(MindMapNode node) { Vector returnValue = new Vector(); returnValue.addAll(getAllLinksIntoMe(node)); returnValue.addAll(getAllLinksFromMe(node)); // Dimitry : logger is a performance killer here // //logger.fine("All links ("+returnValue+") from node:"+node); return returnValue; } /** @return returns all links to this node as {@link MindMapLink} vector. */ public Vector getAllLinksIntoMe(MindMapNode target) { Vector returnValue = new Vector(); String id = getState(target); if (id != null) { Vector vec = getAssignedLinksVector(id); /* "clone" */ returnValue.addAll(vec); } return returnValue; } /** @return returns all links from this node as {@link MindMapLink} vector. */ public Vector getAllLinksFromMe(MindMapNode source) { Vector returnValue = new Vector(); Collection vec = (Collection) mSourceToLinks.get(source); if (vec != null) { returnValue.addAll(vec); } return returnValue; } public String getLabel(MindMapNode target) { return getState(target); } public void registerLocalHyperlinkId(String pTargetId) { mLocallyLinkedIds.add(pTargetId); } public boolean isTargetOfLocalHyperlinks(String pTargetId) { return mLocallyLinkedIds.contains(pTargetId); } }