gt: 	protected Registration getRegistration() {
context: /*FreeMind - A Program for creating and viewing Mindmaps
   *Copyright (C) 2000-2011 Joerg Mueller, Daniel Polansky, Christian Foltin, Dimitri Polivaev and others.
   *
   *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 accessories.plugins;
  
  import java.awt.EventQueue;
  import java.util.Collection;
  import java.util.HashMap;
  import java.util.HashSet;
  import java.util.Iterator;
  import java.util.Properties;
  import java.util.StringTokenizer;
  import java.util.Vector;
  
  import accessories.plugins.ClonePasteAction.CloneProperties;
  import accessories.plugins.ClonePasteAction.ClonePropertiesObserver;
  import accessories.plugins.ClonePasteAction.Registration;
  import freemind.extensions.PermanentNodeHook;
  import freemind.main.Tools;
  import freemind.main.XMLElement;
  import freemind.modes.MindMapNode;
  import freemind.modes.ModeController.NodeLifetimeListener;
  import freemind.modes.mindmapmode.hooks.PermanentMindMapNodeHookAdapter;
  
  public class ClonePlugin extends PermanentMindMapNodeHookAdapter implements
  		NodeLifetimeListener, ClonePropertiesObserver {
  
  	public static final String CLONE_ITSELF_FALSE = "false";
  	public static final String CLONE_ITSELF_TRUE = "true";
  	public static final String PLUGIN_LABEL = "accessories/plugins/ClonePlugin.properties";
  	public static final String XML_STORAGE_CLONES = "CLONE_IDS";
  	public static final String XML_STORAGE_CLONE_ID = "CLONE_ID";
  	public static final String XML_STORAGE_CLONE_ITSELF = "CLONE_ITSELF";
  
  	/**
  	 * This is the master list. {@link ClonePlugin#mCloneNodes mCloneNodes} is
  	 * derived from it. It contains id strings.
  	 */
  	private HashSet mCloneNodeIds;
  	/**
  	 * Includes the original node. This is a cached list with the MindMapNodes
  	 * belonging to the {@link ClonePlugin#mCloneNodeIds mCloneNodeIds}.
  	 */
  	private HashSet mCloneNodes;
  
  	private String mCloneId;
  	private boolean mDisabled = false;
  	private Boolean mCloneItself = null;
  	private CloneProperties mClonePropertiesHolder;
  
  	public ClonePlugin() {
  	}
  
  	public void invoke(MindMapNode node) {
  		super.invoke(node);
  		registerPlugin();
  	}
  
  	public void addClone(MindMapNode cloneNode) {
  		mCloneNodeIds.add(getMindMapController().getNodeID(cloneNode));
  		clearCloneCache();
  	}
  
  	public void clearCloneCache() {
  		mCloneNodes = new HashSet();
  	}
  
  	private void disablePlugin() {
  		mDisabled = true;
  		getMindMapController().getController().errorMessage(
  				getMindMapController().getText("clone_plugin_impossible"));
  		EventQueue.invokeLater(new Runnable() {
  			public void run() {
  				if (getHook(getNode()) != null) {
  					removeHook();
  				}
  			}
  		});
  	}
  
  	/**
  	 * double add = remove.
  	 * 
  	 */
  	protected void removeHook() {
  		// first deactivate cloning for this node (otherwise, the deactivation will be cloned, too!)
  		deregisterCloning();
  		Vector selecteds = Tools.getVectorWithSingleElement(getNode());
  		getMindMapController()
  				.addHook(getNode(), selecteds, PLUGIN_LABEL, null);
  	}
  
  	public void save(XMLElement xml) {
  		super.save(xml);
  		HashMap values = new HashMap();
  		values.put(XML_STORAGE_CLONES, getCloneIdsAsString());
  		values.put(XML_STORAGE_CLONE_ID, mCloneId);
  		String cloneItselfValue = getCloneItselfValue();
  		values.put(XML_STORAGE_CLONE_ITSELF, cloneItselfValue);
  		logger.finest("Saved mCloneItself to " + cloneItselfValue);
  		saveNameValuePairs(values, xml);
  		logger.fine("Saved clone plugin");
  	}
  
  	protected String getCloneItselfValue() {
  		return mClonePropertiesHolder
  				.isCloneItself() ? CLONE_ITSELF_TRUE
  				: CLONE_ITSELF_FALSE;
  	}
  
  	public String getCloneIdsAsString() {
  		StringBuffer cloneIds = new StringBuffer();
  		for (Iterator it = mCloneNodeIds.iterator(); it.hasNext();) {
  			String cloneId = (String) it.next();
  			cloneIds.append(cloneId);
  			cloneIds.append(",");
  		}
  		return cloneIds.toString();
  	}
  
  	public void loadFrom(XMLElement child) {
  		super.loadFrom(child);
  		mCloneNodes = null;
  		mCloneNodeIds = new HashSet();
  		HashMap values = loadNameValuePairs(child);
  		String cloneIds = (String) values.get(XML_STORAGE_CLONES);
  		if (cloneIds != null) {
  			StringTokenizer st = new StringTokenizer(cloneIds, ",");
  			while (st.hasMoreTokens()) {
  				String cloneId = st.nextToken();
  				mCloneNodeIds.add(cloneId);
  			}
  		}
  		mCloneId = (String) values.get(XML_STORAGE_CLONE_ID);
  		if (values.containsKey(XML_STORAGE_CLONE_ITSELF)) {
  			mCloneItself = Boolean.valueOf(Tools.safeEquals(CLONE_ITSELF_TRUE,
  					values.get(XML_STORAGE_CLONE_ITSELF)));
  		} else {
  			mCloneItself = Boolean.FALSE;
  		}
  		logger.finest("Loaded mCloneItself to " + mCloneItself);
  	}
  
  	public void shutdownMapHook() {
  		logger.fine("Shutdown of clones");
  		deregisterPlugin();
  		super.shutdownMapHook();
  	}
  
  	private void registerPlugin() {
  		if (mDisabled)
  			return;
  		/*
  		 * test for error cases: - orig is child of clone now - if clone is a
  		 * child of clone, this is here not reachable, as the plugin remains
  		 * active and is not newly invoked. Hmm, what to do?
  		 */
  		MindMapNode originalNode = getNode();
  		HashSet cloneNodes = getCloneNodes();
  		logger.fine("Invoke shadow class with orig: "
  				+ printNodeId(originalNode) + " and clones "
  				+ printNodeIds(cloneNodes));
  		// check for error case that clones are descendant of one another.
  		for (Iterator it = cloneNodes.iterator(); it.hasNext();) {
  			MindMapNode cloneNode = (MindMapNode) it.next();
  			if (originalNode != null && originalNode.isDescendantOf(cloneNode)) {
  				disablePlugin();
  				return;
  			}
  		}
  		getMindMapController().registerNodeLifetimeListener(this, false);
  		Registration registration = getRegistration();
  		if (mCloneId == null) {
  			// hmm, it seems, that I am the first. Let's generate an id:
  			mCloneId = registration.generateNewCloneId(null);
  		}
  		registration.registerClone(mCloneId, this);
  		// the clone list contains itself, too.
  		addClone(getNode());
  		mClonePropertiesHolder = registration
  				.getCloneProperties(mCloneId);
  		if (mCloneItself != null) {
  			mClonePropertiesHolder.setCloneItself(mCloneItself.booleanValue());
  		}
  		mClonePropertiesHolder.registerObserver(this);
  	}
  
  