/*
 * 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.M4CompilerError;
import edu.udo.cs.miningmart.exception.M4Exception;
import edu.udo.cs.miningmart.exception.ParameterNotFoundException;
import edu.udo.cs.miningmart.m4.utils.InterM4Communicator;
import edu.udo.cs.miningmart.m4.utils.InterM4MCFeatureBaseAttribute;
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 MultiColumnFeature.
 * 
 * @author Timm Euler
 * @version $Id: MultiColumnFeature.java,v 1.3 2006/04/11 14:10:14 euler Exp $
 */
public class MultiColumnFeature extends Feature implements XmlInfo, edu.udo.cs.miningmart.m4.MultiColumnFeature {

	/** The M4 table name storing concept information. */
	public static final String M4_TABLE_NAME = "mcfeature_t";
	
	/** DB level: The attribute storing the MCF IDs. */
	public static final String ATTRIB_MCF_ID = "mcf_id"; // NOT NULL NUMBER

	/** DB level: The attribute storing the MCF IDs. */
	public static final String ATTRIB_MCF_NAME = "mcf_name"; // NOT NULL VARCHAR2(100)
	
	/** DB level: The attribute storing the MCFs' concept IDs. */
	public static final String ATTRIB_MCF_CONCEPT = "mcf_conid"; // NUMBER
	
	

	/** Inter M4 communication class between MultiColumnFeature and BaseAttribute */
	static final InterM4Communicator mcf2ba = new InterM4MCFeatureBaseAttribute();


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

	/** @see M4Table.getM4Info() */
	public M4Info getM4Info() {
		if (m4Info == null) {
			M4InfoEntry[] m4i = {
				new M4InfoEntry(ATTRIB_MCF_ID,      "getId",       "setId",               long.class,    NOT_NULL),
				new M4InfoEntry(ATTRIB_MCF_NAME,    "getName",     "setName",	          String.class,  NOT_NULL),
				new M4InfoEntry(ATTRIB_MCF_CONCEPT, "getConcept",  "primitiveSetConcept", edu.udo.cs.miningmart.m4.Concept.class)
		    //  new M4InfoEntry(ATTRIB_MCF_VALID,   "", "", 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("Concept", "getConcept",       "setConcept",    edu.udo.cs.miningmart.m4.Concept.class),
				new M4InfoEntry("Docu",    "getDocumentation", "setDocumentation", String.class)
			};
			xmlInfo = new M4Info(m4i);
		}
		return xmlInfo;
	}
	
    // MCFeature Variables from M4-DB (Name inherited from M4Data.java)
    private Concept myConcept;

	// Vector of BaseAttribute objects
    private final Vector myAttribs = new Vector();
	private boolean allBaseAttributesLoaded = false;

	/**
	 * @see edu.udo.cs.miningmart.m4.core.M4Data#Constructor
	 */	    
    public MultiColumnFeature(DB m4Db) {
    	super(m4Db);	
    }
	
	/**
	 * @see edu.udo.cs.miningmart.m4.core.Parameter#print
	 */
    public void print(){
		this.doPrint(Print.M4_OBJECT, "Multicolumn Feature " + getName() + "(Id = " + myId + ")" +
		      " of Concept: " + myConcept.getName());
    }

	/**
	 * Getter method.
	 * 
	 * @return all BaseAttributes that are bundled in this MCF.
	 */
    public Collection getBaseAttributes() throws M4Exception {
    	if (this.allBaseAttributesLoaded == false && ( ! this.isNew())) {
    		this.allBaseAttributesLoaded = true;
    		this.readBaseAttributesForMCFFromDB();
    	}
    	return this.myAttribs;
    }

	/**
	 * Setter method.
	 * 
	 * @param a <code>Collection</code> of the new BaseAttributes for this MCF.
	 */
    public void setBaseAttributes(Collection theAttribs) throws M4Exception {
    	mcf2ba.setCollectionTo(this, theAttribs);
    }

	/**
	 * @param ba a <code>BaseAttribute</code>
	 * @return <code>true</code> if a <code>BaseAttribute</code> with the same ID is already
	 * linked to this <code>MultiColumnFeature</code>
	 * */
	public boolean hasBaseAttribute(edu.udo.cs.miningmart.m4.BaseAttribute ba) throws M4Exception {
		Collection bas = this.getBaseAttributes();
		if (ba == null || bas == null) {
			return false;	
		}
		return bas.contains(ba);
	}

	public void addBaseAttribute(edu.udo.cs.miningmart.m4.BaseAttribute ba) throws M4Exception {
		mcf2ba.checkNameExists((BaseAttribute) ba, this);
		mcf2ba.add(this, (BaseAttribute) ba);
	}
	
	/**
	 * @param name the name 
	 * @return the <code>BaseAttribute</code> with the given name
	 *         if there is one (ignoring case); <code>null</code> otherwise
	 */
	public edu.udo.cs.miningmart.m4.BaseAttribute getBaseAttributeByName(String name) throws M4Exception {		
		Collection attribs = this.getBaseAttributes();
		Iterator it = attribs.iterator();
		BaseAttribute ba;
		while (it.hasNext())
		{
			ba = (BaseAttribute) it.next();
			if (ba.getName().equalsIgnoreCase(name))
			{  return ba;  }
		}
		return null;
	}

	public boolean removeBaseAttribute(edu.udo.cs.miningmart.m4.BaseAttribute ba) throws M4Exception {
		return mcf2ba.remove(this, (BaseAttribute) ba);
	}

