/*
 * 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.HashSet;
import java.util.Iterator;
import java.util.Map;
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.InterM4Communicator;
import edu.udo.cs.miningmart.m4.utils.InterM4ConceptColumnset;
import edu.udo.cs.miningmart.m4.utils.InterM4ConceptFeature;
import edu.udo.cs.miningmart.m4.utils.InterM4FromConceptProjection;
import edu.udo.cs.miningmart.m4.utils.InterM4FromConceptRelation;
import edu.udo.cs.miningmart.m4.utils.InterM4SubConceptConInh;
import edu.udo.cs.miningmart.m4.utils.InterM4SuperConceptConInh;
import edu.udo.cs.miningmart.m4.utils.InterM4ToConceptProjection;
import edu.udo.cs.miningmart.m4.utils.InterM4ToConceptRelation;
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;

/**
 * This class represents an M4 Concept.
 * 
 * @author Martin Scholz
 * @version $Id: Concept.java,v 1.10 2006/04/11 14:10:14 euler Exp $
 */
public class Concept extends ParameterObject implements XmlInfo, edu.udo.cs.miningmart.m4.Concept {

	/** The M4 table name storing concept information. */
	public static final String M4_TABLE_NAME = "concept_t";
	
	/** DB level: The attribute storing the concept IDs. */
	public static final String ATTRIB_CONCEPT_ID   = "con_id";
	
	/** DB level: The attribute storing the concepts' case IDs. */
	public static final String ATTRIB_CASE_ID      = "con_caid";
	
	/** DB level: The attribute storing the concept names. */
	public static final String ATTRIB_CONCEPT_NAME = "con_name";

	/** DB level: The attribute storing the concept type. */
	public static final String ATTRIB_CONCEPT_TYPE = "con_type";


	// ***** Communication classes *****

	static final InterM4Communicator con2cs  = new InterM4ConceptColumnset();
	
	static final InterM4Communicator con2fea = new InterM4ConceptFeature();
	
	static final InterM4Communicator con2ToRel   = new InterM4ToConceptRelation();
	static final InterM4Communicator con2FromRel = new InterM4FromConceptRelation();
	
	static final InterM4Communicator con2toPro   = new InterM4ToConceptProjection();
	static final InterM4Communicator con2fromPro = new InterM4FromConceptProjection();
	
	static final InterM4Communicator conInheritSub   = new InterM4SubConceptConInh();
	static final InterM4Communicator conInheritSuper = new InterM4SuperConceptConInh();


	/** Cache for getM4Info() */
	public static M4Info m4Info = null;

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

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

