package uk.org.taverna.scufl2.api.common;

import java.util.HashMap;

public abstract class AbstractCloneable implements WorkflowBean {
	
	public static class CopyVisitor implements Visitor {
		private Cloning cloning;

		public CopyVisitor(Cloning cloning) {
			this.cloning = cloning;
		}

		@Override
		public boolean visit(WorkflowBean node) {
			cloneNode(node);
			return true;
		}

		@Override
		public boolean visitEnter(WorkflowBean node) {
			cloneNode(node);
			return true;
		}

		@Override
		public boolean visitLeave(WorkflowBean node) {
			return true;
		}

		@SuppressWarnings({ "rawtypes", "unchecked" })
		public void cloneNode(WorkflowBean node) {
			WorkflowBean clone = cloning.cloneIfNotInCache((WorkflowBean)node);
			if (node instanceof Child && clone instanceof Child) {
				Child child = (Child) node;
				Child childClone = (Child)clone;
				WorkflowBean oldParent = child.getParent();
				// NOTE: oldParent==null is OK, as cloned is HashMap

				// NOTE: We don't clone the parent! If it's not already cloned,
				// then it might be above our current visit tree and should not be
				// cloned (this is the case for the top level node). The clone will then have the parent as null.
				WorkflowBean newParent = cloning.getCloned(oldParent);
				childClone.setParent(newParent);
			}
		}
	}
	
	@Override
	public AbstractCloneable cloned() {
		Cloning cloning = new Cloning(this);
		CopyVisitor copyVisitor = new CopyVisitor(cloning);
		accept(copyVisitor);
		return cloning.getCloned(this);
	}
	
	protected class Cloning {
		private HashMap<WorkflowBean, WorkflowBean> cloned = new HashMap<WorkflowBean, WorkflowBean>();

		
		/**
		 * Construct a Cloning helper.
		 * <p>
		 * 
		 * @param ancestor
		 *            The highest WorkflowBean in the hierarchy to clone. Any
		 *            beans that are 'below' this ancestor will be cloned.
		 */
		public Cloning(WorkflowBean ancestor) {
			this.ancestor = ancestor;
		}
		
		final WorkflowBean ancestor;
	
		@SuppressWarnings("unchecked")
		public <T extends WorkflowBean> T cloneOrOriginal(T original) {
			if (cloned.containsKey(original)) {
				return (T) cloned.get(original);
			}
			return original;
		}

		@SuppressWarnings("unchecked")
		public <T extends WorkflowBean> T cloneIfNotInCache(T original) {
			if (cloned.containsKey(original)) {
				return (T) cloned.get(original);
			}		
			T clone;
			try {
				clone = (T) original.getClass().newInstance();
			} catch (InstantiationException e) {
				System.err.println("Can't do this one.. " + original);
				return null;
			} catch (IllegalAccessException e) {
				throw new RuntimeException(e);
			}
			cloned.put(original, clone);
			
			if (original instanceof AbstractCloneable) {
				AbstractCloneable cloneable = (AbstractCloneable) original;
				cloneable.cloneInto(clone, this);
			}
			
			System.out.println("Cloned " + clone);
			return clone;	
		}
		

		@SuppressWarnings("unchecked")
		public <T extends WorkflowBean> T getCloned(T originalBean) {
			return (T) cloned.get(originalBean);
		}
		
		public <T extends WorkflowBean> void knownClone(T original, T clone) {
			cloned.put(original, clone);
		}
		
		
	}

	protected abstract void cloneInto(WorkflowBean clone,
			Cloning cloning);
	
	

}

@@ -139,21 +139,24 @@
 %7B%0D%0A%09%0D%0A%09p
-ublic
+rotected
  static 
@@ -1416,16 +1416,129 @@
 ed() %7B%0D%0A
+%09%09return cloneWorkflowBean(this);%09%09%0D%0A%09%7D%0D%0A%0D%0A%09public static %3CT extends WorkflowBean%3E T cloneWorkflowBean(T obj) %7B%0D%0A
 %09%09Clonin
@@ -1561,20 +1561,19 @@
 Cloning(
-this
+obj
 );%0D%0A%09%09Co
@@ -1625,16 +1625,20 @@
 ng);%0D%0A%09%09
+obj.
 accept(c
@@ -1678,20 +1678,19 @@
 tCloned(
-this
+obj
 );%0D%0A%09%7D%0D%0A
@@ -1694,24 +1694,28 @@
 %7D%0D%0A%09%0D%0A%09p
-rotected
+ublic static
  class C

Add cloneWorkflowBean()