/*
 * MiningMart Version 1.0
 * 
 * Copyright (C) 2006 Martin Scholz, Timm Euler, 
 *                    Daniel Hakenjos, Katharina Morik
 *
 * Contact: miningmart@ls8.cs.uni-dortmund.de
 *
 * A list of contributing developers (other than the copyright 
 * holders) can be found at
 * http://mmart.cs.uni-dortmund.de/downloads/download.html
 * 
 * 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, see the file MM_HOME/LICENSE; if not, write
 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
 * Floor, Boston, MA 02110-1301, USA.
 */
package edu.udo.cs.miningmart.m4.core;

import java.util.Collection;
import java.util.Iterator;
import java.util.Vector;

import edu.udo.cs.miningmart.db.DB;
import edu.udo.cs.miningmart.exception.M4Exception;
import edu.udo.cs.miningmart.m4.utils.HasCrossReferences;
import edu.udo.cs.miningmart.m4.utils.InterM4ChainStep;
import edu.udo.cs.miningmart.m4.utils.InterM4ChainToParentChain;
import edu.udo.cs.miningmart.m4.utils.InterM4Communicator;
import edu.udo.cs.miningmart.m4.utils.M4Info;
import edu.udo.cs.miningmart.m4.utils.M4InfoEntry;
import edu.udo.cs.miningmart.m4.utils.Print;
import edu.udo.cs.miningmart.m4.utils.XmlInfo;

/**
 * @author Martin Scholz
 * @version $Id: Chain.java,v 1.7 2006/04/11 14:10:14 euler Exp $
 */
public class Chain extends GraphicalM4Object implements XmlInfo, edu.udo.cs.miningmart.m4.Chain, HasCrossReferences {
	
	public static final String M4_TABLE              = "chain_t";
	public static final String ATTRIB_CHAIN_ID       = "ch_id";
	public static final String ATTRIB_CASE_ID        = "ch_caseid";
	public static final String ATTRIB_PARENTCHAIN_ID = "ch_parent";
	public static final String ATTRIB_CHAIN_NAME     = "ch_name";
	public static final String ATTRIB_CHAIN_DESCR    = "ch_descript";	
	
	static final InterM4Communicator chain2step = new InterM4ChainStep();
    static final InterM4Communicator chain2parentChain = new InterM4ChainToParentChain();
   
	/** Cache for getM4Info() */
	public static M4Info m4Info = null;

	/** @see M4Table.getM4TableName() */
	public String getM4TableName() {
		return M4_TABLE;	
	}

	/** @see M4Table.getIdAttributeName() */
	public String getIdAttributeName() {
		return ATTRIB_CHAIN_ID;
	}

	/** @see M4Table.getM4Info() */
	public M4Info getM4Info() {
		if (m4Info == null) {
			M4InfoEntry[] m4i = {
				new M4InfoEntry(ATTRIB_CHAIN_ID,       "getId",          "setId",                   long.class,   NOT_NULL),				
				new M4InfoEntry(ATTRIB_CHAIN_NAME,     "getName",        "setName",                 String.class, NOT_NULL),				
				new M4InfoEntry(ATTRIB_PARENTCHAIN_ID, "getParentChain", "primitiveSetParentChain", edu.udo.cs.miningmart.m4.Chain.class),
				new M4InfoEntry(ATTRIB_CASE_ID,        "getTheCase",     "primitiveSetCase",        edu.udo.cs.miningmart.m4.Case.class,   NOT_NULL),
				new M4InfoEntry(ATTRIB_CHAIN_DESCR,    "getDescription", "setDescription",          String.class)
			};
			m4Info = new M4Info(m4i);
		}
		return m4Info;
	}

	/** Cache for getXmlInfo() */
	private static M4Info xmlInfo = null;

