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

import java.sql.SQLException;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Vector;

import edu.udo.cs.miningmart.db.CompilerDatabaseService;
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.m4.BaseAttribute;
import edu.udo.cs.miningmart.m4.Column;
import edu.udo.cs.miningmart.m4.Columnset;
import edu.udo.cs.miningmart.m4.Concept;
import edu.udo.cs.miningmart.m4.Feature;
import edu.udo.cs.miningmart.m4.MultiColumnFeature;

/**
 * This class is the abstract super-class for model-applying
 * operators. It cares about creating an intermediate table in
 * the business data schema linked to the original data by the
 * keys specified, creates the meta-data and ensures integrity
 * of the parameters. The abstract method <code>predict()</code>
 * has to read the data from the database, apply the model(s)
 * and update the intermediate table accordingly.
 * 
 * @author Martin Scholz
 * @version $Id: ModelApplier.java,v 1.4 2006/04/11 14:10:11 euler Exp $
 */
abstract public class ModelApplier extends SingleCSOperator {

	/** Key Columns of the current InputColumnset */
	private final Vector keyColumns = new Vector();

	/** Columns that can be exploited for Prediction in the current InputColumnset */
	private final Vector predictionColumns = new Vector();

	/** List of attributes of the intermediate table */
	private final Vector tablesAttributes = new Vector();

	/**
	 * After initialization at the beginning of <code>generateSQLDefinition</code>
	 * this method returns a <code>Collection</code> of the <code>InputConcept</code>'s
	 * current key <code>Column</code>s.
	 * 
	 * @return a <code>Collection</code> of <code>Column</code>s
	 */
	public Vector getKeyColumns() {
		return this.keyColumns;	 	
	}

	/** 
	 * Similar to <code>getKeyColumns()</code>, but returns the Column names, only.
	 * @return a <code>Collection</code> of <code>String</code>s
	 */
	public Vector getKeyColumnNames() {
		return columnsToColumnNames(this.getKeyColumns());
	}

	/**
	 * After initialization at the beginning of <code>generateSQLDefinition</code>
	 * this method returns a <code>Collection</code> of the <code>InputConcept</code>'s
	 * current <code>Column</code> that might be exploited for predicting values.
	 * The <code>Column</code>s correspond to the parameter <i>PredictingAttributes</i>.
	 * 
	 * @return a <code>Collection</code> of <code>Column</code>s
	 */
	public Vector getPredictionColumns() {
		return this.predictionColumns;
	}

	/** 
	 * Similar to <code>getPredictionColumns()</code>, but returns the Column names,
	 * only.
	 * @return a <code>Collection</code> of <code>String</code>s
	 */
	public Vector getPredictionColumnNames() {
		return columnsToColumnNames(this.getPredictionColumns());
	}
	
	/**
	 * This operator creates a view joining the InputConcept with a new
	 * table holding the predicted values.
	 * @see SingleCSOperator#getTypeOfNewColumnSet()
	 */
	public String getTypeOfNewColumnSet() {
		return Columnset.CS_TYPE_VIEW;
	}

	/** Helper method converting a collection of columns to a collection of the column's names. */
	private static Vector columnsToColumnNames(Collection columns) {
		Iterator it = columns.iterator();
		Vector names = new Vector();
		while (it.hasNext()) {
			Column col = (Column) it.next();
			if (col != null) {
				names.add(col.getName());
			}
		}	
		return names;	
	}