	/** @see M4Table.getM4Info() */
	public M4Info getM4Info() {
		if (m4Info == null) {
			M4InfoEntry[] m4i = {
			 // The NOT_NULL for the foreign key to Case is not in the DB, but it is useful to enforce integrity!
			 // As a result storing Concepts without a Case means deleting them from the DB and Cache!
			 
				new M4InfoEntry(ATTRIB_CONCEPT_ID,   "getId",      "setId",            long.class,   NOT_NULL),
				new M4InfoEntry(ATTRIB_CONCEPT_NAME, "getName",    "setName",          String.class, NOT_NULL),
				new M4InfoEntry(ATTRIB_CASE_ID, 	 "getTheCase", "primitiveSetCase", edu.udo.cs.miningmart.m4.Case.class,   NOT_NULL),
				new M4InfoEntry(ATTRIB_CONCEPT_TYPE, "getType",    "setType",          String.class, NOT_NULL)
			};
			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("Type",         "getType",          	    "setType",              String.class),
				// new M4InfoEntry("SuperConcept", "getTheSuperConcept",   	"setTheSuperConcept",   edu.udo.cs.miningmart.m4.Concept.class),
				// new M4InfoEntry("ProjectionOf", "getTheFromProjection", 	"setTheFromProjection", edu.udo.cs.miningmart.m4.Concept.class),
				new M4InfoEntry("Docu",         "getDocumentation",    		"setDocumentation",     String.class)
			};
			xmlInfo = new M4Info(m4i);
		}
		return xmlInfo;
	}
	
	// ***** The private fields of the instances *****

    private final Vector myColumnSets = new Vector();
    private boolean allColumnSetsLoaded = false;
    
    private final Vector myFeatures = new Vector();
    private boolean allFeaturesLoaded = false;
    
	private final Vector myFromProjections = new Vector();
    private boolean allfromProjectionsLoaded = false;
    
	private final Vector myToProjections = new Vector();
	private boolean allToProjectionsLoaded = false;
	
	private final Vector mySubConceptsInh = new Vector(); // conceptsInheritances where this is subconcept
	private boolean allSubConceptsLoaded = false;

	private final Vector mySuperConceptsInh = new Vector(); // conceptsInheritances where this is super concept
	private boolean allSuperConceptsLoaded = false; 

	private final Vector myFromRelations = new Vector();
	private boolean allFromRelationsLoaded = false;
	
	private final Vector myToRelations = new Vector();
	private boolean allToRelationsLoaded = false;
	
    private String  myType;
	private edu.udo.cs.miningmart.m4.core.Case myCase;

	private EstimatedStatistics myEstimations = null;

	/** @see edu.udo.cs.miningmart.m4.core.M4Object#Constructor */
	public Concept(DB m4Db) { super(m4Db); }


	/** @see edu.udo.cs.miningmart.m4.core.Parameter#print */
    public void print() {
        this.doPrint(Print.M4_OBJECT, "Concept " + myName + "(Id = " + myId + ";" + " Type = "
                      + myType + ") ");

		try {
			Collection colSets = this.getColumnSets();
    	    if (colSets != null) {
        		Iterator it = colSets.iterator();
        		while (it.hasNext()) {
					((Columnset) it.next()).print();
				}
        	}
  
	        Collection features = this.getFeatures();
    	    if (features != null) {
        		Iterator it = features.iterator();
        		while (it.hasNext()) {
					((Feature) it.next()).print();
				}
    	    }
		}
        catch (M4Exception e) {
			this.doPrint(Print.MAX, "Warning: Exception caught while printing!");
			this.doPrint(e);
		}
        super.print();  // prints graphical info
    }
	
	/**
	 * @see edu.udo.cs.miningmart.m4.core.M4Data#getObjectsInNamespace(Class)
	 */
	protected Collection getObjectsInNamespace(Class typeOfObjects) throws M4Exception {
		if (typeOfObjects.isAssignableFrom(Columnset.class)) {
			return this.getColumnSets();
		}
		else if (typeOfObjects.isAssignableFrom(BaseAttribute.class)) {
			return this.getAllBaseAttributes();
		}
		else if (typeOfObjects.isAssignableFrom(MultiColumnFeature.class)) {
			return this.getAllMultiColumnFeatures();
		}
		else if (typeOfObjects.isAssignableFrom(Feature.class)) {
			return this.getFeatures();
		}
		else throw new M4Exception("Concept.getObjectsInNamespace: unknown type of objects given: " + typeOfObjects.getName());
	}

	// ***** Getter and setter methods ****
	
	/**
	 * Do not use spaces in Concept names, because these names
	 * are also used at the DB level for Columnset names.
	 * @param name the new name to be set
	 * 
	 * @see M4Object#setName(String)
	 */
	public void setName(String name) {
		name = this.replaceSpacesInName(name);
		super.setName(name);
	}
	
	/**
	 * Setter method for the type of this concept.
	 * 
	 * @param t the new type (one of "DB", "Base", "Mining")
	 */
    public void setType(String t) throws M4Exception
    {
    	this.setDirty();
    	
    	if ( ! (t.equalsIgnoreCase(Concept.TYPE_DB) || 
    			t.equalsIgnoreCase(Concept.TYPE_BASE) || 
				t.equalsIgnoreCase(Concept.TYPE_MINING)))
    	{  throw new M4Exception("Tried to set unknown type into Concept '" + this.getName() + "': " + t);  }
        myType = t.toUpperCase();
    }

	/**
	 * Overwrites the superclass method because the columnsets and
	 * Features that belong to this concept must be deleted, too.
	 * 
	 * @throws M4Exception
	 */
	public void deleteSoon() throws M4Exception {
		
		Collection all = new Vector();
		all.addAll(this.getColumnSets());
		all.addAll(this.getFeatures());
		all.addAll(this.getTheFromRelationships());
		all.addAll(this.getTheToRelationships());
		all.addAll(this.getProjectionsAsFromConcept());
		all.addAll(this.getProjectionsAsToConcept());
		all.addAll(this.getConceptInheritanceAsSubConcept());
		all.addAll(this.getConceptInheritanceAsSuperConcept());
		
		Iterator it = (new Vector(all)).iterator();
		while (it.hasNext()) {
			M4Data m4d = (M4Data) it.next();
			m4d.deleteSoon();
		}
		
		super.deleteSoon();
	}
    
	/**
	 * Getter method.
	 * 
	 * @return the type of this concept
	 */
    public String getType() {
        return myType;
    }

    public edu.udo.cs.miningmart.m4.Parameter getParameterWhereThisIsOutputConcept() 
           throws M4Exception {
		Collection params = this.getParameterReferences();
		if (params != null) {
			Iterator it = params.iterator();
			while (it.hasNext()) {
				Parameter p = (Parameter) it.next();
				if (p != null && p.getParameterType().equals(Parameter.TYPE_OUTPUT))
					return p;
			}
		}
		return null;
    }

    /**
     * Returns all Steps in the Case that use this Concept as
     * an input concept.
     * 
     * @return a Collection of Steps
     */
    public Collection getStepsWhereThisIsInputConcept() throws M4Exception {
		Collection params = this.getParameterReferences();
		Vector steps = new Vector();
		if (params != null) {
			Iterator it = params.iterator();
			while (it.hasNext()) {
				Parameter p = (Parameter) it.next();
				if (p != null && p.getParameterType().equals(Parameter.TYPE_INPUT)) {
					steps.add(p.getTheStep());
				}					
			}
		}
		return steps;
    }    

    /**
     * Returns the Step in the Case where this Concept is created as
     * the output concept.
     * 
     * @return a Step
     */
    public edu.udo.cs.miningmart.m4.Step getStepWhereThisIsOutputConcept() throws M4Exception {
		Collection params = this.getParameterReferences();
		if (params != null) {
			Iterator it = params.iterator();
			while (it.hasNext()) {
				Parameter p = (Parameter) it.next();
				if (p != null && p.getParameterType().equals(Parameter.TYPE_OUTPUT)) {
					return (Step) p.getTheStep();
				}					
			}
		}
		return null;
    }
    
	// ***** Handling of Columnsets *****

	/**
	 * Add a columnset to this concept's columnsets.
	 * 
	 * @param cs the additional columnset
	 */
	public void addColumnSet(edu.udo.cs.miningmart.m4.Columnset cs) throws M4Exception
	{
		con2cs.checkNameExists((edu.udo.cs.miningmart.m4.core.Columnset) cs, this);
		con2cs.add(this, (edu.udo.cs.miningmart.m4.core.Columnset) cs);
	}

	/**
	 * Remove a Columnset from this concept.
	 * 
	 * @param cs the Columnset
	 */
	public boolean removeColumnset(edu.udo.cs.miningmart.m4.Columnset cs) throws M4Exception
	{
		return con2cs.remove(this, (edu.udo.cs.miningmart.m4.core.Columnset) cs);
	}

	/**
	 * @param cs a <code>Columnset</code>
	 * @return <code>true</code> if a <code>Columnset</code> with the same ID is already
	 * linked to this <code>Concept</code>
	 * */
	public boolean hasColumnset(edu.udo.cs.miningmart.m4.Columnset cs) throws M4Exception
	{
		Collection csets = this.getFeatures();
		if (cs == null || csets == null) {
			return false;	
		}
		return csets.contains(cs);
	}

	/**
	 * Set the columnsets of this concept.
	 * 
	 * @param theColumnsets An array with the new columnsets.
	 */
    public void setColumnSets(Collection theColumnsets) throws M4Exception
    {
		con2cs.setCollectionTo(this, theColumnsets);
    }    

	/**
	 * @return the columnsets that belong to this concept
	 */
    public Collection getColumnSets() throws M4Exception {        
        if (!allColumnSetsLoaded && ( ! this.isNew())) {
			this.allColumnSetsLoaded = true;
            this.readColumnSetsForConceptFromDB();
        }
        return this.myColumnSets;
    }
	
    /**
     * This method returns a suffix for the current <i>Columnset</i>.
     * See the method "getCurrentColumnset()".
     * It is ment as a helper method for generating distinct table
     * names, when having an input concept with multiple column sets.
     *
     * @return the index of the current <i>Columnset</i> as a <i>String</i>
     *         or the empty String if the concept has only one <i>Columnset</i>
     */
	public String getCurrentSuffix()
	{
		if (multiStepsColumnsets != null)
		{
			return ("_" + columnsetIndex);
		}
		else
			return ("");
	}

    /** Fills an array of ColumnSet-Objects for this Concept. */
    private void readColumnSetsForConceptFromDB() throws M4Exception
    {
		Iterator it = this.getObjectsReferencingMe(Columnset.class).iterator();
		while (it.hasNext()) {
			this.addColumnSet((edu.udo.cs.miningmart.m4.Columnset) it.next());
		}
    }

    // --- Methods / fields for the control structure implementing multi steps: ---

    private Vector multiStepsColumnsets;

    private int columnsetIndex;

    /**
     * This method is invoked to initialize the support for concepts
     * referring to multiple column sets (multi steps). The set of
     * column sets is stored internally and a pointer to a "current"
     * column set is maintained, in order to ease the handling by
     * operators.
     * 
     * @param columnsets An array of column sets this concept refers to.
     */
    public void initMultiStepSupport(Collection columnsets) {
    	// work on local copy:
		this.multiStepsColumnsets = new Vector(columnsets);
		
		// set counter to first element:
		this.resetColumnSets();
    }

	/**
	 * This method returns a specific columnset: in a compilation where
	 * multistep operators have occured, there can be more than one columnset
	 * for an input concept. The compiler then chooses one columnset for the step, 
	 * compiles the step with that columnset, then chooses another one, compiles
	 * again and so on. This method returns the currently chosen columnset.
	 * 
	 * @return the current columnset as selected by the compiler
	 */
	public edu.udo.cs.miningmart.m4.Columnset getCurrentColumnSet() throws M4Exception
	{
		if (multiStepsColumnsets != null) // MultiStep-Support enabled!
			return ((edu.udo.cs.miningmart.m4.Columnset) multiStepsColumnsets.get(columnsetIndex));
		else
		{ // No MultiStep support:
			Collection csets = this.getColumnSets();
			if ((csets != null) && !csets.isEmpty()) {
				return (edu.udo.cs.miningmart.m4.Columnset) csets.iterator().next();
			}
		}
		return null;
	}

    /** 
     * In case of multi step support this method resets the internal
     * pointer to the current column set to the first one. 
     */
    public void resetColumnSets() {
		this.columnsetIndex = 0;
    }

    /** 
     * In case of multi step support this method answers the question
     * if <i>getNextColumnSet()</i> will succeed.
     * 
     * @return <i>true</i> if and only if there is at least one more
     *         column set after the current one.
    */
    public boolean hasNextColumnSet() throws M4Exception {
		if (this.multiStepsColumnsets != null) {
			return (columnsetIndex + 1 < multiStepsColumnsets.size());
		}
		else {
			Collection c = this.getColumnSets();
			if (c == null) {
				throw new M4Exception
					("Concept '" + this.getName() + "', id: " + this.getId()
					+ "\nError during call of method Concept.hasNextColumnSet(): "
					+ "\"InputConcept\".getColumnSets() returned null.");
			}
		}
		return false;
    }

    /** 
     * In case of multi step support this method returns the next
     * column set after the current one and increases the internal
     * pointer accordingly. If this method is called when
     * <i>hasNextColumnSet()</i> returns <i>false</i> then an
     * exception is thrown. 
     * 
     * @return the next columnset
     */
    public edu.udo.cs.miningmart.m4.Columnset getNextColumnSet() throws M4Exception {
    	if (this.hasNextColumnSet()) {
			return ( (edu.udo.cs.miningmart.m4.Columnset) multiStepsColumnsets.get(++columnsetIndex) );
    	}
    	else { // NullPointerException is already 'caught' by hasNextColumnSet
    		throw new M4Exception
				("Concept '" + this.getName() + "', id: " + this.getId()
				+ "\nTried to call method getNextColumnSet() with no Columnset left!");
    	}
    }
	

	// ***** Handling cases *****

	/**
	 * Getter method.
	 * 
	 * @return the <code>Case</code> this <code>Concept</code> belongs to
	 */
	public edu.udo.cs.miningmart.m4.Case getTheCase() throws M4Exception {
		return this.myCase;
	}

	/**
	 * Sets the case for this concept
	 * 
	 * @param ca the <code>Case</code> this <code>Concept</code> belongs to
	 */
	public void setTheCase(edu.udo.cs.miningmart.m4.Case ca) throws M4Exception {
		edu.udo.cs.miningmart.m4.core.Case.case2con.checkNameExists(this, ca);
		edu.udo.cs.miningmart.m4.core.Case.case2con.updateReferenceTo(this, ca);
	}
	
	/**
	 * Primitive setter method. Do not use it!
	 * @param ca the <code>Case</code> object to be set
	 */
	public void primitiveSetCase(edu.udo.cs.miningmart.m4.Case ca) {
		this.setDirty();
		this.myCase = (Case) ca;
	}


	// ***** Handling features *****

	/**
	 * Add a Feature (a BaseAttribute or a MultiColumnFeature) to this concept.
	 * 
	 * @param f the new Feature
	 */
	public void addFeature(edu.udo.cs.miningmart.m4.Feature f) throws M4Exception {
		con2fea.checkNameExists((edu.udo.cs.miningmart.m4.core.Feature) f, this);
		con2fea.add(this, (edu.udo.cs.miningmart.m4.core.Feature) f);
		
		// Since a feature has been added, propagate the changes:
		// this.propagateChangesToDependingConcepts();
	}

	/**
	 * Get a specific Feature by number.
	 * 
	 * @param index the number of the Feature
	 * @return the Feature with that index
	 */
    public edu.udo.cs.miningmart.m4.Feature getFeature(int index) throws M4Exception {
        Collection fea = this.getFeatures();
        if ((fea == null) || (index < 0) && (index >= fea.size())) {
            throw new M4Exception("Concept.getFeature(): No Featurearray exists, or wrong index!");
        }

		Vector v;
        if (fea instanceof Vector) { // We know that it's a Vector ...
        	v = (Vector) fea;
        }
        else { // ... but this might change in the future?
        	v = new Vector(fea);	
        }
        return (edu.udo.cs.miningmart.m4.Feature) v.get(index);
    }

	/**
	 * Get a specific Feature by name
	 * 
	 * @param name
	 * @return the Feature with that name
	 */
    public edu.udo.cs.miningmart.m4.Feature getFeature(String name) throws M4Exception {
        Collection fea = this.getFeatures();
        if (fea == null) {
            throw new M4Exception("Concept.getFeature(): No Featurearray exists!");
        }
        Iterator it = fea.iterator();
        while (it.hasNext()) {
        	Feature f = (Feature) it.next();
        	if (f.getName().equals(name)) {
        		return f;
        	}
        }
        return null;
    }
    
	/**
	 * Remove a Feature (a BaseAttribute or a MultiColumnFeature) from this concept.
	 * 
	 * @param f the Feature
	 */
	public boolean removeFeature(edu.udo.cs.miningmart.m4.Feature feature) throws M4Exception {		
		boolean ok = con2fea.remove(this, (edu.udo.cs.miningmart.m4.core.Feature) feature);
		/*
		if (ok) {
			this.propagateChangesToDependingConcepts();
		}
		*/
		return ok;
	}
	
	public boolean removeFeatureByName(String name) throws M4Exception {
		Feature f = (Feature) this.getFeature(name);
		return this.removeFeature(f);
	}
	
	/**
	 * Set all Features into this concept.
	 * 
	 * @param fs A Collection of Feature objects
	 */
    public void setFeatures(Collection theFeatures) throws M4Exception {
		con2fea.setCollectionTo(this, theFeatures);
    }
    
    /**
     * Remove all features from this concept.
     * 
     * @throws M4Exception
     */
    public void removeAllFeatures() throws M4Exception {
    	con2fea.setCollectionTo(this, new Vector());
    }

	/**
	 * @return All Features of this concept.
	 */
	public Collection getFeatures() throws M4Exception
	{
		if (!allFeaturesLoaded && ( ! this.isNew()))
		{
			this.allFeaturesLoaded = true;
			this.readFeaturesForConcept();
		}
		return this.myFeatures;
	}

	/**
	 * Returns a String with comma-separated names of the Features
	 * of this Concept.
	 * 
	 * @return a String
	 * @throws M4Exception
	 */
	public String getFeatureNames() throws M4Exception {
		Collection fs = this.getFeatures();
		if (fs == null || fs.isEmpty()) {
			return "";
		}
		Iterator it = fs.iterator();
		String ret = "";
		while (it.hasNext()) {
			Feature f = (Feature) it.next();
			ret += f.getName() + ", ";
		}
		return ret.substring(0, ret.length() - 2);
	}
	
	/**
	 * Returns the number of features that this Concept has.
	 * 
	 * @return the number of features as an int
	 */
	public int getNumberOfFeatures() throws M4Exception
	{
		Collection features = this.getFeatures();
		if (features == null)
		{   return 0;  }
		return features.size();
	}
	
	/**
	 * @param f a <code>Feature</code>
	 * @return <code>true</code> if a <code>Feature</code> with the same ID is already
	 * linked to this <code>Concept</code>
	 * */
	public boolean hasFeature(edu.udo.cs.miningmart.m4.Feature f) throws M4Exception
	{
		Collection features = this.getFeatures();
		if (f == null || features == null) {
			return false;	
		}
		return features.contains(f);
	}
	
    /** Fills the array of Feature objects of this Concept. */
    private void readFeaturesForConcept() throws M4Exception
    {
		this.readBaseAttributesFromDb();
		this.readMCFsFromDb();
    }

	/** Helper method of readFeaturesForConcept() */
	private void readBaseAttributesFromDb() throws M4Exception {
		Iterator it = this.getObjectsReferencingMe(BaseAttribute.class).iterator();
		while (it.hasNext()) {
			this.addFeature((edu.udo.cs.miningmart.m4.BaseAttribute) it.next());
		}
	}

	/** Helper method of readFeaturesForConcept() */
	private void readMCFsFromDb() throws M4Exception
	{
		Iterator it = this.getObjectsReferencingMe(MultiColumnFeature.class).iterator();
		while (it.hasNext()) {
			this.addFeature((edu.udo.cs.miningmart.m4.MultiColumnFeature) it.next());
		}
	}
    
	/**
	 * When this Concept has changed its Features, depending concepts
	 * of later steps can be adapted with this method. The method is not
	 * called automatically from methods addFeature or removeFeature because
	 * those methods are used too frequently during normal M4 Java administration.
	 */
	public void propagateChangesToDependingConcepts() throws M4Exception {
		// find depending steps:
		Collection stepsToAdapt = this.getStepsWhereThisIsInputConcept();
		// adapt the output of those steps:
		Iterator it = stepsToAdapt.iterator();
		while (it.hasNext()) {
			Step aStep = (Step) it.next();
			aStep.updateOutput(null);
		}
	}

	// ***** Handling relationships *****

	/**
	 * Returns a Collection of Relations in which this concept
	 * is the FromConcept.
	 * 
	 * @return Collection
	 */
	public Collection getTheFromRelationships() throws M4Exception
	{	
		if ( ! this.allFromRelationsLoaded && ( ! this.isNew())) {
			this.allFromRelationsLoaded = true;
			this.readRelationsFromDB(true);
		}
		return this.myFromRelations;  
	}
	
	/**
	 * Returns a Relation with this concept as the FromConcept and the
	 * given concept as the ToConcept, if such a Relation exists, and NULL otherwise.
	 * 
	 * @param toConcept the given ToConcept
	 * @return a Relation between this concept and the ToConcept or NULL
	 * @throws M4Exception
	 */
	public edu.udo.cs.miningmart.m4.Relation getRelationshipToConcept(edu.udo.cs.miningmart.m4.Concept toConcept) throws M4Exception {
		Iterator it = this.getTheFromRelationships().iterator();
		while (it.hasNext()) {
			Relation rel = (Relation) it.next();
			if (rel.getTheFromConcept().equals(this) && rel.getTheToConcept().equals(toConcept)) {
				return rel;
			}
		}
		return null;
	}	
	
	/**
	 * Returns a Relation with this concept as the ToConcept and the
	 * given concept as the FromConcept, if such a Relation exists, and NULL otherwise.
	 * 
	 * @param fromConcept the given FromConcept
	 * @return a Relation between this concept and the FromConcept or NULL
	 * @throws M4Exception
	 */
	public edu.udo.cs.miningmart.m4.Relation getRelationshipFromConcept(edu.udo.cs.miningmart.m4.Concept fromConcept) throws M4Exception {
		Iterator it = this.getTheToRelationships().iterator();
		while (it.hasNext()) {
			Relation rel = (Relation) it.next();
			if (rel.getTheToConcept().equals(this) && rel.getTheFromConcept().equals(fromConcept)) {
				return rel;
			}
		}
		return null;
	}	
	
	/**
	 * Adds the given Relation to the fromRelationships of this concept.
	 */
	protected void addFromRelationship(edu.udo.cs.miningmart.m4.Relation newRel) throws M4Exception
	{
		con2FromRel.checkNameExists((edu.udo.cs.miningmart.m4.core.Relation) newRel, this);
		con2FromRel.add(this, (edu.udo.cs.miningmart.m4.core.Relation) newRel);
	}
	
	/**
	 * Adds the given Relation to the toRelationships of this concept.
	 */
	protected void addToRelationship(edu.udo.cs.miningmart.m4.Relation newRel)  throws M4Exception
	{
		con2ToRel.checkNameExists((edu.udo.cs.miningmart.m4.core.Relation) newRel, this);
		con2ToRel.add(this, (edu.udo.cs.miningmart.m4.core.Relation) newRel);
	}
	
	/**
	 * Returns a Collection of Relations in which this concept
	 * is the ToConcept.
	 * 
	 * @return Collection
	 */
	public Collection getTheToRelationships() throws M4Exception
	{	
		if ( ! this.allToRelationsLoaded && ( ! this.isNew())) {
			this.allToRelationsLoaded = true;
			this.readRelationsFromDB(false);
		}
		return this.myToRelations;  
	}


	// ***** Handling concept inheritance *****

	/**
	 * Returns the Collection of ConceptInheritance objects with this Concept
	 * as a Super-Concept
	 * 
	 * @return <code>Collection</code> of <code>ConceptInheritance</code>
	 */
	public Collection getConceptInheritanceAsSuperConcept() throws M4Exception {	
		if ( ! this.allSubConceptsLoaded && ( ! this.isNew())) {   
			this.allSubConceptsLoaded = true;
			this.readSubConceptsFromDB();
		}
		return this.mySubConceptsInh;
	}

	/**
	 * Returns the Collection of ConceptInheritance objects with this Concept
	 * as a Sub-Concept
	 * 
	 * @return <code>Collection</code> of <code>ConceptInheritance</code>
	 */
	public Collection getConceptInheritanceAsSubConcept() throws M4Exception {	
		if ( ! this.allSuperConceptsLoaded && ( ! this.isNew())) { 
			this.allSuperConceptsLoaded = true;
			this.readSuperConceptsFromDB();
		}
		return this.mySuperConceptsInh;
	}

	/**
	 * This method gets the set of all super concepts
	 * 
	 * @return a <code>Collection</code> of <code>Concept</code>s
	 */
	public Collection getSuperConcepts() throws M4Exception {
		Iterator it = this.getConceptInheritanceAsSubConcept().iterator();
		Collection concepts = new Vector();
		while (it.hasNext()) {
			ConceptInheritance conInh = (ConceptInheritance) it.next();
			if (conInh != null) {
				Concept con = (Concept) conInh.getSuperConcept();
				if (con != null)
					concepts.add(con);
			}
		}
		return concepts;
	}

	/**
	 * This method gets the set of all sub concepts
	 * 
	 * @return a <code>Collection</code> of <code>Concept</code>s
	 */
	public Collection getSubConcepts() throws M4Exception {
		Iterator it = this.getConceptInheritanceAsSuperConcept().iterator();
		Collection concepts = new Vector();
		while (it.hasNext()) {
			ConceptInheritance conInh = (ConceptInheritance) it.next();
			if (conInh != null) {
				Concept con = (Concept) conInh.getSubConcept();
				if (con != null)
					concepts.add(con);
			}
		}
		return concepts;
	}

	/**
	 * Usually there is only one Super Concept.
	 *
	 * @return the first Super Concept or <code>null</code>
	 */
	public edu.udo.cs.miningmart.m4.Concept getTheSuperConcept() throws M4Exception
	{	
		Collection col = this.getSuperConcepts();
		if (col == null || col.isEmpty()) {
			return null;
		}
		else {
			return (edu.udo.cs.miningmart.m4.Concept) col.iterator().next();	
		}
	}
		
	/**
	 * Adds a Sub Concept.
	 * 
	 * @param newSubConcept The Sub Concept to add
	 */
	public void addSubConcept(edu.udo.cs.miningmart.m4.Concept newSubConcept) throws M4Exception
	{
		if (newSubConcept != null) {
			Collection col = newSubConcept.getSubConcepts();
			if (col.contains(this)) {
				String thisName = this.getName();
				String subName  = newSubConcept.getName();
				throw new M4Exception(
					"Adding/setting sub-concept (ID: " + newSubConcept.getId()
					+ ", Name: " + (subName == null ? "<null>" : subName)
					+ ") for concept (ID: " + this.getId() + ", Name: "
					+ (thisName == null ? "<null>" : thisName) 
					+ ") would create an inheritance cycle!");
			}

			ConceptInheritance conInh =
				(ConceptInheritance) this.getM4Db().createNewInstance(edu.udo.cs.miningmart.m4.core.ConceptInheritance.class);
				
			conInh.setSubConcept(newSubConcept);
			conInh.setSuperConcept(this); // sets the references automatically
		}
	}

	/**
	 * Adds a Super Concept and check for cycles in the inheritance graph.
	 * 
	 * @param newSuperConcept The Super Concept to add
	 */
	public void addSuperConcept(edu.udo.cs.miningmart.m4.Concept newSuperConcept) throws M4Exception
	{
		if (newSuperConcept != null) {

			Collection col = this.getAllSubConcepts();
			if (col.contains(newSuperConcept)) {
				String thisName = this.getName();
				String supName  = newSuperConcept.getName();
				throw new M4Exception(
					"Adding/setting super concept (ID: " + newSuperConcept.getId()
					+ ", Name: " + (supName == null ? "<null>" : supName)
					+ ") for concept (ID: " + this.getId() + ", Name: "
					+ (thisName == null ? "<null>" : thisName) 
					+ ") would create an inheritance cycle!");
			}
			
			ConceptInheritance conInh =
				(ConceptInheritance) this.getM4Db().createNewInstance(edu.udo.cs.miningmart.m4.core.ConceptInheritance.class);
				
			conInh.setSubConcept(this); // sets the references automatically
			conInh.setSuperConcept(newSuperConcept);
		}
	}
	
	public void removeSubConcept(edu.udo.cs.miningmart.m4.Concept theSubConcept) throws M4Exception {
		Iterator subIt = this.getConceptInheritanceAsSuperConcept().iterator();
		ConceptInheritance toBeRemoved = null;
		while (subIt.hasNext()) {
			ConceptInheritance mySubCon = (ConceptInheritance) subIt.next();
			if (mySubCon.getSubConcept().equals(theSubConcept)) {
				toBeRemoved = mySubCon;
			}
		}
		this.removeSubConcept(toBeRemoved);
	}

	public void removeSuperConcept(edu.udo.cs.miningmart.m4.Concept theSuperConcept) throws M4Exception {
		Iterator superIt = this.getConceptInheritanceAsSubConcept().iterator();
		ConceptInheritance toBeRemoved = null;
		while (superIt.hasNext()) {
			ConceptInheritance mySuperCon = (ConceptInheritance) superIt.next();
			if (mySuperCon.getSubConcept().equals(theSuperConcept)) {
				toBeRemoved = mySuperCon;
			}
		}
		this.removeSuperConcept(toBeRemoved);
	}
	
	/**
	 * Checks wether the given concept has the same features as this one
	 * (correspondence of features is by name). 
	 */
	public boolean isValidSubconcept(edu.udo.cs.miningmart.m4.Concept potentialSubConcept) throws M4Exception {
		if (potentialSubConcept == null) {
			return false;
		}
		// first check if this Concept's features are all present in
		// the given Concept's feature set:
		Iterator firstFeaturesIterator = this.getFeatures().iterator();
		while (firstFeaturesIterator.hasNext()) {
			Feature firstF = (Feature) firstFeaturesIterator.next();
			boolean correspondingFeatureFound = false;
			Iterator secondFeaturesIterator = potentialSubConcept.getFeatures().iterator();
			while (secondFeaturesIterator.hasNext()) {
				Feature secondF = (Feature) secondFeaturesIterator.next();
				if (firstF.correspondsTo(secondF)) {
					correspondingFeatureFound = true;
				}
			}
			if ( ! correspondingFeatureFound) {
				return false;
			}
		}

		// second check if the given Concept's features are all present in
		// this Concept's feature set:
		firstFeaturesIterator = potentialSubConcept.getFeatures().iterator();
		while (firstFeaturesIterator.hasNext()) {
			Feature firstF = (Feature) firstFeaturesIterator.next();
			boolean correspondingFeatureFound = false;
			Iterator secondFeaturesIterator = this.getFeatures().iterator();
			while (secondFeaturesIterator.hasNext()) {
				Feature secondF = (Feature) secondFeaturesIterator.next();
				if (firstF.correspondsTo(secondF)) {
					correspondingFeatureFound = true;
				}
			}
			if ( ! correspondingFeatureFound) {
				return false;
			}
		}
		
		// feature sets are equal:
		return true;
	}
	
	/** Private helper method of addSuperConcept(Concept) */
	private Collection getAllSubConcepts() throws M4Exception {
		Vector  open   = new Vector();
		HashSet closed = new HashSet();
		open.add(this);
		
		while (!open.isEmpty()) {
			Concept next = (Concept) open.firstElement();
			if (next != null) {
				Iterator it = next.getSubConcepts().iterator();
				while (it.hasNext()) {
					Concept sub = (Concept) it.next();
					if (!open.contains(sub) && !closed.contains(sub)) {
						open.add(sub);
					}
				}
				open.remove(next);
				closed.add(next);
			}
		}
		return closed;
	}

	/**
	 * Sets the Super Concept to be the only Super Concept of this Concept
	 * @param newSuperConcept The Super Concept to set
	 */
	public void setTheSuperConcept(edu.udo.cs.miningmart.m4.Concept newSuperConcept) throws M4Exception
	{	
		// delete all super-concepts:
		Concept.conInheritSub.setCollectionTo(this, new Vector());
		
		// add the new super concept
		this.addSuperConcept(newSuperConcept);
	}

	/**
	 * Adds a Sub Concept.
	 * 
	 * @param conInh The <code>ConceptInheritance</code> object
	 * (with this concept in the role of the super concept!) to add
	 */
	private void addSubConcept(ConceptInheritance conInh) throws M4Exception
	{
		conInheritSuper.add(this, conInh);
	}

	/**
	 * Adds a Super Concept.
	 * 
	 * @param conInh The <code>ConceptInheritance</code> object
	 * (with this concept in the role of the sub concept!) to add
	 */
	private void addSuperConcept(ConceptInheritance conInh) throws M4Exception
	{
		conInheritSub.add(this, conInh);
	}

	
	
	/**
	 * Removes a Sub Concept.
	 * 
	 * @param conInh The <code>ConceptInheritance</code> object (with this
	 * concept as the super concept) to remove
	 */
	private void removeSubConcept(ConceptInheritance conInh) throws M4Exception	{
		Concept.conInheritSuper.remove(this, conInh);
	}

	/**
	 * Removes a Super Concept.
	 * 
	 * @param conInh The <code>ConceptInheritance</code> object (with this
	 * concept as the sub concept) to remove
	 */
	private void removeSuperConcept(ConceptInheritance conInh) throws M4Exception {
		Concept.conInheritSub.remove(this, conInh);
	}



	// ***** Handling projections *****

	/**
	 * Create a new Projection from this concept to the given concept.
	 * The new Projection is returned; in it, this concept is the from 
	 * concept and the given concept is the to concept.
	 */
	public edu.udo.cs.miningmart.m4.Projection createProjectionToConcept(edu.udo.cs.miningmart.m4.Concept toConcept, String nameOfProjection) 
	throws M4Exception {
		Projection proj = new Projection(this.getM4Db());
		proj.setName(nameOfProjection);
		proj.setFromConcept(this);
		proj.setToConcept(toConcept);
		return proj;
	}
	
	/**
	 * Adds a To-Projection.
	 * 
	 * @param projection The Projection to add with this Concept as the To-Concept
	 */
	public void addToProjection(edu.udo.cs.miningmart.m4.Projection projection) throws M4Exception
	{
		if (projection != null && projection.getFromConcept() != null) {
			Collection col =
				Projection.getTransitiveToProjectionsForConcept(this);
			if (col.contains(projection.getFromConcept())) {
				projection.setFromConcept(null);
				throw new M4Exception(
					"Setting the toConcept for this projection would result in"
					+ " cyclic projections!");
			}
		}
		con2toPro.add(this, (edu.udo.cs.miningmart.m4.core.Projection) projection);
	}

	/**
	 * Removes a To-Projection.
	 * 
	 * @param projection The Projection (with this Concept as the To-Concept) to be removed
	 * @return <code>true</code> if the object was found and removed from the
	 * 		   <code>Collection</code>
	 */
	public boolean removeToProjection(edu.udo.cs.miningmart.m4.Projection projection) 
	       throws M4Exception {
		return con2fromPro.remove(this, (edu.udo.cs.miningmart.m4.core.Projection) projection);
	}

	/**
	 * Adds a From-Projection.
	 * 
	 * @param projection The Projection to add with this Concept as the From-Concept
	 * @return <code>true</code> if the object was found and removed from the
	 * 		   <code>Collection</code>
	 */
	public void addFromProjection(edu.udo.cs.miningmart.m4.Projection projection) 
	       throws M4Exception {
		if (projection != null && projection.getToConcept() != null) {
			Collection col =
				Projection.getTransitiveToProjectionsForConcept((Concept) ((edu.udo.cs.miningmart.m4.core.Projection) projection).getToConcept());
			if (col.contains(this)) {
				projection.setToConcept(null);
				throw new M4Exception(
					"Setting the toConcept for this projection would result in"
					+ " cyclic projections!");	
			}
		}
		con2fromPro.add(this, (edu.udo.cs.miningmart.m4.core.Projection) projection);
	}
	
	/**
	 * Removes a From-Projection.
	 * 
	 * @param projection The Projection (with this Concept as the From-Concept) to be removed
	 */
	public boolean removeFromProjection(edu.udo.cs.miningmart.m4.Projection projection) throws M4Exception {
		return con2fromPro.remove(this, (edu.udo.cs.miningmart.m4.core.Projection) projection);
	}

	/**
	 * Sets the From-Projection as the only From-Projection of this
	 * <code>Concept</code>
	 * @param newFromProjection The From-Projection to set
	 */
	public void setTheFromProjection(edu.udo.cs.miningmart.m4.Concept newFromProjection)
		throws M4Exception
	{	
		// Problem: We have a collection of fromProjections now.
		// However, if anybody calls this method this indicates
		// that this person expects a single reference!
		
		// remove all old from-projections:
		con2toPro.setCollectionTo(this, new Vector());

		if (newFromProjection != null) {
			// Create a new Projection object:
			Projection projection =
				(Projection) this.getM4Db().createNewInstance(edu.udo.cs.miningmart.m4.core.Projection.class);
				
			projection.setFromConcept(newFromProjection);
			projection.setToConcept(this);
			// The (back-)reference from this concept to the projection
			// is set automatically!
		}
	}

	/**
	 * @return the (first) <code>Concept</code> which this <code>Concept</code>
	 * is a <code>Projection</code> from.
	 */
	public edu.udo.cs.miningmart.m4.Concept getTheFromProjection() throws M4Exception {
		Collection col = this.getProjectionsAsToConcept();
		if (col == null || col.isEmpty()) {
			return null;
		}
		else {
			return ((Projection) col.iterator().next()).getFromConcept();	
		}
	}

	/**
	 * Returns the From-Projections.
	 * 
	 * @return <code>Collection</code> of <code>Projection</code> objects
	 */
	public Collection getProjectionsAsFromConcept() throws M4Exception
	{	
		if ( ! this.allfromProjectionsLoaded && ( ! this.isNew())) {
			this.allfromProjectionsLoaded = true;
			this.readFromProjectionsFromDB();
		}
		return this.myFromProjections;
	}

	/**
	 * Returns the To-Projections.
	 * 
	 * @return <code>Collection</code> of <code>Projection</code> objects
	 */
	public Collection getProjectionsAsToConcept() throws M4Exception
	{
		if ( ! this.allToProjectionsLoaded && ( ! this.isNew())) {
		   this.allToProjectionsLoaded = true;
		   this.readToProjectionsFromDB();
		}
		return this.myToProjections;  
	}

	// ***** Reading objects from the database *****
		
	private void readSubConceptsFromDB() throws M4Exception	{
		// read concepts where this concept is super concept:
		Collection conInhs = this.getObjectsReferencingMe(ConceptInheritance.class, ConceptInheritance.ATTRIB_SUPER_CONCEPT);
		Iterator it = conInhs.iterator();
		while (it.hasNext()) {
			this.addSubConcept((ConceptInheritance) it.next());
		}
	}
	
	private void readSuperConceptsFromDB() throws M4Exception {
		// read concepts where this concept is subconcept:
		Collection conInhs = this.getObjectsReferencingMe(ConceptInheritance.class, ConceptInheritance.ATTRIB_SUB_CONCEPT);
		Iterator it = conInhs.iterator();
		while (it.hasNext()) {
			this.addSuperConcept((ConceptInheritance) it.next());
		}
	}
	
	private void readFromProjectionsFromDB() throws M4Exception	{
		Iterator it = this.getObjectsReferencingMe(Projection.class, Projection.ATTRIB_FROM_CONCEPT).iterator();
		while (it.hasNext()) {
			this.addFromProjection((edu.udo.cs.miningmart.m4.Projection) it.next());
		}
	}

	private void readToProjectionsFromDB() throws M4Exception {
		Iterator it = this.getObjectsReferencingMe(Projection.class, Projection.ATTRIB_TO_CONCEPT).iterator();
		while (it.hasNext()) {
			this.addToProjection((edu.udo.cs.miningmart.m4.Projection) it.next());
		}
	}
	
	/**
	 * Database reader for the <code>Relation</code>s of this <code>Concept</code>
	 * 
	 * @param thisConceptIsFromConcept indicates whether to load all Relationships
	 *        where this concept is the FromConcept (<code>true</code>), or the
	 *        the ToConcept (<code>false</code>)
	 */
	private void readRelationsFromDB(boolean thisConceptIsFromConcept) throws M4Exception
	{
    	String fkAttribute = (thisConceptIsFromConcept ?
    			edu.udo.cs.miningmart.m4.core.Relation.ATTRIB_FROM_CONCEPT : 
    			edu.udo.cs.miningmart.m4.core.Relation.ATTRIB_TO_CONCEPT);
    	
    	Collection relations = this.getObjectsReferencingMe(Relation.class, fkAttribute);
		Iterator it = relations.iterator();
		
		if (thisConceptIsFromConcept == true) {
			while (it.hasNext())
				this.addFromRelationship((edu.udo.cs.miningmart.m4.Relation) it.next());
		}
		else {
			while (it.hasNext())
				this.addToRelationship((edu.udo.cs.miningmart.m4.Relation) it.next());
		}
	}
	
	/** @see M4Data#removeAllM4References() */
	protected void removeAllM4References() throws M4Exception {
		super.removeAllM4References();
		this.setTheCase(null);
		Vector empty = new Vector(0);
		this.setFeatures(empty);
		this.setColumnSets(empty);
		con2FromRel.setCollectionTo(this, empty);
		con2ToRel.setCollectionTo(this, empty);
		con2fromPro.setCollectionTo(this, empty);
		con2toPro.setCollectionTo(this, empty);
		conInheritSub.setCollectionTo(this, empty);
		conInheritSuper.setCollectionTo(this, empty);
		this.removeDocObject();
	}

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

	/** @see GraphicalM4Object#getGraphicallyEbeddedObjects() */
	protected Collection getGraphicallyEmbeddedObjects() throws M4Exception {
		Vector ret = new Vector();
		// Which of the following objects can refer to this one
		// as the context? Currently it seems to be a superset.
		ret.addAll(this.getSuperConcepts());
		ret.addAll(this.getSubConcepts());
		ret.addAll(this.getTheFromRelationships());
		ret.addAll(this.getTheToRelationships());
		return ret;
	}

	/** @see M4Data#getDependentObjects */
	public Collection getDependentObjects() throws M4Exception {
		Collection ret = super.getDependentObjects();
		ret.addAll(this.getColumnSets());
		ret.addAll(this.getFeatures());
		ret.addAll(this.getProjectionsAsFromConcept());
		ret.addAll(this.getProjectionsAsToConcept());
		ret.addAll(this.getConceptInheritanceAsSubConcept());
		ret.addAll(this.getConceptInheritanceAsSuperConcept());
		ret.addAll(this.getTheFromRelationships());
		ret.addAll(this.getTheToRelationships());
		return ret;
	}


	/**
	 * This method returns information about the relational validity of the object. 
	 * A concept is valid if it has at least one BA, and
	 * - for type DB: it has a Columnset
	 * - for type MINING: it is an output concept somewhere
	 */
	public boolean isRelationallyValid() throws M4Exception	{
		Collection bas = this.getAllBaseAttributes();
		if (bas == null || bas.isEmpty())
		{   return false;   }
		
		if (this.getType().equals(Concept.TYPE_DB))
		{
			Collection cs = this.getColumnSets();
			if (cs == null || cs.isEmpty())
			{  return false;  }
			return true;
		}
		else
		{
			Iterator it = null;
			try {
				it = this.getParameterReferences().iterator();
			}
			catch (M4Exception m4e)
			{   super.doPrint(m4e);
				return false;
			}
			while (it.hasNext())
			{   if (((edu.udo.cs.miningmart.m4.Parameter) it.next()).getParameterType() == edu.udo.cs.miningmart.m4.Parameter.TYPE_OUTPUT)
				{   return true;   }
			}
			return false;
		}
	}	

	/**
	 * Make a copy of this Concept that is attached to the given Case.
	 */
	public edu.udo.cs.miningmart.m4.Concept copy(edu.udo.cs.miningmart.m4.Case newCase) throws M4Exception 
	{		
		//copy the concept
		edu.udo.cs.miningmart.m4.core.Concept theCopy = new edu.udo.cs.miningmart.m4.core.Concept(this.getM4Db());
		theCopy.setTheCase(newCase);
		
		// makes no sense to copy columnsets, projections or superconcpts 
		// to a different case!		
		
		// create copies of the features:
		Iterator featIt = this.getFeatures().iterator();
		while (featIt.hasNext()) {
			Feature myF = (Feature) featIt.next();
			myF.copy(theCopy);
		}
		String nameOfCopy = newCase.getValidName(this.getName(), Concept.class);
		theCopy.setName(nameOfCopy);
		theCopy.setType(this.getType());
			
		return theCopy;	
	}

	/**
	 * Creates a BaseAttribute that is connected to this Concept. The name of the BaseAttribute must be unique within the Concept.
	 * The attributeType should also be provided or will be set to BASE if omitted. Use
	 * the predefined constants to set the datatype.
	 * 
	 * @throws M4Exception when an error occurs during creation of the object
	 */
	public edu.udo.cs.miningmart.m4.BaseAttribute createBaseAttribute(String name, String datatype, String attributeType)
		throws M4Exception
	{
		if (name == null || datatype == null)
		{   throw new M4Exception("ConceptImpl.createBaseAttribute: got <null> for name or datatype of new BA!");  }	
		
		if (attributeType == null)  
		{   attributeType = edu.udo.cs.miningmart.m4.BaseAttribute.TYPE_BASE;  }
		
		edu.udo.cs.miningmart.m4.core.BaseAttribute newBA = 
			new edu.udo.cs.miningmart.m4.core.BaseAttribute(this.getM4Db());
	
		newBA.setDBAttribS(attributeType);
		newBA.setConceptualDataTypeName(datatype);
		
		newBA.setName(name);		
		newBA.setConcept(this);
		
		// Since a feature has been added, propagate the changes:
		// this.propagateChangesToDependingConcepts();
		
		return newBA;
	}	

	/**
	 * Creates a new ColumnSet that is connected to this Concept. The name
	 * must be unique within this Concept. The schema and type are also required.
	 * Use the predefined types for the type parameter.
	 *
	 * @throws M4Exception when an error occurs during creation of the object.
	 */
	public edu.udo.cs.miningmart.m4.Columnset createColumnset(String name, String schema, String type)
		throws M4Exception
	{
		if (name == null || schema == null || type == null)
		{   throw new M4Exception("Concept.createColumnSet: got <null> as a parameter!");  }
			
		edu.udo.cs.miningmart.m4.core.Columnset newCS = new edu.udo.cs.miningmart.m4.core.Columnset(this.getM4Db());
		
		newCS.setType(type);
		newCS.setTheConcept(this);
		newCS.setName(name);
		newCS.setSchema(schema);
				
		return newCS;
	}	
	
	public edu.udo.cs.miningmart.m4.PrimaryKey createPrimaryKey(Collection keyAttribs) throws M4Exception {
		Columnset current = (Columnset) this.getCurrentColumnSet();
		if (current == null) {
			throw new M4Exception("Concept '" + this.getName() + "': cannot create a PrimaryKey because no Columnset is available!");
		}
		PrimaryKey thePrimKey = null;
		if ( ! keyAttribs.isEmpty()) {
			thePrimKey = (PrimaryKey) current.createPrimaryKey(current.getName() + "_PK");
			Iterator it = keyAttribs.iterator();
			while (it.hasNext()) {
				BaseAttribute myBa = (BaseAttribute) it.next();
				Column col = (Column) myBa.getCurrentColumn();
				if (col != null) {
					thePrimKey.addColumn(col);
				}
			}
		}		
		return thePrimKey;
	}
	
	public edu.udo.cs.miningmart.m4.PrimaryKey getPrimaryKey() throws M4Exception {
		Columnset current = (Columnset) this.getCurrentColumnSet();
		if (current != null) {
			return current.getPrimaryKey();
		}
		else {
			return null;
		}
	}
	
	/**
	 * @see edu.udo.cs.miningmart.m4.Concept#connect(String, boolean, Map)
	 */
	public void connect(String nameOfDbObject, boolean isTable, Map columnNamesForAttribs) throws M4Exception {
		// first check if this is a connectable Concept:
		if ( ! this.getType().equals(Concept.TYPE_DB)) {
			return;
		}
		
		// second, if a Columnset is connected at the moment, remove it:
		this.removeConnection();
		
		// third, create a new Columnset and Columns:
		String csType = (isTable ? Columnset.TYPE_TABLE : Columnset.TYPE_VIEW);
		Columnset newCS = (Columnset) this.createColumnset(nameOfDbObject, this.getM4Db().getBusinessSchemaName(), csType);
		newCS.createColumnsFromDbObject(nameOfDbObject);
		
		// fourth, map this Concept's features to the columns:
		Iterator attribIt = this.getAllBaseAttributes().iterator();
		while (attribIt.hasNext()) {
			BaseAttribute myBa = (BaseAttribute) attribIt.next();
			String columnNameForBa = (String) columnNamesForAttribs.get(myBa);
			if (columnNameForBa != null) {
				Column columnForBa = (Column) newCS.getColumn(columnNameForBa);
				Vector v = new Vector();
				v.add(columnForBa);
				myBa.setColumns(v);
			}
		}
	}
	
	/**
	 * @see edu.udo.cs.miningmart.m4.Concept#removeConnection()
	 */
	public void removeConnection() throws M4Exception {
		// first check if this is a connectable Concept:
		if ( ! this.getType().equals(Concept.TYPE_DB)) {
			return;
		}
		Columnset current = (Columnset) this.getCurrentColumnSet();
		// check wether this Columnset exists
		if (current==null){
			return;
		}
		this.removeColumnset(current);
		
		// just to be safe, see if there are further columnsets:
		if ( ! this.getColumnSets().isEmpty()) {
			throw new M4Exception("Concept '" + this.getName() + 
					"': found more than one columnset, although it is a DB type concept!");
		}		
		
		// since the Columnset was connected to a Concept of type DB,
		// it cannot be used elsewhere except in a Relation:
		if (current.getRelation() == null) {
			current.deleteSoon();
		}
	}
	
	/**
	 * Creates a new Relationship to which this Concept is connected as FromConcept.
	 * The name of the Relationship in combination with the name of the ToConcept must be unique for the Concept.
	 * This allows a Concept to have Relationships to other Concepts with the same name.
	 * NOTE:
	 * Only the conceptual level is created, but no keys!
	 * A new Relationship should always be created as a FromConceptRelationship;
	 * no method is provided to create a ToConceptRelationship.
	 * @param name the name for the relationship
	 * @param toConcept the Concept the relationship should point to
	 * @throws M4Exception when an error occurs during creation of the object.
	 */
	public edu.udo.cs.miningmart.m4.Relation createFromConceptRelation(String name, edu.udo.cs.miningmart.m4.Concept toConcept)
		throws M4Exception
	{
		if (name == null)
		{  throw new M4Exception("Concept.createFromConceptRelationship: given name was <null>!");  }
		
		Collection c = this.getTheFromRelationships();
		
		if (c != null)
		{
			Iterator it = c.iterator();
			while (it.hasNext())
			{
				// check names of Relationship and toConcept:
				Relation rel = (Relation) it.next();
				Concept aToConcept = (Concept) rel.getTheToConcept();
				if (rel.getName().equals(name) && aToConcept != null && toConcept != null && 
				    aToConcept.getName().equals(toConcept.getName()))
				{  throw new M4Exception("Could not create new Relationship in Concept " + 
					                             this.getId() + ": Name '" + name + "' is not unique for ToConcept!");
				}
			}
		}
		edu.udo.cs.miningmart.m4.core.Relation newRel = new edu.udo.cs.miningmart.m4.core.Relation(this.getM4Db());
		
		newRel.setTheFromConcept(this);
		newRel.setTheToConcept(toConcept);
		newRel.setName(name);
		// super.addFromRelationship(newRel); // is done by setTheFromConcept
		
		return newRel;
	}

	/**
	 * Creates a new MultiColumnFeature that is connected to this Concept.
	 * The name must be unique within this Concept.
	 * @throws M4Exception when an error occurs during creation of the object.
	 */
	public edu.udo.cs.miningmart.m4.MultiColumnFeature createMultiColumnFeature(String name)
		throws M4Exception
	{
		if (name == null)
		{   throw new M4Exception("ConceptImpl.createMCF: got <null> for the name!");  }
		
		edu.udo.cs.miningmart.m4.core.MultiColumnFeature newMCF = 
			new edu.udo.cs.miningmart.m4.core.MultiColumnFeature(this.getM4Db());
		
		newMCF.setConcept(this); // checks for existing names
		newMCF.setName(name);

		// Since a feature has been added, propagate the changes:
		// this.propagateChangesToDependingConcepts();
		
		return newMCF;	
	}

	public edu.udo.cs.miningmart.m4.BaseAttribute getBaseAttribute(String name) 
	       throws M4Exception {
		if (name != null) {
			Iterator it = this.getAllBaseAttributes().iterator();
			while (it.hasNext()) {
				edu.udo.cs.miningmart.m4.BaseAttribute b = (edu.udo.cs.miningmart.m4.BaseAttribute) it.next();
				if (b != null && name.equals(b.getName())) {
					return b;
				}
			}
		}
		return null;
	}
	
	public edu.udo.cs.miningmart.m4.BaseAttribute getBaseAttribute(edu.udo.cs.miningmart.m4.Column theColumn)
	throws M4Exception {
		if (theColumn != null) {
			Iterator it = this.getAllBaseAttributes().iterator();
			while (it.hasNext()) {
				edu.udo.cs.miningmart.m4.BaseAttribute b = (edu.udo.cs.miningmart.m4.BaseAttribute) it.next();
				if (b != null && b.hasColumn(theColumn)) {
					return b;
				}
			}
		}
		return null;
	}
	
	public Collection getAllBaseAttributes() throws M4Exception {
		Collection fea = this.getFeatures();
		Vector bas = new Vector();
		Iterator it = fea.iterator();
		while (it.hasNext()) {
			edu.udo.cs.miningmart.m4.Feature f = (edu.udo.cs.miningmart.m4.Feature) it.next();
			if (f instanceof BaseAttribute) {
				if ( ! bas.contains(f)) {
					bas.add(f);	
				}
			}
			if (f instanceof MultiColumnFeature) {
				Iterator baIt = ((MultiColumnFeature) f).getBaseAttributes().iterator();
				while (baIt.hasNext()) {
					BaseAttribute ba = (BaseAttribute) baIt.next();					
					if ( ! bas.contains(ba)) {
						bas.add(ba);					
					}
				}
			}
		}
		return bas;
	}
	
	public Collection getBaseAttribsNotInMcf() throws M4Exception {
		Collection fea = this.getFeatures();
		Vector bas = new Vector();
		Iterator it = fea.iterator();
		while (it.hasNext()) {
			edu.udo.cs.miningmart.m4.Feature f = (edu.udo.cs.miningmart.m4.Feature) it.next();
			if (f instanceof BaseAttribute) {
				bas.add(f);	
			}
		}
		return bas;		
	}
	
	/**
	 * Removes the specified BaseAttribute from this Concept.
	 * The BaseAttribute is also removed from the M4 Schema if this
	 * Concept is the last Concept it belongs to.
	 */
	public void removeBaseAttribute(String name) throws M4Exception {
		edu.udo.cs.miningmart.m4.BaseAttribute ba = this.getBaseAttribute(name);
		if (ba != null) {
			this.removeFeature(ba);			
		}
	}
	
	/**
	 * Removes all BaseAttributes from this Concept. The BaseAttributes will be deleted from the M4 Schema if this is the last Concept they are part of.
	 */
	public void removeAllBaseAttributes() throws M4Exception {
		Collection c = this.getAllBaseAttributes();
		Iterator it = (new Vector(c)).iterator();
		while (it.hasNext()) {
			edu.udo.cs.miningmart.m4.core.Feature ba = (edu.udo.cs.miningmart.m4.core.Feature) it.next();
			this.removeBaseAttribute(ba.getName());
		}
	}	

	public Collection getAllMultiColumnFeatures() throws M4Exception {
		Collection fea = this.getFeatures();
		Vector mcfs = new Vector();
		Iterator it = fea.iterator();
		while (it.hasNext()) {
			edu.udo.cs.miningmart.m4.Feature f = (edu.udo.cs.miningmart.m4.Feature) it.next();
			if (f instanceof MultiColumnFeature) {
				mcfs.add(f);	
			}
		}
		return mcfs;
	}
	
	/**
	 * Removes the specified MultiColumnFeature from this Concept.
	 * The MultiColumnFeature is also removed from the M4 Schema.
	 */
	public void removeMultiColumnFeature(String name) throws M4Exception {
		edu.udo.cs.miningmart.m4.MultiColumnFeature mcf = this.getMultiColumnFeature(name);
		if (mcf != null) {
			this.removeFeature(mcf);			
		}
	}
		
	/**
	 * Removes all MultiColumnFeatures from this Concept. The MultiColumnFeatures will be deleted from the M4 Schema.
	 */
	public void removeAllMultiColumnFeatures() throws M4Exception {
		Collection c = this.getAllMultiColumnFeatures();
		Iterator it = (new Vector(c)).iterator();
		while (it.hasNext()) {
			edu.udo.cs.miningmart.m4.core.Feature mcf = (edu.udo.cs.miningmart.m4.core.Feature) it.next();
			this.removeBaseAttribute(mcf.getName());
		}
	}

	public edu.udo.cs.miningmart.m4.MultiColumnFeature getMultiColumnFeature(String name) throws M4Exception {
		Collection mcfCol = this.getAllMultiColumnFeatures();
		Iterator it = mcfCol.iterator();
		while (it.hasNext()) {
			edu.udo.cs.miningmart.m4.MultiColumnFeature mcf = (edu.udo.cs.miningmart.m4.MultiColumnFeature) it.next();
			if (mcf != null && mcf.getName().equals(name))
				return mcf;
		}	
		return null;
	}	

	/**
	 * Removes all ColumnSets of this Concept. The objects are removed from
	 * the M4 Schema.
	 */
	public void removeAllColumnsets() throws M4Exception {
		this.setColumnSets(new Vector());
	}
	
	/**
	 * @return a ColumnSet specified by the given name or null if not found.
	 */
	public edu.udo.cs.miningmart.m4.Columnset getColumnset(String name) 
	       throws M4Exception {
		Collection csCol = this.getColumnSets();
		Iterator it = csCol.iterator();
		while (it.hasNext()) {
			edu.udo.cs.miningmart.m4.Columnset cs = (edu.udo.cs.miningmart.m4.Columnset) it.next();
			if (cs != null && cs.getName().equals(name))
				return cs;
		}		
		return null;
	}
	
	/**
	 * @return a ColumnSet specified by the given multiStepBranch or null if not found.
	 */
	public edu.udo.cs.miningmart.m4.Columnset getColumnSetByBranch(String multiStepBranch) 
	       throws M4Exception {
		Collection csCol = this.getColumnSets();
		Iterator it = csCol.iterator();
		while (it.hasNext()) {
			edu.udo.cs.miningmart.m4.Columnset cs = (edu.udo.cs.miningmart.m4.Columnset) it.next();
			if (cs != null && cs.getMultiStepBranch().equals(multiStepBranch))
				return cs;
		}		
		return null;		
	}
	
	public edu.udo.cs.miningmart.m4.EstimatedStatistics getEstimatedStatistics() 
	throws M4Exception {
		// if the estimations exist they are simply returned:
		if (this.myEstimations != null) {
			return this.myEstimations;
		}
		
		// otherwise we have to compute them:
		if (this.getType().equals(TYPE_DB)) {
			if (this.getCurrentColumnSet() != null) {
				this.myEstimations = new EstimatedStatistics(this);
			}
			else {
				throw new M4Exception("Concept '" + this.getName() + "': cannot estimate statistics because no connection to a database object is given!");				
			}
		}
		else {
			Step creatingStep = (Step) this.getStepWhereThisIsOutputConcept();
			this.myEstimations = (EstimatedStatistics) creatingStep.estimateStatisticsForOutputConcept();
		}
		return this.myEstimations;
	}
	
}
/*
 * Historie
 * --------
 * 
 * $Log: Concept.java,v $
 * Revision 1.10  2006/04/11 14:10:14  euler
 * Updated license text.
 *
 * Revision 1.9  2006/04/06 16:31:14  euler
 * Prepended license remark.
 *
 * Revision 1.8  2006/04/03 09:32:53  euler
 * Improved Export/Import of Projections
 * and Subconcept links.
 *
 * Revision 1.7  2006/03/20 16:44:21  euler
 * Bugfixes
 *
 * Revision 1.6  2006/03/16 14:53:38  euler
 * *** empty log message ***
 *
 * Revision 1.5  2006/02/01 16:09:03  euler
 * Bugfixes
 *
 * Revision 1.4  2006/01/18 16:58:58  euler
 * Added some basic estimations of statistics.
 * Will need improvements.
 *
 * Revision 1.3  2006/01/12 08:46:10  euler
 * Added method getBaseAttribute(Column)
 *
 * Revision 1.2  2006/01/06 16:25:04  euler
 * Updates and bugfixes in the delete-Mechanism for M4Data objects.
 *
 * Revision 1.1  2006/01/03 09:54:18  hakenjos
 * Initial version!
 *
 */