	/** @see XmlInfo.getXmlInfo() */
	public M4Info getXmlInfo() {
		if (xmlInfo == null) {
			M4InfoEntry[] m4i = {				
				new M4InfoEntry("Name",        "getName",          "setName",            String.class),
				new M4InfoEntry("Case",        "getTheCase",       "setTheCase",         edu.udo.cs.miningmart.m4.Case.class),
				new M4InfoEntry("ParentChain", "getParentChain",   "setParentChain",     edu.udo.cs.miningmart.m4.Chain.class),
				new M4InfoEntry("Description", "getDescription",   "setDescription",     String.class),
				new M4InfoEntry("Docu",        "getDocumentation", "setDocumentation",   String.class)
			};
			xmlInfo = new M4Info(m4i);
		}
		return xmlInfo;
	}

	private String myDescription;
	private Case   myCase;
	private Chain  myParentChain = null; // null means no parent chain exists.	
	private final Vector mySteps = new Vector();
	private final Vector mySubchains = new Vector();
	private boolean allSubchainsLoaded = false;
	private boolean allStepsLoaded = false;

	/**
	 * @param db the instance of <code>DB</code> used for the embedding <code>Case</code>
	 */
	public Chain(DB db) {
		super(db);	
	}
		
	/**
	 * @see M4Object#print()
	 */
	public void print() {
		this.doPrint(Print.M4_OBJECT, "Chain (Id = " + this.getId() + ";"
				+ "      Name = " + this.getName() + ";" + "      Steps: ");
		try
		{
			final Iterator it = this.getTopLevelSteps().iterator();
			while (it.hasNext())
			{
				Long l = (Long) it.next();
				if (l != null)
					this.doPrint(Print.M4_OBJECT, "Step " + l.longValue());
			}
		}
		catch (M4Exception e)
		{
			this.doPrint(
				Print.M4_OBJECT,
				"Warning: Error loading steps in Chain.print(): "
					+ e.getMessage());
		}
		this.doPrint(Print.M4_OBJECT, ")");
		super.print(); // prints graphical info
	}
	
	/**
	 * Implements the interface HasCrossReferences
	 */
	public Collection getCrossReferences() {
		// collect ALL parent chains:
		Chain parent = (Chain) this.getParentChain();
		Vector allParents = new Vector();
		while (parent != null) {
			allParents.add(parent);
			parent = (Chain) parent.getParentChain();
		}
		return allParents;
	}
	
	/**
	 * @see edu.udo.cs.miningmart.m4.core.M4Data#getObjectsInNamespace(Class)
	 */
	protected Collection getObjectsInNamespace(Class typeOfObjects) throws M4Exception {
		if (typeOfObjects.isAssignableFrom(Chain.class)) {
			return this.getDirectSubChains();
		}
		else if (typeOfObjects.isAssignableFrom(Step.class)) {
			return this.getTopLevelSteps();
		}
		else throw new M4Exception("Chain.getObjectsInNamespace: unknown type of objects given: " + typeOfObjects.getName());
	}

	/**
	 * Active getter of the steps embedded in this chain. Only the
	 * steps belonging directly to this chain are returned. Steps
	 * of subchains are not included in the returned collection.
	 * 
	 * @return a <code>Collection</code> of <code>Step</code>s
	 */
	public Collection getTopLevelSteps() throws M4Exception {
		if (!this.allStepsLoaded && ( ! this.isNew())) {			
			this.allStepsLoaded = true;
			this.readStepsForChainFromDb();
		}
		return this.mySteps;
	}

	/**
	 * Active getter of the steps embedded in this chain. Only the
	 * steps belonging directly to this chain are returned. Steps
	 * of subchains are not included in the returned iterator. The 
	 * iterator iterates through the Steps in the right sequential 
	 * order. If there are no top level steps, NULL is returned.
	 * 
	 * @return an <code>Iterator</code> through <code>Step</code>s, or NULL.
	 */
	public Iterator getTopLevelStepsSequentialised() throws M4Exception {
		Collection c = this.getTopLevelSteps();
		if ( ! c.isEmpty()) {
			// using the Case sequentialiser for this chain:
			Iterator allStepsIterator = this.getTheCase().getStepIterator();
			Vector sortedStepsOfThisChain = new Vector();
			while (allStepsIterator.hasNext()) {
				Step globalStep = (Step) allStepsIterator.next();
				Iterator thisChainsStepsIterator = c.iterator();
				while (thisChainsStepsIterator.hasNext()) {
					Step nextStep = (Step) thisChainsStepsIterator.next();
					if (globalStep.equals(nextStep)) {
						sortedStepsOfThisChain.add(nextStep);
					}
				}
			}
			return sortedStepsOfThisChain.iterator();
		}
		else {
			return null;
		}
	}

