/*
 * 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.sql.ResultSet;
import java.sql.SQLException;
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.InterM4Communicator;
import edu.udo.cs.miningmart.m4.utils.InterM4KeyKeymember;
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;
import edu.udo.cs.miningmart.m4.Columnset;

/**
 * This class represents an M4 Key. Its concrete subclasses are
 * ForeignKey and PrimaryKey.
 * 
 * @author Timm Euler
 * @version $Id: Key.java,v 1.4 2006/04/11 14:10:14 euler Exp $
 */
public abstract class Key extends M4Data implements XmlInfo, edu.udo.cs.miningmart.m4.Key {
	
	private boolean allMembersLoaded = false;
	
	// Database constants for the keyhead table
	public static final String M4_TABLE_NAME 	  = "keyhead_t";
	public static final String ATTRIB_HEAD_ID     = "kh_id";     // NOT NULL NUMBER
	public static final String ATTRIB_HEAD_NAME   = "kh_name";   // NOT NULL VARCHAR2(100)
	public static final String ATTRIB_HEAD_PK_CS  = "kh_pkcsid"; // NUMBER
	public static final String ATTRIB_HEAD_FK_CS  = "kh_fkcsid"; // NUMBER

	// Inter M4 communication class
	static final InterM4Communicator key2member  = new InterM4KeyKeymember();

	// Instance variables  
    private Vector theKeyMembers = new Vector();