	/**
	 * @see SingleCSOperator#generateSQLDefinition(String)
	 */
	public String generateSQLDefinition(String selectPart) throws M4CompilerError
	{
		try {
			this.initKeyColumns();
			this.initPredictionColumns();
			final String targetTable = this.getTargetTableName();
			final Columnset inputCs = this.getInputConcept().getCurrentColumnSet();

			String csSql = inputCs.getSQLDefinition(); // easy case: no virtual column
			{
				Iterator it = inputCs.getColumns().iterator();
			 L: while (it.hasNext()) {
					Column col = (Column) it.next();
					String sql = col.getSQLDefinition();
					if ( sql != null && ! sql.equals(col.getName()) && ! (sql.length() == 0) ) {
						csSql = inputCs.getCompleteSQLQuery(); // virtual column found
						break L;
					}
				}
			}

			String whereList;
			{	
				Iterator it = this.getKeyColumns().iterator();
				StringBuffer whereBuf = new StringBuffer();
				while (it.hasNext()) {
					Column keyC = (Column) it.next();
					String colName = keyC.getName();
					whereBuf.append("V1." + colName + " = V2." + colName + ", ");
				}
				whereList = whereBuf.substring(0, whereBuf.length() - 2); // drop the last ", "
			}

			// Creates the table and copies the primary key:
			this.createPredictionsTable();

			// The heart of this applier: Fill the attributes with the predictions!
			this.predict();

			String selectList = this.getViewsSelectList(inputCs, "V1", "V2");

			return "( SELECT " + selectList +
				   " FROM (" + csSql       + ") V1, "
		                    + targetTable + " V2 " +
		           "WHERE " + whereList + " )";
		}
		catch (M4Exception e) {
			throw new M4CompilerError(e.getMessage());	
		}
	}

	/** 
	 * Helper method: Creates an explicit list of attributes for the select-part
	 * of the output columnset's view definition.
	 * @param inputCs the input <code>Columnset</code>
	 * @param inputCsPrefix the name of the input <code>Columnset</code> in the view
	 * @param tablePrefix the name of the intermediate table in the view
	 */
	private String getViewsSelectList(Columnset inputCs, String inputCsPrefix, String tablePrefix)
		throws M4Exception
	{
		StringBuffer attributes = new StringBuffer();
		HashSet keys = new HashSet();
		Iterator it = this.getKeyColumns().iterator();
		while (it.hasNext()) {
			keys.add(((Column) it.next()).getName());
		}
		
		it = inputCs.getColumns().iterator();
	 L: while (it.hasNext()) {
			Column column = (Column) it.next();
			String name = column.getName();
			if (keys.contains(name)) {
				continue L;	
			}
			String sql = column.getSQLDefinition();			
			if ( ! sql.equals(name)) {
				sql += " AS " + name;
			}
			attributes.append(inputCsPrefix + "." + sql + ", ");
		}
		
		it = this.tablesAttributes.iterator();
		while (it.hasNext()) {
			String name = (String) it.next();
			attributes.append(tablePrefix + "." + name + ", ");
		}
		
		return (attributes.substring(0, attributes.length() - 2));
	}

	/**
	 * Helper method: Creates the prediction table with just the key attributes and
	 * copies the keys of the original table to the new table. For creating the predicted
	 * there is a method <code>createPredictedAttributeInDb</code>.
	 * The private field <code>tablesAttributes</code> is also filled by these two methods.
	 */
	private void createPredictionsTable()
		throws M4Exception, M4CompilerError
	{
		final String tableName = this.getTargetTableName();
		final CompilerDatabaseService db = this.getM4Db();
		final edu.udo.cs.miningmart.m4.Columnset inputCs = this.getInputConcept().getCurrentColumnSet();
		final String inCsSql = inputCs.getSQLDefinition();

		{ // Create intermediate table with keys:
			db.dropBusinessTable(tableName);
			String sql = "CREATE TABLE " + tableName
					   + " AS ( SELECT " + this.getSelectStatementOfKeys()
					   + " FROM " + inCsSql + " )";
			try {
				db.executeBusinessSqlWrite(sql);
			}
			catch (SQLException e) {
				throw new M4CompilerError(
					"Operator 'ModelApplier': SQLException when trying to create an intermediate table '"
					+ tableName + "'!\nSQL statement was:\n" + sql
					+ "\nException message:\n" + e.getMessage());
			}
			
			db.addTableToTrash(tableName, inputCs.getSchema(), this.getStep().getId());
		}

		{ // Collect the name of the key attributes in the target table
		  // and create the primary key (index):
			Iterator it = this.getKeyColumns().iterator();
			StringBuffer pkBuf = new StringBuffer();
			while (it.hasNext()) {
				String attribName = ((Column) it.next()).getName();
				tablesAttributes.add(attribName);
				pkBuf.append(attribName + ", ");
			}
			
			if (tablesAttributes.isEmpty()) {
				throw new M4CompilerError(
					"Operator 'ModelApplier': No primary key for table '"
					+ tableName + "'found!");
			}
			
			String pkString = "(" + pkBuf.substring(0, pkBuf.length() - 2) + ")";
			String pkName = tableName + "_idx";
			String sql = "ALTER TABLE " + tableName + " ADD ( CONSTRAINT "
						   + pkName + " PRIMARY KEY " + pkString + " )";
						   
			try {
				db.executeBusinessSqlWrite(sql);
			}
			catch (SQLException e) {
				throw new M4CompilerError(
					"Operator 'ModelApplier': SQLException, probably because the primary key constraint of Concept '"
					+ this.getInputConcept().getName() + "', table/view '" + inputCs.getName() + "' is not valid!\n"
					+ e.getMessage());
			}
			
			db.addIndexToTrash(tableName, inputCs.getSchema(), this.getStep().getId());
		}
	}