	/**
	 * Creates a new Chain whose parent chain is this Chain. The name of the
	 * Chain should be unique within the Case. All steps in the given
	 * collection are added as steps of the newly created chain. All
	 * chains in the given collection are added as direct subchains of
	 * the newly created chain.
	 *
	 * @see edu.udo.cs.miningmart.m4.Case#createChain(String, Collection)
	 */
	public edu.udo.cs.miningmart.m4.Chain createSubChain(String name, Collection stepsAndChains) throws M4Exception {
		
		if (stepsAndChains == null) {
			this.doPrint(Print.M4_OBJECT, "Warning: Chain.createSubChain/2: got <null> instead of a collection of steps and chains!");  
		}
		Chain newChain = (Chain) this.getTheCase().createChain(name);
		Iterator it = stepsAndChains.iterator();
		while (it.hasNext()) {
			M4Object anM4Object = (M4Object) it.next();
			if (anM4Object instanceof Step) {
				newChain.addStep((Step) anM4Object);
			}
			else if (anM4Object instanceof Chain) {
				newChain.addSubChain((Chain) anM4Object);
			}
			else { 
				throw new M4Exception("Case.createChain/2: found something in the given collection that is neither Step nor Chain!");
			}
		}
		newChain.setParentChain(this);
		return newChain;
	}
	
	/**
	 * Gets the Case.
	 * @return Returns a Case
	 */
	public edu.udo.cs.miningmart.m4.Case getTheCase() {
		return this.myCase;
	}

	/**
	 * When a chain is deleted, it moves its steps to its
	 * parent chain.
	 */
	public void deleteSoon() throws M4Exception {
		
		// iterate through the top level steps:
		Collection steps = this.getTopLevelSteps();
		if (steps == null) {
			throw new M4Exception("Chain.deleteSoon: got NULL when looking for my top level steps (in Chain '" + this.getName() + "')!");
		}
		Iterator stIt = steps.iterator();
		Chain parent = (Chain) this.getParentChain(); // may be null
		if (parent != null) {
			// if the parent is null, nothing needs to be done,
			// but if it exists, the steps of the deleted chain move to the parent chain

			// we have to avoid a ConcurrentModificationException here:
			Vector theSteps = new Vector();
			while (stIt.hasNext()) {
				Step aStep = (Step) stIt.next();
				theSteps.add(aStep);
			}
			
			stIt = theSteps.iterator();
			while (stIt.hasNext()) {
				Step aStep = (Step) stIt.next();
				parent.addStep(aStep);
			}			
		}
		// iterate through the top level chains:
		Collection subchains = this.getDirectSubChains();
		if (subchains == null) {
			throw new M4Exception("Chain.deleteSoon: got NULL when looking for my direct subchains (in Chain '" + this.getName() + "')!");
		}
		// again a ConcurrentModificationException must be circumvented:		
		Iterator chIt = (new Vector(subchains)).iterator();
		while (chIt.hasNext()) {
			Chain subChain = (Chain) chIt.next();
			subChain.setParentChain(parent);			
		}
		
		super.deleteSoon();
	}
	
	/**
	 * Return all steps of this chain and all direct or
	 * indirect subchains.
	 * 
	 * @return a <code>Collection</code> of <code>Step</code>s 
	 */
	public Collection getAllSteps() throws M4Exception {
		Vector allTheSteps = new Vector();
		this.getAllSteps(allTheSteps);
		return allTheSteps;
	}
	
	// go recursively through the subchains to collect all steps
	private void getAllSteps(Collection allTheSteps) throws M4Exception {
		// first, get the steps that belong directly to this chain:
		Collection thisLevelsSteps = this.getTopLevelSteps();
		allTheSteps.addAll(thisLevelsSteps);
		
		// second, get the subchains' steps:
		Collection thisLevelsSubchains = this.getDirectSubChains();		
		if (thisLevelsSubchains == null) {
			throw new M4Exception("Chain '" + this.getName() + "': got NULL when asking for subchains!");			
		}
		Iterator it = thisLevelsSubchains.iterator();
		while (it.hasNext()) {
			Chain aChain = (Chain) it.next();
			aChain.getAllSteps(allTheSteps);
		}
	}	

