/*
 * 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.Vector;

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

/**
 * @author Martin Scholz
 * @version $Id: Projection.java,v 1.5 2006/04/11 14:10:14 euler Exp $
 */
public class Projection extends M4Data implements XmlInfo, edu.udo.cs.miningmart.m4.Projection {

	/** The name of the corresponding M4 table. */
	public static final String M4_TABLE_NAME = "projection_t";

	/** db level: name of the attribute specifying the projection's id */
	public static final String ATTRIB_PROJECTION_ID = "pro_id";	

	/** db level: name of the attribute specifying the from-concept's id */
	public static final String ATTRIB_FROM_CONCEPT = "pro_fromconid";	

	/** db level: name of the attribute specifying the to-concept's id */
	public static final String ATTRIB_TO_CONCEPT = "pro_toconid";	



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

	/** @see M4Table.getM4Info() */
	public M4Info getM4Info() {
		if (m4Info == null) {
			M4InfoEntry[] m4i = {
				new M4InfoEntry(ATTRIB_PROJECTION_ID, "getId",          "setId",                   long.class,    NOT_NULL),
				new M4InfoEntry(ATTRIB_FROM_CONCEPT,  "getFromConcept", "primitiveSetFromConcept", edu.udo.cs.miningmart.m4.Concept.class, NOT_NULL),
				new M4InfoEntry(ATTRIB_TO_CONCEPT,    "getToConcept",   "primitiveSetToConcept",   edu.udo.cs.miningmart.m4.Concept.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("FromConcept", "getFromConcept", "setFromConcept", edu.udo.cs.miningmart.m4.Concept.class),
				new M4InfoEntry("ToConcept",   "getToConcept",   "setToConcept",   edu.udo.cs.miningmart.m4.Concept.class)
			};
			xmlInfo = new M4Info(m4i);
		}
		return xmlInfo;
	}

	// the instance fields:
	private Concept fromConcept;
	private Concept toConcept;


	public Projection(DB db) { super(db); }


	/**
	 * @see M4Object#print()
	 */
	public void print() {
		edu.udo.cs.miningmart.m4.Concept fromC = this.getFromConcept();
		edu.udo.cs.miningmart.m4.Concept toC   = this.getToConcept();
		
		doPrint(Print.M4_OBJECT, "Projection (Id = " + this.getId() + ";"
				+ " From-Concept's ID: " + (fromC == null ? "<none>" : "<" + fromC.getId() + ">") + "; "
				+ " To-Concept's ID: " + (toC == null ? "<none>" : "<" + toC.getId()) + ">" + ")"
				);
	}
	
	/**
	 * @see edu.udo.cs.miningmart.m4.core.M4Data#getObjectsInNamespace(Class)
	 */
	protected Collection getObjectsInNamespace(Class typeOfObjects) throws M4Exception {
		return null;
	}

	/**
	 * Getter method.
	 * 
	 * @return the <code>Concept</code> stored as the From-Concept in
	 *         this <code>Projection</code>
	 */
	public edu.udo.cs.miningmart.m4.Concept getFromConcept() {
		return this.fromConcept;
	}

	/**
	 * Getter method.
	 * 
	 * @return the <code>Concept</code> stored as the To-Concept in
	 *         this <code>Projection</code>
	 */		
	public edu.udo.cs.miningmart.m4.Concept getToConcept() {
		return this.toConcept;		
	}

	/**
	 * Setter method for the From-Concept of this <code>Projection</code>
	 * @param fromConcept the <code>Concept</code> to be set as the new
	 *        From-Concept
	 */
	public void setFromConcept(edu.udo.cs.miningmart.m4.Concept fromConcept) throws M4Exception {
		if (this.getToConcept() != null && fromConcept != null) {
			Collection col = getTransitiveToProjectionsForConcept((Concept) this.getToConcept());
			if (col.contains(fromConcept)) {
				this.setToConcept(null);
				throw new M4Exception(
					"Setting the toConcept for this projection would result in"
					+ " cyclic projections!");
			}
		}
		Concept.con2fromPro.updateReferenceTo(this, fromConcept);
	}

	/**
	 * Setter method for the To-Concept of this <code>Projection</code>
	 * @param toConcept the <code>Concept</code> to be set as the new
	 *        To-Concept
	 */
	public void setToConcept(edu.udo.cs.miningmart.m4.Concept toConcept) throws M4Exception {
		if (this.getFromConcept() != null && toConcept != null) {
			Collection col = getTransitiveToProjectionsForConcept((Concept) toConcept);
			if (col.contains(this.getFromConcept())) {
				this.setFromConcept(null);
				throw new M4Exception(
					"Setting the toConcept for this projection would result in"
					+ " cyclic projections!");
			}
		}
		Concept.con2toPro.updateReferenceTo(this, toConcept);
	}

	/**
	 * Primitive setter, do not use it!
	 * @param fromConcept the new From-Concept to be set
	 */
	public void primitiveSetFromConcept(edu.udo.cs.miningmart.m4.Concept fromConcept) {
		this.setDirty();
		this.fromConcept = (Concept) fromConcept;
	}

	/**
	 * Primitive setter, do not use it!
	 * @param fromConcept the new From-Concept to be set
	 */
	public void primitiveSetToConcept(edu.udo.cs.miningmart.m4.Concept toConcept) {
		this.setDirty();
		this.toConcept = (Concept) toConcept;
	}

   	/** @see M4Data#removeAllM4References() */
	protected void removeAllM4References() throws M4Exception {
		this.setFromConcept(null);
		this.setToConcept(null);
	}

	/**
	 * Private helper method.
	 * @param startCon the <code>Concept</code> to start with
	 * @return a <code>Collection</code> of all <code>Concept<code>s reachable by
	 *         continuously following the specified Concept's to-projections.
	 */
	static Collection getTransitiveToProjectionsForConcept(Concept startCon)
		throws M4Exception
	{	
		Vector  open   = new Vector();
		HashSet closed = new HashSet();

		if (startCon != null)
			open.add(startCon);
		
		while (!open.isEmpty()) {
			Concept next = (Concept) open.remove(open.size() - 1);
			closed.add(next);
			if (next != null) {
				Iterator it = next.getProjectionsAsFromConcept().iterator();
				while (it.hasNext()) {
					Projection pro = (Projection) it.next();
					edu.udo.cs.miningmart.m4.Concept to = pro.getToConcept();
					if (to != null && (!open.contains(to)) && (!closed.contains(to))) {
						open.add(to);
					}
				}
			}			
		}
		return closed;
	}

	/**
	 * For a valid projection, the From concept must have all the features
	 * of the To concept (correspondence is determined by name), 
	 * and can have more.
	 *  
	 * @return TRUE iff this projection represents a valid projection 
	 * (feature selection) from the from concept to the to concept.
	 * @throws M4Exception
	 */
	public boolean isValid() throws M4Exception {
		if (this.getFromConcept() == null || this.getToConcept() == null) {
			return false;
		}
		// for every feature in the ToConcept there must be a corresponding
		// feature in the FromConcept:
		Iterator toFeaturesIterator = this.getToConcept().getFeatures().iterator();
		while (toFeaturesIterator.hasNext()) {
			Feature toF = (Feature) toFeaturesIterator.next();
			boolean correspondingFeatureFound = false;
			Iterator fromFeaturesIterator = this.getFromConcept().getFeatures().iterator();
			while (fromFeaturesIterator.hasNext()) {
				Feature fromF = (Feature) fromFeaturesIterator.next();
				if (toF.correspondsTo(fromF)) {
					correspondingFeatureFound = true;
				}
			}
			if ( ! correspondingFeatureFound) {
				return false;
			}
		}
		return true;
	}
}
/*
 * Historie
 * --------
 * 
 * $Log: Projection.java,v $
 * Revision 1.5  2006/04/11 14:10:14  euler
 * Updated license text.
 *
 * Revision 1.4  2006/04/06 16:31:13  euler
 * Prepended license remark.
 *
 * Revision 1.3  2006/04/03 09:32:53  euler
 * Improved Export/Import of Projections
 * and Subconcept links.
 *
 * Revision 1.2  2006/01/26 15:40:28  euler
 * bugfix
 *
 * Revision 1.1  2006/01/03 09:54:17  hakenjos
 * Initial version!
 *
 */