	/**
	 * Helper method adding a new attribute to the newly created database table
	 * for the attribute values created in the specified loop.
	 * @param loop the loop number sepcifying the attribute to be predicted
	 */
	protected void createPredictedAttributeInDb(int loop) throws M4CompilerError
	{
		BaseAttribute predBa = this.getPredictedAttribute(loop);
		String predColName = predBa.getName();
		String dataType = this.getDatabaseDtForNewColumn(predBa);
		String tableName = this.getTargetTableName();
		
		// if the model is applied blockwise, the predicted attribute may already
		// exist:
		String getMetadataQuery = "select * from " + tableName + " where " + this.getM4Db().getUniqueRowIdentifier(true) + " < 2";
		try {
			ResultSet rs = this.getM4Db().executeBusinessSqlRead(getMetadataQuery);
			ResultSetMetaData rsmd = rs.getMetaData();
			for (int i = 1; i <= rsmd.getColumnCount(); i++) {
				if (rsmd.getColumnName(i).equalsIgnoreCase(predColName)) {
					rs.close();
					return;
				}
			}
			rs.close();
		}
		catch (SQLException sqle) {
			throw new M4CompilerError(
				"Operator 'ModelApplier': SQLException when trying to find out if attribute '"
				+ predColName + "' for predicted values in table '" + tableName + "' already exists:\n"
				+ sqle.getMessage());
		}
		
		String sql = "ALTER TABLE " + tableName + " ADD ( " + predColName + " " + dataType + " )";
			
		try {
			this.getM4Db().executeBusinessSqlWrite(sql);
		}
		catch (SQLException e) {
			throw new M4CompilerError(
				"Operator 'ModelApplier': SQLException when trying to add attribute '"
				+ predColName + "' for predicted values to table '" + tableName + "'!\n"
				+ e.getMessage());
		}			
		tablesAttributes.add(predColName);		
	}

	/**
	 * This method cares about creating a well-formed select part to read
	 * the key attributes from the input <code>Columnset</code>. It is relevant
	 * for creating the joint output view, but is also useful for the
	 * <code>predict()</code> part.
	 * 
	 * @return the select-part of the key(s) of the input columnset.
	 * The <code>String</code> contains the part between <i>SELECT</i>
	 * and <i>FROM</i> if you would only want to select the keys from the
	 * <code>Columnset</code>.
	 */
	protected String getSelectStatementOfKeys() {
		Collection keys = this.getKeyColumns();
		return this.getSelectFromColumnCollection(keys);
	}

	/**
	 * This method is similar to <code>getSelectStatementOfKeys()</code>, but
	 * creates a select part for the predicting columns specified as parameters.
	 * This method is a service method for the <code>predict()</code> part to
	 * be implemented by the non-abstract <code>ModelAppliers</code>.
	 * 
	 * @return the select-part of the attributes available for predicting,
	 * part of the input columnset.
	 * The <code>String</code> contains the part between <i>SELECT</i>
	 * and <i>FROM</i> if you would only want to select these attributes from the
	 * <code>Columnset</code>.
	 */
	protected String getSelectStatementOfPredAttribs() {
		Collection predAttribs = this.getPredictionColumns();
		return this.getSelectFromColumnCollection(predAttribs);
	}

	/**
	 * Helper method of <code>getSelectStatementOfKeys()</code> and
	 * <code>getSelectStatementOfPredAttribs()</code>.
	 */
	private String getSelectFromColumnCollection(Collection columns) {
		Iterator it = columns.iterator();
		StringBuffer buf = new StringBuffer();
		int i = 0;
		try {
		while (it.hasNext()) {
			Column column = (Column) it.next();
			String sql = column.getSQLDefinition();
			if (! sql.equals(column.getName())) {
				sql += " AS " +	column.getName();
			}
			buf.append(sql + ", ");
			i++;
		}
		return buf.substring(0, buf.length() - 2);		
		}
		catch (Exception e) {
			System.out.println(e.getMessage());
		}
		return null;
	}