	/**
	 * Return all direct or indirect subchains of this chain.
	 * 
	 * @return a <code>Collection</code> of <code>Chain</code>s 
	 */
	public Collection getAllChains() throws M4Exception {
		Vector allTheChains = new Vector();
		this.getAllChains(allTheChains);
		return allTheChains;
	}
	
	// go recursively through the subchains to collect all subchains
	private void getAllChains(Collection allTheChains) throws M4Exception {		
		
		// get the subchains:
		Collection thisLevelsSubchains = this.getDirectSubChains();	
		if (thisLevelsSubchains == null) {
			throw new M4Exception("Chain '" + this.getName() + "': got NULL when asking for direct subchains!");			
		}	
		allTheChains.addAll(thisLevelsSubchains);
		
		Iterator it = thisLevelsSubchains.iterator();
		while (it.hasNext()) {
			Chain aChain = (Chain) it.next();
			aChain.getAllChains(allTheChains);
		}
	}
	
	/**
	 * @see edu.udo.cs.miningmart.m4.Chain#dependencyExists(Step)
	 */
	public boolean dependencyExists(edu.udo.cs.miningmart.m4.Step toStep) throws M4Exception {
		Collection allStepsOfThisChain = this.getAllSteps();
		if (allStepsOfThisChain == null) {
			throw new M4Exception("Chain '" + this.getName() + "': got NULL when asking for all steps!");
		}
		Iterator it = allStepsOfThisChain.iterator();
		while (it.hasNext()) {
			Step aStep = (Step) it.next();
			if (this.getTheCase().containsDependency(aStep, toStep)) {
				return true;
			}
		}
		return false;
	}

	/**
	 * @see edu.udo.cs.miningmart.m4.Chain#dependencyExists(Chain)
	 */
	public boolean dependencyExists(edu.udo.cs.miningmart.m4.Chain toChain) throws M4Exception {

		Collection allStepsOfThisChain = this.getAllSteps();
		Collection allStepsOfToChain = toChain.getAllSteps();
		if (allStepsOfThisChain == null) {
			throw new M4Exception("Chain '" + this.getName() + "': got NULL when asking for all steps!");
		}
		if (allStepsOfToChain == null) {
			throw new M4Exception("ToChain '" + toChain.getName() + "': got NULL when asking for all steps!");
		}
		Iterator it = allStepsOfThisChain.iterator();
		while (it.hasNext()) {
			Step aStep = (Step) it.next();
			Iterator it2 = allStepsOfToChain.iterator();
			while (it2.hasNext()) {
				Step toStep = (Step) it2.next();
				if (this.getTheCase().containsDependency(aStep, toStep)) {
					return true;
				}
			}
		}
		return false;
	}
	
	/**
	 * This method returns all steps in this chain, including steps 
	 * in subchains etc., that are a <b>direct</b> predecessor of the given Step.
	 *  
	 * @param toStep the Step whose predecessors are returned
	 * @return a Collection of Steps  
	 */
	public Collection getPredecessors(edu.udo.cs.miningmart.m4.Step toStep) throws M4Exception {
		Collection thePredecessors = new Vector();
		Collection allSteps = this.getAllSteps();
		Iterator it = allSteps.iterator();
		Case myCase = (Case) this.getTheCase();
		while (myCase != null && it.hasNext()) {
			Step myStep = (Step) it.next();
			if (myCase.containsDependency(myStep, toStep)) {
				thePredecessors.add(myStep);
			}			
		}
		return thePredecessors;
	}
	
