/*
 * 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 edu.udo.cs.miningmart.db.DB;
import edu.udo.cs.miningmart.exception.M4Exception;
import edu.udo.cs.miningmart.m4.ForeignKeyLink;

/**
 * This class implements the foreign key functionality.
 * 
 * @author Timm Euler
 * @version $Id: ForeignKey.java,v 1.4 2006/04/11 14:10:13 euler Exp $
 */
public class ForeignKey extends Key implements edu.udo.cs.miningmart.m4.ForeignKey {
	 
    private Columnset primaryKeyCS, foreignKeyCS; 
    private Relation myRelation; 
    private boolean myRelationLoaded = false;
    
	/**
	 * Constructor for ForeignKeyImpl.
	 * 
	 * @param m4Db The DB object
	 */
	public ForeignKey(DB m4Db)
	{	super(m4Db);	}
	
	/**
	 * @see edu.udo.cs.miningmart.m4.core.M4Data#getObjectsInNamespace(Class)
	 */
	protected Collection getObjectsInNamespace(Class typeOfObjects) throws M4Exception {
		return null;
	}

	/**
	 * @see edu.udo.cs.miningmart.m4.Key#getColumn(KeyMember)
	 */
	protected Column getColumn(KeyMember km)
	{   return (Column) km.getForeignKeyColumn();	}

	/*
	 * @see edu.udo.cs.miningmart.m4.ForeignKey#setRelationship(Relationship)
	 */
	private void setRelation(edu.udo.cs.miningmart.m4.Relation relationship) throws M4Exception
	{   
		// whether we treat <this> as a fromKey or toKey does not matter:
		boolean thisIsAFromKey = true; // might as well be false
		
		this.setRelation(relationship, thisIsAFromKey); 
	}

	/**
	 * @see miningmart.m4.ForeignKey#addColumn(Column, Column)
	 */
	public void addColumnLink(edu.udo.cs.miningmart.m4.Column fkColumn, edu.udo.cs.miningmart.m4.Column pkColumn)	throws M4Exception {
		try {
			KeyMember km = (KeyMember) this.getM4Db().createNewInstance(edu.udo.cs.miningmart.m4.core.KeyMember.class);
			km.setForeignKeyColumn(null);
			// km.setTheKey(this);
			km.setPrimaryKeyColumn(pkColumn);
			km.setForeignKeyColumn(fkColumn);
			// what about the new member's m4 id?
			// must be dealt with in this.store()?
			this.addMember(km);
		}
		catch (M4Exception m4e)
		{   throw new M4Exception("ForeignKey.addColumnLink: Error adding Columns " +
			                      ((Column) fkColumn).getId() + " (foreign key column) and " +
			                      ((Column) pkColumn).getId() + " (primary key column) to Foreign Key " +
			                      this.getId() + ": " + m4e.getMessage());
		}
	}
	
	public boolean isColumnContainedInForeignColumns(edu.udo.cs.miningmart.m4.Column theColumn)
	throws M4Exception {
		Iterator linkIt = this.getAllColumnLinks().iterator();
		while (linkIt.hasNext()) {
			ForeignKeyLink myColumnLink = (ForeignKeyLink) linkIt.next();
			if (myColumnLink.getForeignKeyColumn().equals(theColumn)) {
				return true;
			}
		}
		return false;
	}

	public boolean isColumnContainedInPrimaryColumns(edu.udo.cs.miningmart.m4.Column theColumn)
	throws M4Exception {
		Iterator linkIt = this.getAllColumnLinks().iterator();
		while (linkIt.hasNext()) {
			ForeignKeyLink myColumnLink = (ForeignKeyLink) linkIt.next();
			if (myColumnLink.getPrimaryKeyColumn().equals(theColumn)) {
				return true;
			}
		}
		return false;
	}
	
	public edu.udo.cs.miningmart.m4.Column getPrimaryForForeignColumn(edu.udo.cs.miningmart.m4.Column theForeignKeyColumn)
	throws M4Exception {
		Iterator linkIt = this.getAllColumnLinks().iterator();
		while (linkIt.hasNext()) {
			ForeignKeyLink myColumnLink = (ForeignKeyLink) linkIt.next();
			if (myColumnLink.getForeignKeyColumn().equals(theForeignKeyColumn)) {
				return myColumnLink.getPrimaryKeyColumn();
			}
		}
		return null;
	}