	/** Helper method initializing the field <code>keyColumns</code> */
	private void initKeyColumns() throws M4CompilerError {
		try {
			Collection BAs = this.getTheKeys();
			Iterator it = BAs.iterator();
			Vector keyColumns = new Vector();
			while (it.hasNext()) {
				keyColumns.add(((BaseAttribute) it.next()).getCurrentColumn());
			}			
			if (keyColumns.isEmpty()) {
				throw new M4CompilerError("Operator 'ModelApplier': Empty set of keys in initKeyColumns() !");	
			}
			this.keyColumns.clear();
			this.keyColumns.addAll(keyColumns);
		}
		catch (M4Exception m4e) {
			throw new M4CompilerError("Operator 'ModelApplier': M4Exception caught when accessing columns of the Primary Key features: " + m4e.getMessage());
		}
	}

	/** Helper method initializing the field <code>predictionColumns</code> */
	private void initPredictionColumns() throws M4Exception, M4CompilerError {
		Feature[] predFeatures = this.getThePredictingAttributes();
		if (predFeatures == null || predFeatures.length == 0) {
			throw new M4CompilerError("Operator 'ModelApplier': Empty set of PredictingAttributes!");	
		}

		this.predictionColumns.clear();
		for (int i=0; i<predFeatures.length; i++) {
			Collection col = this.findInputColumnForFeature(predFeatures[i]);
			this.predictionColumns.addAll(col);
		}
	}

	/** Helper method: Finds the Columns of the specified feature in the InputConcept. */
	private Collection findInputColumnForFeature(Feature feature)
		throws M4Exception, M4CompilerError
	{
		Vector result = new Vector();
		Concept inputCon = this.getInputConcept();

		if (feature instanceof BaseAttribute) {
			BaseAttribute inKeyF = findCorrespondingBaInConcept((BaseAttribute) feature, inputCon);
			if (inKeyF != null) {
				Column keyC = inKeyF.getCurrentColumn();
				result.add(keyC);
			}
			else {
				throw new M4CompilerError(
					"Operator 'ModelApplier': BaseAttribute '" + feature.getName()
					+ "' not found in InputConcept!");
			}
		}
		else {
			Iterator it = ((MultiColumnFeature) feature).getBaseAttributes().iterator();
			while (it.hasNext()) {
				BaseAttribute inKeyF = findCorrespondingBaInConcept((BaseAttribute) it.next(), inputCon);
				if (inKeyF != null) {
					Column keyC = inKeyF.getCurrentColumn();
					result.add(keyC);
				}
				else {
					throw new M4CompilerError(
						"Operator 'ModelApplier': MultiColumnFeature '" + feature.getName()
						+ "' has a BaseAttribute not found in the InputConcept!");
				}
			}
		}
		return result;
	}

	/** 
	 * Service method turning a <code>Collection</code> of <code>Column</code>
	 * names or other attributes or SQL definitions into a comma separated format
	 * usuable within SQL statements.
	 * 
	 * @param columnNames a <code>Collection</code> of <code>Column</code> names
	 * @return a comma separated <code>String</code> of these names.
	 */
	protected static String stringCollectionToCommaSeparatedString(Collection columnNames)
	{
		Iterator it = columnNames.iterator();
		StringBuffer sBuf = new StringBuffer();			
		while (it.hasNext()) {
			sBuf.append((String) it.next() + ", ");
		}
		return sBuf.substring(0, sBuf.length() - 2);
	}

	/** 
	 * Helper method: The keys are part of the output concept, but the input concept's key columns
	 * are necessary when copying virtual columns. This method find a corresponding BaseAttribute
	 * in the InputConcept when given the BaseAttribute of another Concept.
	 * If no corresponding BaseAttribute is found then null is returned.
	 */
	public static BaseAttribute findCorrespondingBaInConcept(BaseAttribute ba, Concept concept)
		throws M4Exception
	{
		Iterator it = concept.getFeatures().iterator();
		BaseAttribute result = null;
		while (it.hasNext() && result == null) {
			Feature f = (Feature) it.next();
			if (f instanceof MultiColumnFeature) {
				result = ((MultiColumnFeature) f).getBaseAttributeByName(ba.getName());
			}
			else if (ba.correspondsTo(f)) {
				result = (BaseAttribute) f;
			}
		}
		return result;
	}