	/**
	 * @see edu.udo.cs.miningmart.m4.Chain#getSuccessors(Step)
	 */
	public Collection getSuccessors(edu.udo.cs.miningmart.m4.Step fromStep) throws M4Exception {
		Collection theSuccessors = new Vector();
		Collection allSteps = this.getAllSteps();
		Iterator it = allSteps.iterator();
		Case myCase = (Case) this.getTheCase();
		while (myCase != null && it.hasNext()) {
			Step myStep = (Step) it.next();
			if (myCase.containsDependency(fromStep, myStep)) {
				theSuccessors.add(myStep);
			}			
		}
		return theSuccessors;
	}
	
	
	/**
	 * @see edu.udo.cs.miningmart.m4.Chain#hasPredecessorOutside()
	 */
	public boolean hasPredecessorOutside() throws M4Exception {
		Iterator stepIt = this.getAllSteps().iterator();
		while (stepIt.hasNext()) {
			Step myStep = (Step) stepIt.next();
			Iterator predIt = myStep.getAllPredecessors().iterator();
			while (predIt.hasNext()) {
				Step predStep = (Step) predIt.next();
				if (predStep.getTheChain() == null || ( ! predStep.belongsToChainOrSubChain(this.getParentChain()))) {
					return true;
				}
			}
		}
		return false;
	}

	/**
	 * @see edu.udo.cs.miningmart.m4.Chain#hasSuccessorOutside()
	 */
	public boolean hasSuccessorOutside() throws M4Exception {
		Iterator stepIt = this.getAllSteps().iterator();
		while (stepIt.hasNext()) {
			Step myStep = (Step) stepIt.next();
			Iterator succIt = myStep.getSuccessors().iterator();
			while (succIt.hasNext()) {
				Step succStep = (Step) succIt.next();
				if (succStep.getTheChain() == null || ( ! succStep.belongsToChainOrSubChain(this.getParentChain()))) {
					return true;
				}
			}
		}
		return false;
	}
	
	/**
	 * This method returns pairs of Steps. In each pair, the first Step belongs to this Chain, or one
	 * of its direct or indirect subchains, and the second to the given Chain or one of its subchains, 
	 * and there is a direct dependency (transition) between them. This method returns a
	 * Collection of Arrays of Steps. Each Array has exactly two elements. The one with index 0
	 * is a Step from this Chain, the one with index 1 is a Step from the given Chain, and a direct
	 * dependeny between the Steps exists.
	 * 
	 * @param toChain the given Chain
	 * @return a Collection of two-element Arrays of Steps
	 * @throws M4Exception
	 */
	public Collection getAllTransitionsToChain(edu.udo.cs.miningmart.m4.Chain toChain) throws M4Exception {
		Collection allToSteps = toChain.getAllSteps();
	    Vector resultingPairs = new Vector();
		if (allToSteps != null) {
			Iterator it = allToSteps.iterator();
			while (it.hasNext()) {
				Step toStep = (Step) it.next();
				Collection allFromSteps = this.getPredecessors(toStep);
				if (allFromSteps != null) {
					Iterator it2 = allFromSteps.iterator();
					while (it2.hasNext()) {
						Step[] onePair = new Step[2];
						onePair[0] = (Step) it2.next();
						onePair[1] = toStep;
						resultingPairs.add(onePair);
					}
				}
			}
		}
		resultingPairs.trimToSize();
		return resultingPairs;
	}
	
	/**
	 * Returns a Collection of the Concepts that are used as input
	 * or output in any of the top level steps of this chain. Steps
	 * of subchains are not involved.
	 * 
	 * @return a Collection of Concepts
	 * @throws M4Exception
	 */
	public Collection getInvolvedConcepts() throws M4Exception {
		Collection myTopLevelSteps = this.getTopLevelSteps();
		Vector involvedConcepts = new Vector();
		if (myTopLevelSteps != null && ( ! myTopLevelSteps.isEmpty())) {
			Iterator it = myTopLevelSteps.iterator();
			while (it.hasNext()) {
				Step aStep = (Step) it.next();
				Collection stepInvolvedConcepts = aStep.getAllInputConcepts();
				Concept out = (Concept) aStep.getOutputConcept();
				if (out != null) {
					stepInvolvedConcepts.add(out);
				}
				if (stepInvolvedConcepts != null && ( ! stepInvolvedConcepts.isEmpty())) {
					Iterator conIt = stepInvolvedConcepts.iterator();
					while (conIt.hasNext()) {
						Concept oneInputConcept = (Concept) conIt.next();
						if ( ! involvedConcepts.contains(oneInputConcept)) {
							involvedConcepts.add(oneInputConcept);
						}
					}
				}
			}
		}
		return involvedConcepts;
	}
	