	public edu.udo.cs.miningmart.m4.Column getForeignForPrimaryColumn(edu.udo.cs.miningmart.m4.Column thePrimaryKeyColumn)
	throws M4Exception {
		Iterator linkIt = this.getAllColumnLinks().iterator();
		while (linkIt.hasNext()) {
			ForeignKeyLink myColumnLink = (ForeignKeyLink) linkIt.next();
			if (myColumnLink.getPrimaryKeyColumn().equals(thePrimaryKeyColumn)) {
				return myColumnLink.getForeignKeyColumn();
			}
		}
		return null;
	}
	
	/*
	 * @see miningmart.m4.ForeignKey#getIsConnectionTo()
	 *
	public edu.udo.cs.miningmart.m4.Columnset getIsConnectionTo()
	{   return (Columnset) this.getPrimaryKeyColumnSet();   }

	/*
	 * @see edu.udo.cs.miningmart.m4.ForeignKey#setIsConnectionTo(ColumnSet)
	 *
	public void setIsConnectionTo(edu.udo.cs.miningmart.m4.Columnset columnSet) 
	       throws M4Exception {
		this.setPrimaryKeyColumnSet((Columnset) columnSet);
	}

	/*
	 * @see edu.udo.cs.miningmart.m4.ForeignKey#removeIsConnectionTo()
	 *
	public void removeIsConnectionTo() throws M4Exception {   
		this.setPrimaryKeyColumnSet(null);
	}
	*/

	/**
	 * @see edu.udo.cs.miningmart.m4.ForeignKey#copy(ColumnSet)
	 */
	public edu.udo.cs.miningmart.m4.ForeignKey copy(edu.udo.cs.miningmart.m4.Columnset newColumnSet)
		throws M4Exception
	{
		ForeignKey theCopy = new ForeignKey(this.getM4Db());
		try {
			// theCopy.setColumnset(newColumnSet);
			theCopy.setForeignKeyColumnset(this.getForeignKeyColumnset());
			theCopy.setPrimaryKeyColumnset(this.getPrimaryKeyColumnset());
			Case myContainer = (Case) this.getForeignKeyColumnset().getTheConcept().getTheCase();			
			String nameOfCopy = myContainer.getValidName(this.getName(), ForeignKey.class);
			theCopy.setName(nameOfCopy);
			theCopy.setMembers(this.getMembers());
			// theCopy.setIsConnectionTo(this.getIsConnectionTo());
			theCopy.setRelation(this.getRelation());
		}
		catch (M4Exception se) {   
			throw new M4Exception("Could not copy ForeignKey " + this.getId() + 
			                      " to Columnset '" + newColumnSet.getName() + "': " + se.getMessage());
		}
		return theCopy;
	}	
	
    /**
     * Getter method.
     * 
     * @return The columnset of the primary key of this Key
     */
    public edu.udo.cs.miningmart.m4.Columnset getPrimaryKeyColumnset() throws M4Exception
    {   return this.primaryKeyCS; }

	/**
	 * Setter method.
	 * 
	 * @param cs The new columnset of the primary key of this Key
	 */
    public void setPrimaryKeyColumnset(edu.udo.cs.miningmart.m4.Columnset cs) throws M4Exception { 
    	Columnset.csAsPk2FKey.updateReferenceTo(this, cs);
    }
    
    public void removePrimaryKeyColumnset() throws M4Exception {
    	Columnset.csAsPk2FKey.remove(this.primaryKeyCS, this);
    }

	/** Primitive setter, do not use it! */
	public void primitiveSetPrimaryKeyColumnset(edu.udo.cs.miningmart.m4.Columnset cs) {
    	this.setDirty();
    	this.primaryKeyCS = (Columnset) cs;		
	}

    /**
     * Getter method.
     * 
     * @return The columnset of the foreign key of this Key
     */
    public edu.udo.cs.miningmart.m4.Columnset getForeignKeyColumnset() throws M4Exception    
	{   return this.foreignKeyCS; }