	/** The name of the helper table with primary keys and target value is chosen here. */
	protected String getTargetTableName() throws M4CompilerError {
		return "TMP_" + this.getStep().getId() + this.getInputConcept().getCurrentSuffix();	
	}
	
	/**
	 * All Columns of the InputConcept need to be copied to the OutputConcept.
	 * @see ConceptOperator#mustCopyFeature(String)
	 */
	protected boolean mustCopyFeature(String nameOfFeature)	throws M4CompilerError
	{
		return true;
	}

	/** Method overridden to create <code>Column</code>s for the newly created attributes. */
    protected Columnset createSingleColumnSet(int index) throws M4CompilerError {
		final Columnset cs = super.createSingleColumnSet(index);
		HashSet keys = new HashSet();
		Iterator it = this.getKeyColumns().iterator();
		while (it.hasNext()) {
			keys.add(((Column) it.next()).getName());
		}

		final DB db = this.getM4Db();
		it = this.tablesAttributes.iterator();
		
		try {
			while (it.hasNext()) {
				String attribName = (String) it.next();
				if ( ! keys.contains(attribName)) {
					Column newCol = (Column) db.createNewInstance(edu.udo.cs.miningmart.m4.core.Column.class);
					newCol.setName(attribName);
					newCol.setColumnset(cs);
				
					Iterator feaIt = this.getOutputConcept().getFeatures().iterator();
					BaseAttribute correspondingBa = null;
				    while (feaIt.hasNext() && correspondingBa == null) {
						Feature f = (Feature) feaIt.next();
						if (f instanceof BaseAttribute) {
							BaseAttribute ba = (BaseAttribute) f;
							if (attribName.equals(ba.getName())) {
								correspondingBa = ba;
							}
						}
						else {
							BaseAttribute ba = ((MultiColumnFeature) f).getBaseAttributeByName(attribName);
							if (ba != null) {
								correspondingBa = ba;
							}
						}	
					}
					
					if (correspondingBa == null) {
						throw new M4CompilerError(
							"Operator 'ModelApplier' created attribute '"
							+ attribName + "' without a corresponding BaseAttribute!");	
					}
				
					newCol.setBaseAttribute(correspondingBa);

					// To do: This is not true in general:
					newCol.setColumnDataTypeName(this.getRelationalDtForNewColumn(correspondingBa));
				}
			}
		}
		catch (M4Exception e) {
			throw new M4CompilerError(e.getMessage());
		}
		
		return cs;
    }

	/**
	 * Builds a database query on the input <code>Columnset</code> projected to the set of
	 * keys and predicting attributes. The parameters specify a possible ROWNUM-restriction,
	 * to read only a block of tuples.
	 * @param fromRowNum the first row number to be returned or <code>null</code> for unrestricted
	 * @param toRowNum the last row number to be returned or <code>null</code> for unrestricted
	 * @return an SQL-query <code>String</code>
	 */
	protected String getDatabaseQueryForRows(Long fromRowNum, Long toRowNum)
		throws M4CompilerError {
		return buildDatabaseQuery() + getTupleRestriction(fromRowNum, toRowNum);
	}


	/** Builds the SQL-query without any ROWNUM-restriction */
	private String buildDatabaseQuery()  throws M4CompilerError {		
		edu.udo.cs.miningmart.m4.Columnset inputCs = null;
		try {
			this.getInputConcept().getCurrentColumnSet();
		}
		catch (M4Exception m4e) {
			throw new M4CompilerError("ModelApplier: M4 error accessing input concept's columnset: " + m4e.getMessage());
		}
		String keys = this.getSelectStatementOfKeys();
		String pred = this.getSelectStatementOfPredAttribs();
		String selectPart = "SELECT " + keys + ", " + pred;
		String fromPart = " FROM " + inputCs.getSQLDefinition();
		return selectPart + fromPart;	
	}