	/** 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_HEAD_ID;
	}

	/** @see M4Table.getM4Info() */
	public M4Info getM4Info() 
	{
		if (m4Info == null) 
		{
		    M4InfoEntry[] m4i = {
				new M4InfoEntry(ATTRIB_HEAD_ID,    "getId",   "setId",   long.class,   NOT_NULL),
				new M4InfoEntry(ATTRIB_HEAD_NAME,  "getName", "setName", String.class, NOT_NULL),
				new M4InfoEntry(ATTRIB_HEAD_PK_CS, "getPrimaryKeyColumnset", "primitiveSetPrimaryKeyColumnset", edu.udo.cs.miningmart.m4.Columnset.class),
				new M4InfoEntry(ATTRIB_HEAD_FK_CS, "getForeignKeyColumnset", "primitiveSetForeignKeyColumnset", edu.udo.cs.miningmart.m4.Columnset.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("PrimaryKeyColumnset", "getPrimaryKeyColumnset", "setPrimaryKeyColumnset", edu.udo.cs.miningmart.m4.Columnset.class),
				new M4InfoEntry("ForeignKeyColumnset", "getForeignKeyColumnset", "setForeignKeyColumnset", edu.udo.cs.miningmart.m4.Columnset.class),
				new M4InfoEntry("Docu",                "getDocumentation",       "setDocumentation",       String.class)
			};
			xmlInfo = new M4Info(m4i);
		}
		return xmlInfo;
	}

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

	/**
	 * Used to print information about this object.
	 * 
	 * @see edu.udo.cs.miningmart.m4.core.M4Object#print
	 */
    public void print() {
        this.doPrint(Print.M4_OBJECT, "Key " + myName + ": Id = " + myId + ";");
    }    

    // Subclasses must implement the following six methods to enable self-reflection based
    // loading and XML im-/exporting:
	public abstract void setPrimaryKeyColumnset(Columnset pkCs) throws M4Exception;	
	public abstract void setForeignKeyColumnset(Columnset fkCs) throws M4Exception;
	public abstract void primitiveSetPrimaryKeyColumnset(Columnset pkCs) throws M4Exception;	
	public abstract void primitiveSetForeignKeyColumnset(Columnset fkCs) throws M4Exception;	
	public abstract edu.udo.cs.miningmart.m4.Columnset getPrimaryKeyColumnset() throws M4Exception;	
	public abstract edu.udo.cs.miningmart.m4.Columnset getForeignKeyColumnset() throws M4Exception;
    
    
    /**
     * Add a new KeyMember to this Keyhead.
     * 
     * @see edu.udo.cs.miningmart.m4.core.KeyMember
     */
    protected void addMember(KeyMember newMember) throws M4Exception {
		key2member.checkNameExists(newMember, this);
		key2member.add(this, newMember);
    	newMember.setThePosition(this.theKeyMembers.size());
    }
    
    /**
     * Removes that KeyMember whose primary key Column has the given name
     * from this KeyHead (without deleting it).
     * 
     * @param name The name
     */
    protected void removeMemberByPrimaryColumnName(String name) throws M4Exception
    {
    	if (this.getMembers().isEmpty())
    	{   return;   }
    	Iterator it = this.getMembers().iterator();
    	KeyMember theMemberToBeRemoved = null;
    	
    	while (it.hasNext())
    	{   KeyMember km = (KeyMember) it.next();
    		if (km.getPrimaryKeyColumn().getName().equals(name))
    		{  
    			theMemberToBeRemoved = km;
    		}
    	}
    	key2member.remove(this, theMemberToBeRemoved);
    	this.setPositions();
    }
    
    /**
     * Removes that KeyMember whose foreign key Column has the given name
     * from this KeyHead (without deleting it).
     * 
     * @param name The name
     */
    protected void removeMemberByForeignColumnName(String name) throws M4Exception
    {
    	if (this.getMembers().isEmpty())
    	{   return;   }
    	Iterator it = this.getMembers().iterator();
    	KeyMember theMemberToBeRemoved = null;
    	
    	while (it.hasNext())
    	{   KeyMember km = (KeyMember) it.next();
    		if (km.getForeignKeyColumn().getName().equals(name))
    		{  
    			theMemberToBeRemoved = km;
    		}
    	}
    	key2member.remove(this, theMemberToBeRemoved);
    	this.setPositions();
    }
     
    /**
     * Get all Members of this Keyhead.
     * 
     * @return A <code>Collection</code> of <code>KeyMember</code>s.
     */
    public Collection getMembers() throws M4Exception
    {   
        if (!this.allMembersLoaded && ( ! this.isNew())) {
            this.allMembersLoaded = true;
            this.readKeyMembersFromDb();
        }
    	return this.theKeyMembers;   
    }  
    
    /**
     * Remove all KeyMembers from this KeyHead. Only
     * the association is removed, but the members are
     * not deleted.
     */
    protected void removeMembers()
    {   this.theKeyMembers = new Vector();   }
    
    /**
     * Associate this Key with the given Collection of
     * KeyMembers.
     * 
     * @param newMembers a <code>Collection</code> of <code>KeyMember</code>s
     */
    protected void setMembers(Collection newMembers) throws M4Exception
    {   
    	key2member.setCollectionTo(this, newMembers);   
    	this.setPositions(); 
    }  

	/**
	 * Overwrites the superclass method because the members
	 * of this key must be deleted, too.
	 * 
	 * @throws M4Exception
	 */
	public void deleteSoon() throws M4Exception {
		Collection kms = this.getMembers();
		if (kms != null && ( ! kms.isEmpty())) {
			Iterator it = (new Vector(kms)).iterator();
			while (it.hasNext()) {
				KeyMember km = (KeyMember) it.next();
				km.deleteSoon();
			}
		}
		super.deleteSoon();
	}
    
	/* reads the key head 
	private void readKeyHeadFromDb() throws M4Exception {
	    String query =
	           "SELECT " + ATTRIB_HEAD_NAME +", " + ATTRIB_HEAD_PK_CS + ", " + ATTRIB_HEAD_FK_CS +
	           " FROM " + M4_TABLE_NAME +
	           " WHERE " + ATTRIB_HEAD_ID +
	           " = " + this.getId();
	        
	    ResultSet rs = null;  
	    try {
		    rs = this.executeM4SqlRead(query);
		    // final DB db = this.getM4Db();
		    if (rs.next()) {
		        this.setName(rs.getString(ATTRIB_HEAD_NAME));
		    }
		    else {
		    	throw new M4Exception("M4 Database: Key not found (ID: " + this.getId()+")!");
		    }
	    }
	    catch (SQLException sqle) {
	    	throw new M4Exception(
	    		"SQL error when reading Key " + this.getId() + ": "
		    	+ sqle.getMessage());
	    }
	    finally {
			DB.closeResultSet(rs);
	    }
	} */

	/** reads the key members */ 
 	private void readKeyMembersFromDb() throws M4Exception {
    	this.setMembers(this.getObjectsReferencingMe(KeyMember.class));
 	}
     
    // ensures that each member has got the correct position value
    private void setPositions() throws M4Exception
    {
    	Iterator it = this.getMembers().iterator();
    	int i = 1;
    	while (it.hasNext())
    	{   ((KeyMember) it.next()).setThePosition(i);
    		i++;
    	}    	
    }
    
   	/** @see M4Data#removeAllM4References() */
	protected void removeAllM4References() throws M4Exception {
		this.setMembers(new Vector());
		this.removeDocObject();
	}

	/** @see M4Data#getDependentObjects */
	public Collection getDependentObjects() throws M4Exception {
		Collection ret = super.getDependentObjects();
		ret.addAll(this.getMembers());
		return ret;
	}
	
	/**
	 * Removing a Column from the Key, but not from the Columnset
	 * the Column belongs to. In the case of ForeignKey, a Column Link
	 * is removed.
	 */
	abstract public void removeColumn(String name) throws M4Exception;	

	public void removeAllColumns() throws M4Exception {
		this.removeMembers();	
	}
}
/*
 * Historie
 * --------
 * 
 * $Log: Key.java,v $
 * Revision 1.4  2006/04/11 14:10:14  euler
 * Updated license text.
 *
 * Revision 1.3  2006/04/06 16:31:13  euler
 * Prepended license remark.
 *
 * 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:17  hakenjos
 * Initial version!
 *
 */