	public void removeLinkToColumns() throws M4Exception {
		Collection attribs = this.getBaseAttributes();
		Iterator it = attribs.iterator();
		BaseAttribute ba;
		while (it.hasNext()) {
			ba = (BaseAttribute) it.next();
			ba.removeLinkToColumns();
		}
	}
	
	/**
	 * Copy this MultiColumnFeature.
	 * 
	 * @return A clone of this MCF with ID 0, because it does not exist in
	 * 		   the DB yet.
	 */
    public edu.udo.cs.miningmart.m4.MultiColumnFeature copy() throws M4Exception
    {
        MultiColumnFeature ret =
        	(MultiColumnFeature) this.getM4Db().createNewInstance(edu.udo.cs.miningmart.m4.core.MultiColumnFeature.class);

		ret.setName(this.getName());
		
		Iterator it = this.getBaseAttributes().iterator();
		while (it.hasNext()) {
			BaseAttribute myBa = (BaseAttribute) it.next();
			ret.addBaseAttribute(myBa.copy());
		}

        return ret;
    }

	/**
	 * Create a copy of this MultiColumnFeature and connect it to the given Concept.
	 * 
	 * @param newConcept the concept to add the copy of this MultiColumnFeature to
	 * @return a copy of this MultiColumnFeature; the copy is connected to the given Concept
	 */
	public edu.udo.cs.miningmart.m4.Feature copy(edu.udo.cs.miningmart.m4.Concept newConcept) throws M4Exception {

        edu.udo.cs.miningmart.m4.MultiColumnFeature ret =
        	(MultiColumnFeature) this.getM4Db().createNewInstance(edu.udo.cs.miningmart.m4.core.MultiColumnFeature.class);

        String nameOfCopy = newConcept.getValidName(this.getName(), MultiColumnFeature.class);
        ret.setName(nameOfCopy);
    	ret.setConcept(newConcept);	

		Iterator it = this.getBaseAttributes().iterator();
		while (it.hasNext()) {
			BaseAttribute myBa = (BaseAttribute) it.next();
			ret.addBaseAttribute((BaseAttribute) myBa.copy(newConcept));			
		}
    	return ret;
    }
	
	/**
	 * @return <code>true</code> iff this <code>MultiColumnFeature</code> has
	 * been &quot;deselected&quot; by a <code>FeatureSelection</code> operator
	 * or has not been connected by the user.
	 * A <code>MultiColumnFeature</code> is considered deselected, if one of
	 * its <code>BaseAttributes</code> has been deselected.
	 */
    public boolean isDeselected() throws M4CompilerError {
		Collection bas;
		try {
			bas = this.getBaseAttributes();
		}
		catch (M4Exception e) {
			throw new M4CompilerError(
				"Could not read BaseAttributes of MCF with ID " + this.getId()
				+ "!\n" + e.getMessage());	
		}
		if (bas != null) {
			Iterator it = bas.iterator();
			while (it.hasNext()) {
				BaseAttribute b = (BaseAttribute) it.next();
				if (b == null || b.isDeselected())
					return true;
			}
		}
		return false;
    }
	
	/**
	 * Sets all BaseAttributes that are found in the database as belonging to 
	 * this MultiColumnFeature into the MCF object.
	 */
    private void readBaseAttributesForMCFFromDB() throws M4Exception
    {
		Iterator it = this.getObjectsReferencingMe(BaseAttribute.class).iterator();
		while (it.hasNext()) {
			this.addBaseAttribute((BaseAttribute) it.next());
		}

        if (this.getBaseAttributes().size() < 2) {
        	throw new ParameterNotFoundException(
        		"Found MultiColumnFeature (Id: " + this.getId()
        		+ ") with less than 2 BaseAttributes!");
	    }
    }

    /**
	 * Primitive setter, do not use.
	 * @param c the <code>Concept</code> to be set
	 * */
    public void primitiveSetConcept(edu.udo.cs.miningmart.m4.Concept c) {
    	this.setDirty();
    	this.myConcept = (Concept) c;
    }

   	/** @see M4Data#removeAllM4References() */
	protected void removeAllM4References() throws M4Exception {
		super.removeAllM4References();
		this.setBaseAttributes(new Vector());
		this.setConcept(null);
		this.removeDocObject();
	}

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

	/**
	 * This method returns information about the relational validity
	 * of the object. A MCF is valid if it has at least two BAs and
	 * all the BAs are valid, and if it has a concept.
	 */
	public boolean isRelationallyValid() throws M4Exception
	{
		if (this.getConcept() == null)
		{   return false;   }
		
		Collection bas = this.getBaseAttributes();
		if (bas == null || bas.size() < 2) {
			return false;	
		}
		Iterator it = bas.iterator();
		while (it.hasNext()) {
			BaseAttribute ba = (BaseAttribute) it.next();
			if (ba.isRelationallyValid() == false) {
				return false;	
			}
		}
		return true;
	}
}
/*
 * Historie
 * --------
 * 
 * $Log: MultiColumnFeature.java,v $
 * Revision 1.3  2006/04/11 14:10:14  euler
 * Updated license text.
 *
 * Revision 1.2  2006/04/06 16:31:13  euler
 * Prepended license remark.
 *
 * Revision 1.1  2006/01/03 09:54:17  hakenjos
 * Initial version!
 *
 */