	/**
	 * Builds the ROWNUM-restriction for the given intervale.
	 * @param fromRowNum the first row number to be returned or <code>null</code> for unrestricted
	 * @param toRowNum the last row number to be returned or <code>null</code> for unrestricted
	 * @return a <code>String</code> like <i>WHERE ROWNUM &lt; NUM1</i> to be concatenated with
	 * a <code>String</code> returned by <code>buildDatabaseQuery()</code>.
	 */
	private String getTupleRestriction(Long fromRowNum, Long toRowNum) {
		if (fromRowNum == null && toRowNum == null)
			return "";
		
		StringBuffer buf = new StringBuffer(" WHERE ");
		if (fromRowNum != null)
			buf.append("ROWNUM > " + (fromRowNum.longValue() - 1));
		
 		if (toRowNum != null)
 			buf.append((fromRowNum != null ? " AND" : "") + " ROWNUM < " + toRowNum.longValue() + 1);
 		
 		return buf.toString();
	}


	// ***** Parameters *****

	/** @return the input concept */	
	public Concept getInputConcept() throws M4CompilerError {
		return super.getInputConcept();	
	}

	/** @return the output concept */	
	public Concept getOutputConcept() throws M4CompilerError {
		return super.getOutputConcept();	
	}

	/** @return the array of <code>BaseAttribute</code>s used to make the prediction */
	public Feature[] getThePredictingAttributes() throws M4CompilerError {
		return (Feature[]) this.getParameter("PredictingAttributes");
	}

	/** @return the <code>BaseAttribute</code> to be predicted by the model. */
	public BaseAttribute getPredictedAttribute(int loop) throws M4CompilerError {
		return (BaseAttribute) this.getSingleParameter("PredictedAttribute", loop);
	}

	/**
	 * @return a Collection of <code>BaseAttribute</code>s that form the
	 * primary key of the input concept
	 */
	public Collection getTheKeys() throws M4CompilerError {
		try {
			Feature[] theFeatures = (Feature[]) this.getParameter("PrimaryKey");
		// 	convert them to BAs (there might be MCFs):
			Vector theKeyBAs = new Vector();
			for (int i = 0; i < theFeatures.length; i++) {
				if (theFeatures[i] instanceof BaseAttribute) {
					theKeyBAs.add(theFeatures[i]);
				}
				else if (theFeatures[i] instanceof MultiColumnFeature) {
					theKeyBAs.addAll(((MultiColumnFeature) theFeatures[i]).getBaseAttributes());
				}
			}
			theKeyBAs.trimToSize();
			return theKeyBAs;
		}
	    catch (M4Exception m4e) {
			throw new M4CompilerError("Operator 'ModelApplier': M4 exception caught when reading Parameter 'PrimaryKey': " + m4e.getMessage());
		}
	}

	// ***** To be implemented by non-abstract ModelAppliers *****

	/**
	 * Specifies the relational datatype for the new Columns based on their BaseAttribute.
	 * @param ba the BaseAttribute of a Column to be predicted
	 * @return the relational datatype of the Column in String representation, e.g.
	 * <code>NUMBER</code> or <code>String</code>.
	 */
	abstract protected String getRelationalDtForNewColumn(BaseAttribute ba);

	/**
	 * Specifies the relational datatype for the new Columns based on their BaseAttribute.
	 * @param ba the BaseAttribute of a Column to be predicted
	 * @return the database datatype of the Column in String representation, e.g.
	 * <code>VARCHAR2(10)</code>.
	 */
	abstract protected String getDatabaseDtForNewColumn(BaseAttribute ba) throws M4CompilerError;

	/**
	 * Predicts the values applying the specified models.
	 * Please note that the operator has to care about loops, the method is called
	 * just once for all loops!
	 */
	abstract protected void predict() throws M4Exception, M4CompilerError;

}
/*
 * Historie
 * --------
 * 
 * $Log: ModelApplier.java,v $
 * Revision 1.4  2006/04/11 14:10:11  euler
 * Updated license text.
 *
 * Revision 1.3  2006/04/06 16:31:11  euler
 * Prepended license remark.
 *
 * Revision 1.2  2006/03/23 11:13:45  euler
 * Improved exception handling.
 *
 * Revision 1.1  2006/01/03 09:54:22  hakenjos
 * Initial version!
 *
 */