	/**
	 * Setter method.
	 * 
	 * @param cs The new columnset of the foreign key of this Key
	 */
    public void setForeignKeyColumnset(edu.udo.cs.miningmart.m4.Columnset cs) throws M4Exception {   
    	Columnset.csAsFk2Fkey.updateReferenceTo(this, cs);
    }

    public void removeForeignKeyColumnset() throws M4Exception {
    	Columnset.csAsFk2Fkey.remove(this.foreignKeyCS, this);
    }
    
	/** Primitive setter, do not use it! */
	public void primitiveSetForeignKeyColumnset(edu.udo.cs.miningmart.m4.Columnset cs) {
    	this.setDirty();
    	this.foreignKeyCS = (Columnset) cs;		
	}
	
	public void removeColumn(String name) throws M4Exception {
		this.removeColumnLink(name);
	}

	public void removeColumnLink(String name) throws M4Exception {
		this.removeMemberByForeignColumnName(name);
	}

	public void removeAllColumnLinks() throws M4Exception {
		this.removeAllColumns();
	}
	
    /**
     * Getter method.
     * 
     * @return The Relation this Key is associated with, if any.
     */
    public edu.udo.cs.miningmart.m4.Relation getRelation() throws M4Exception {
    	if ( ! this.myRelationLoaded && ( ! this.isNew())) {
			this.myRelationLoaded = true;
			// first see if this foreignkey is fromKey:
			Collection rels = this.getObjectsReferencingMe(Relation.class, Relation.ATTRIB_FROM_KEY);
			Relation rel = null;
			if (rels != null && ( ! rels.isEmpty())) {
				rel	= (Relation) rels.iterator().next();
			}
			if (rel != null) {
				Relation.rel2fromKey.setReciprocalReferences(this, rel);
			}
			else {
				// second try the toKey:
				rels = this.getObjectsReferencingMe(Relation.class, Relation.ATTRIB_TO_KEY);
				if (rels != null && ( ! rels.isEmpty())) {
					rel	= (Relation) rels.iterator().next();
				}
				if (rel != null) {
					Relation.rel2toKey.setReciprocalReferences(this, rel);
				}				
			}
    	}
    	return this.myRelation;
    }

	/**
	 * Setter method.
	 * 
	 * @param rel The Relation this Key is to be associated with
	 * @param isFrom TRUE iff this key is the fromKey of that Relation,
	 *        FALSE if it is the toKey.
	 */
    public void setRelation(edu.udo.cs.miningmart.m4.Relation rel, boolean isFrom) throws M4Exception {   
    	if (isFrom)
    	{   Relation.rel2fromKey.setReciprocalReferences((edu.udo.cs.miningmart.m4.core.Relation) rel, this);  }
    	else
    	{   Relation.rel2toKey.setReciprocalReferences((edu.udo.cs.miningmart.m4.core.Relation) rel, this);   }
    }
    
    public Collection getAllColumnLinks() throws M4Exception {
    	return this.getMembers();    
    }	
    
    public edu.udo.cs.miningmart.m4.ForeignKeyLink getFirstColumnLink() throws M4Exception {
    	Iterator it = this.getMembers().iterator();
    	if (it.hasNext()) {
    		return (KeyMember) it.next();
    	}
    	else return null;
    }
    
    /**
     * Primitive setter. Do not use!
     */
    public void setRelationPrimitive(Relation r)
    {   this.myRelation = r;  }    

	/** @see M4Data#getDependentObjects */
	public Collection getDependentObjects() throws M4Exception {
		Collection ret = super.getDependentObjects();
		Columnset cs = (Columnset) this.getPrimaryKeyColumnset();
		if (cs != null)
			ret.add(cs);
		cs = (Columnset) this.getForeignKeyColumnset();
		if (cs != null)
			ret.add(cs);
		
		return ret;
	}
}
/*
 * Historie
 * --------
 * 
 * $Log: ForeignKey.java,v $
 * Revision 1.4  2006/04/11 14:10:13  euler
 * Updated license text.
 *
 * Revision 1.3  2006/04/06 16:31:13  euler
 * Prepended license remark.
 *
 * Revision 1.2  2006/01/05 14:11:22  euler
 * Bugfixes
 *
 * Revision 1.1  2006/01/03 09:54:16  hakenjos
 * Initial version!
 *
 */