	/**
	 * Gets the parent chain. Returns NULL if no parent exists.
	 * 
	 * @return the parent chain, or NULL if no parent chain exists.
	 */
	public edu.udo.cs.miningmart.m4.Chain getParentChain() {
		return this.myParentChain;
	}
	
	/**
	 * Gets the description.
	 * @return Returns a String
	 */
	public String getDescription() {
		return myDescription;
	}

	/**
	 * Sets a new <code>Case</code> object and cares about
	 * back-reference integrity.
	 * 
	 * @param m4Case the <code>Case</code> to be set
	 */
	public void setTheCase(edu.udo.cs.miningmart.m4.Case m4Case) throws M4Exception {
		Case.case2chain.checkNameExists(this, m4Case);
		Case.case2chain.updateReferenceTo(this, m4Case);
	}

	/**
	 * Primitive setter of the Case field. Do not use it!
	 * 
	 * @param m4case The Case to set
	 */
	public void primitiveSetCase(edu.udo.cs.miningmart.m4.Case m4case) {
		this.setDirty();
		this.myCase = (Case) m4case;
	}

	public void setParentChain(edu.udo.cs.miningmart.m4.Chain theParentChain) throws M4Exception {
		Chain.chain2parentChain.checkNameExists(this, theParentChain);
		Chain.chain2parentChain.updateReferenceTo(this, theParentChain);
	}
	
	public void primitiveSetParentChain(edu.udo.cs.miningmart.m4.Chain theParentChain) {
		this.setDirty();
		this.myParentChain = (Chain) theParentChain;
	}
	
	/**
	 * Sets the description.
	 * @param description The description to set
	 */
	public void setDescription(String description) {
		this.setDirty();
		this.myDescription = description;
	}

	/**
	 * Adds a <code>Step</code> to this <code>Chain</code> and takes
	 * care about back-reference integrity.
	 * 
	 * @param step the <code>Step</code> to be added
	 */
	public void addStep(edu.udo.cs.miningmart.m4.Step step) throws M4Exception {
		chain2step.checkNameExists((edu.udo.cs.miningmart.m4.core.Step) step, this);
		chain2step.add(this, (edu.udo.cs.miningmart.m4.core.Step) step);
	}

	/**
	 * Removes a <code>Step</code> from this <code>Chain</code> and takes
	 * care about back-reference integrity.
	 * 
	 * @param step the <code>Step</code> to be removed
	 */
	public boolean removeStep(edu.udo.cs.miningmart.m4.Step step) throws M4Exception {
		return chain2step.remove(this, (edu.udo.cs.miningmart.m4.core.Step) step);
	}

	/**
	 * Helper method of the active getter:
	 * Reads the steps embedded in this chain and stores them in this object's
	 * collection of steps.
	 */
	private void readStepsForChainFromDb() throws M4Exception {
		Iterator it = this.getObjectsReferencingMe(Step.class).iterator();
		while (it.hasNext()) {
			this.addStep((edu.udo.cs.miningmart.m4.Step) it.next());
		}		
	}

	/**
	 * Helper method of the active getter:
	 * Reads the direct subchains of this chain and stores them in this object's
	 * collection of subchains.
	 */
	private void readSubchainsForChainFromDb() throws M4Exception {
		Iterator it = this.getObjectsReferencingMe(Chain.class).iterator();
		while (it.hasNext()) {
			this.addSubChain((edu.udo.cs.miningmart.m4.Chain) it.next());
		}		
	}

	/** @see M4Data#removeAllM4References() */
	protected void removeAllM4References() throws M4Exception {
		this.setTheCase(null);
		this.setParentChain(null);
		chain2step.setCollectionTo(this, new Vector());
		chain2parentChain.setCollectionTo(this, new Vector());
		this.setDescription(null);
		this.removeDocObject();
	}

 	/** <code>Chain</code>s have coordinates. */
	protected boolean hasCoordinates() {
		return true;	
	}

	/** @see GraphicalM4Object#getGraphicallyEbeddedObjects() */
	protected Collection getGraphicallyEmbeddedObjects() throws M4Exception {
		Collection ret = this.getTopLevelSteps();
		ret.addAll(this.getDirectSubChains());
		return ret;
	}

	/** @see M4Data#getDependentObjects */
	public Collection getDependentObjects() throws M4Exception {
		Collection ret = super.getDependentObjects();
		ret.addAll(this.getTopLevelSteps());
		ret.addAll(this.getDirectSubChains());
		return ret;
	}

	/**
	 * Make a copy of this chain. The copy is attached to the given case.
	 * 
	 * @see Chain#copy(Case)
	 */
	public edu.udo.cs.miningmart.m4.Chain copy(edu.udo.cs.miningmart.m4.Case newCase) throws M4Exception
	{
		Chain theCopy = new Chain(this.getM4Db());
		
		theCopy.setTheCase(newCase);
		theCopy.setDescription(this.getDescription());
		String nameOfCopy = newCase.getValidName(this.getName(), Chain.class);
		theCopy.setName(nameOfCopy);
		// what about the steps and subchains?
		return theCopy;
	}
	
	
	/**
	 * @see edu.udo.cs.miningmart.m4.Chain#addChain(Chain)
	 */
	public void addSubChain(edu.udo.cs.miningmart.m4.Chain subchain) throws M4Exception {
		chain2parentChain.checkNameExists((Chain) subchain, this);
		chain2parentChain.add(this, (Chain) subchain);
	}

	/**
	 * @see edu.udo.cs.miningmart.m4.Chain#resolveSubChain(Chain)
	 */
	public void resolveSubChain(edu.udo.cs.miningmart.m4.Chain chain) throws M4Exception {
		Iterator it = this.getDirectSubChains().iterator();
		Collection stepsOfResolvedChain = null, chainsOfResolvedChain = null;
		Chain chainToBeResolved = null;
		while (it.hasNext()) {
			Chain myChain = (Chain) it.next();
			if (myChain.equals(chain)) {
				chainToBeResolved = myChain;
				stepsOfResolvedChain = chainToBeResolved.getTopLevelSteps();
				chainsOfResolvedChain = chainToBeResolved.getDirectSubChains();
			}
		}
		if (stepsOfResolvedChain != null && chainsOfResolvedChain != null) {
			Iterator stepsIt = stepsOfResolvedChain.iterator();
			while (stepsIt.hasNext()) {
				Step myStep = (Step) stepsIt.next();
				myStep.setTheChain(this);
			}
			Iterator chainIt = chainsOfResolvedChain.iterator();
			while (chainIt.hasNext()) {
				Chain subChain = (Chain) chainIt.next();
				subChain.setParentChain(this);
			}
			chainToBeResolved.deleteSoon();
		}
	}
	
	/**
	 * @see edu.udo.cs.miningmart.m4.Chain#getTopChains()
	 */
	public Collection getDirectSubChains() throws M4Exception {
		if (!this.allSubchainsLoaded && ( ! this.isNew())) {			
			this.allSubchainsLoaded = true;
			this.readSubchainsForChainFromDb();
		}
		return this.mySubchains;		
	}

}
/*
 * Historie
 * --------
 * 
 * $Log: Chain.java,v $
 * Revision 1.7  2006/04/11 14:10:14  euler
 * Updated license text.
 *
 * Revision 1.6  2006/04/06 16:31:14  euler
 * Prepended license remark.
 *
 * Revision 1.5  2006/03/02 16:49:59  euler
 * Many bugfixes
 *
 * Revision 1.4  2006/01/24 14:03:49  euler
 * Added display of concepts involved in a chain.
 *
 * Revision 1.3  2006/01/12 11:33:11  euler
 * Changed the way coordinates are stored completely.
 *
 * Revision 1.2  2006/01/09 14:08:42  euler
 * Bugfix
 *
 * Revision 1.1  2006/01/03 09:54:17  hakenjos
 * Initial version!
 *
 */
