/*
 * 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.StringTokenizer;
import java.util.Vector;

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

/**
 * Objects of this class reflect static properties of an operator
 * found in the OP_PARAMS_T table.
 * 
 * @author Martin Scholz
 * @version $Id: OpParam.java,v 1.4 2006/04/11 14:10:14 euler Exp $
 */
public class OpParam extends M4Data implements XmlInfo, edu.udo.cs.miningmart.m4.OpParam {

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

	/** db level: name of the OpParam id attribute */
	public static final String ATTRIB_OP_PARAM_ID   = "param_id";

	/** db level: name of the OpParam id attribute */
	public static final String ATTRIB_OP_PARAM_OPID   = "op_id";

	/** db level: name of the OpParam name attribute */
	public static final String ATTRIB_OP_PARAM_NAME = "name";

	/** db level: name of the OpParam minimum number of parameters attribute */
	public static final String ATTRIB_MIN_ARG       = "minarg";
	
	/** db level: name of the OpParam maximum number of parameters attribute */
	public static final String ATTRIB_MAX_ARG       = "maxarg";

	/** db level: name of the OpParam input/output type attribute */
	public static final String ATTRIB_IO_TYPE       = "io";

	/** db level: name of the OpParam parameter type attribute */
	public static final String ATTRIB_PAR_TYPE      = "type";


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

	/** @see M4Table.getM4Info() */
	public M4Info getM4Info() {
		if (m4Info == null) {
			M4InfoEntry[] m4i = {
				new M4InfoEntry(ATTRIB_OP_PARAM_ID,   "getId",          "setId",                long.class,     NOT_NULL),
				new M4InfoEntry(ATTRIB_OP_PARAM_OPID, "getTheOperator", "primitiveSetOperator", edu.udo.cs.miningmart.m4.Operator.class, NOT_NULL),
				new M4InfoEntry(ATTRIB_OP_PARAM_NAME, "getName",        "setName",              String.class,   NOT_NULL),
				new M4InfoEntry(ATTRIB_MIN_ARG,       "getMinArg",      "setMinArg",            int.class,      NOT_NULL),
				new M4InfoEntry(ATTRIB_MAX_ARG,       "getMaxArg",      "setMaxArg",            int.class),
				new M4InfoEntry(ATTRIB_IO_TYPE,       "getInput",       "setInput",             String.class,   NOT_NULL),
				new M4InfoEntry(ATTRIB_PAR_TYPE,      "getTypeS",       "setType",              String.class,   NOT_NULL)				
			};
			m4Info = new M4Info(m4i);
		}
		return m4Info;
	}

	/** Cache for getXmlInfo() */
	private static M4Info xmlInfo = null;

	/** @see M4Table.getM4Info() */
	public M4Info getXmlInfo() {
		if (xmlInfo == null) {
			M4InfoEntry[] m4i = {
				new M4InfoEntry("Operator",  "getTheOperator",   "setTheOperator",   edu.udo.cs.miningmart.m4.Operator.class),
				new M4InfoEntry("Name",      "getName",          "setName",          String.class),
				new M4InfoEntry("Atleast",   "getMinArg",        "setMinArg",        int.class),
				new M4InfoEntry("Atmost",    "getMaxArg",        "setMaxArg",        int.class),
				new M4InfoEntry("InOut",     "getInput",         "setInput",         String.class),
				new M4InfoEntry("Type",      "getTypeS",         "setType",          String.class),
				new M4InfoEntry("Docu",      "getDocumentation", "setDocumentation", String.class)
			};
			xmlInfo = new M4Info(m4i);
		}
		return xmlInfo;
	}

	// Input/Output constants used in the DB:
	private static final boolean IN         = true;
	private static final String  IN_STRING  = "IN";
	private static final boolean OUT        = false;
	private static final String  OUT_STRING = "OUT";

	// fields of the OpParam objects
	private int      minArg;
	private int      maxArg;
	private short    type;
	private boolean  ioType;
	private Operator myOperator;
	
	/** @param m4Db the <code>DB</code> object to be used */
	public OpParam(DB m4Db) {
		super(m4Db);
	}
	
	/**
	 * @see edu.udo.cs.miningmart.m4.core.M4Object#print()
	 */
	public void print() {
	}
	
	/**
	 * @see edu.udo.cs.miningmart.m4.core.M4Data#getObjectsInNamespace(Class)
	 */
	protected Collection getObjectsInNamespace(Class typeOfObjects) throws M4Exception {
		return null;
	}

	/**
	 * Gets the input/output property.
	 * 
	 * @return Returns <i>true</i> iff the parameter is an input.
	 */
	public boolean isInput() {
		return (this.ioType == OpParam.IN);
	}

	/**
	 * Similar to <code>isInput()</code>: Gets the input/output property,
	 * but returns a <code>String</code>.
	 * 
	 * @return Returns a <code>String</code> indicating if the parameter is
	 *         an input or output.
	 */
	public String getInput() {
		return (this.ioType == OpParam.IN ? OpParam.IN_STRING : OpParam.OUT_STRING);
	}

	/**
	 * Gets the maxArg.
	 * @return Returns an int. A return value of &quot;-1&quot; represents a
	 * <i>NULL</i> in the database, a return value of &quot;0&quot; is not
	 * possible.
	 */
	public int getMaxArg() {
		return (this.maxArg == 0 ? -1 : this.maxArg);
	}

	/**
	 * Gets the minArg.
	 * @return Returns a int
	 */
	public int getMinArg() {
		return minArg;
	}

	/**
	 * Gets the type.
	 * @return Returns a short
	 */
	public short getType() {
		return type;
	}

	/**
	 * @see getType()
	 * 
	 * @return the type in its database <code>String</code> representation.
	 */
	public String getTypeS() throws ParameterError {
		short type = this.getType();
		return edu.udo.cs.miningmart.m4.core.Parameter.typeStringForTypeConst(type);
	}


	/**
	 * Sets the input/output property.
	 * @param ioType Either "IN" for input parameters, or "OUT".
	 */
	public void setInput(String ioType) throws M4Exception {
		this.setDirty();
		if (ioType.equalsIgnoreCase(IN_STRING))
			this.ioType = OpParam.IN;
		else if (ioType.equalsIgnoreCase(OUT_STRING))
			this.ioType = OpParam.OUT;
		else throw new M4Exception("Parameter " + this.getId()
					+ "in table OP_PARAMS_T is neither '" + IN_STRING
					+ "' nor '" + OUT_STRING +"', but: '" + ioType + "'");
	}

	/**
	 * Sets the maxArg.
	 * @param maxArg The maxArg to set
	 */
	public void setMaxArg(int maxArg) {
		this.setDirty();
		this.maxArg = maxArg;
	}

	/**
	 * Sets the minArg.
	 * @param minArg The minArg to set
	 */
	public void setMinArg(int minArg) {
		this.setDirty();
		this.minArg = minArg;
	}

	/**
	 * Sets the type.
	 * @param type The type <i>String</i> read from the database.
	 */
	public void setType(String typeString) throws M4Exception {
		try {
			this.type = edu.udo.cs.miningmart.m4.core.Parameter.typeConstForTypeString(typeString);
		}
		catch (ParameterError e) {
			throw new M4Exception("Parameter " + this.getId()
			+ "in table OP_PARAMS_T has unsupported type entry: '"
			+ typeString + "'");
		}
	}

	/**
	 * Gets the Operator.
	 * @return Returns an Operator
	 */
	public edu.udo.cs.miningmart.m4.Operator getTheOperator() {
		return this.myOperator;
	}

	/**
	 * Sets the Operator.
	 * @param operator The Operator to set
	 */
	public void setTheOperator(edu.udo.cs.miningmart.m4.Operator operator) throws M4Exception {
		Operator.op2oppar.checkNameExists(this, operator);
		Operator.op2oppar.updateReferenceTo(this, operator);
	}

	/**
	 * Primitive setter for the Operator. Do not use it!
	 * @param operator The Operator to set
	 */
	public void primitiveSetOperator(edu.udo.cs.miningmart.m4.Operator operator) {
		this.setDirty();
		this.myOperator = (Operator) operator;
	}

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

	/*  Methods dealing with the constraints for this operator parameter */	

	
	/**
	 * Returns all Constraint objects that constrain this operator parameter.
	 * 
	 * @return a Collection of Constraint objects
	 */
	public Collection getApplicableConstraints() throws M4Exception {

		Collection cons = this.getTheOperator().getConstraints();
		if (cons == null) {
			throw new M4Exception("OpParam '" + this.getName() + "' for Operator '" +
					              this.getTheOperator().getName() + 
								  "': Got NULL when asking for constraints!");
		}
		Iterator it = cons.iterator();
		Vector ret = new Vector();
		while (it.hasNext()) {
			Constraint c = (Constraint) it.next();

			// the constraint must apply to this parameter; this is checked
			// by checking the names of object1 and object2:
			if (c.getObj1().equalsIgnoreCase(this.getName()) ||
			    (c.getObj2() != null && c.getObj2().equalsIgnoreCase(this.getName())))
			    {
				ret.add(c);
			}				
		}
		return ret;
	}
	
	// helper method: checks if the given constraint type occurs for 
	// this operator parameter
	private Constraint getMyConstraintOfType(String type) throws M4Exception {
		Collection cons = this.getApplicableConstraints();
		Iterator it = cons.iterator();
		while (it.hasNext()) {
			Constraint c = (Constraint) it.next();
			if (c.getType().equalsIgnoreCase(type)) {
				return c;
			}				
		}
		return null;
	}
	
	/**
	 * @return TRUE iff this parameter can take different values for each loop.	
	 * This can only occur if the operator is loopable.         
	 * @throws M4Exception
	 */
	public boolean isLoopable() throws M4Exception {
		return (this.getMyConstraintOfType(Constraint.TYPE_IS_LOOPED) != null);
	}
	
	/**
	 * @see edu.udo.cs.miningmart.m4.OpParam#isCoordinated()
	 */
	public boolean isCoordinated() throws M4Exception {
		return (this.getMyConstraintOfType(Constraint.TYPE_COORDINATED_ARRAYS) != null);
	}
	
	/** 
	 * @return TRUE iff this parameter can only take the values specified in
	 * a list. The list is given by calling <code>getOneOfList()</code>.
	 * @throws M4Exception
	 */
	public boolean isOneOf() throws M4Exception {
		return (this.getMyConstraintOfType(Constraint.TYPE_ONE_OF) != null);
	}
	
	/**
	 * @return TRUE iff this parameter is optional.
	 * @throws M4Exception
	 */
	public boolean isOptional() throws M4Exception {
		return (this.getMinArg() == 0);
	}
	
	/**
	 * @return TRUE iff this parameter takes the form of an array. FALSE iff
	 * it is a single parameter.
	 * 
	 * @throws M4Exception
	 */
	public boolean isArray() throws M4Exception {
		return (this.getMaxArg() == -1 || this.getMaxArg() > 1);
	}
	
	/**
	 * Convenience method to <code>getType()</code> and <code>getTypeS()</code>.
	 * @return TRUE iff this parameter is of type Concept.
	 */
	public boolean isConceptParameter() throws M4Exception {
		return (this.getType() == Parameter.TYPE_CONCEPT);
	}
	
	/**
	 * Convenience method to <code>getType()</code> and <code>getTypeS()</code>.
	 * @return TRUE iff this parameter is of type Value.
	 */
	public boolean isValueParameter() throws M4Exception {
		return (this.getType() == Parameter.TYPE_VALUE);
	}

	/**
	 * @see edu.udo.cs.miningmart.m4.OpParam#createValueObject()
	 */
	public Value createValueObject() throws M4Exception {
		if ( ! this.isValueParameter()) {
			throw new M4Exception("OpParam.createValueObject() for OpParam '" + 
					this.getName() + "': this is no Value parameter!");
		}
		Value myVal = (Value) this.getM4Db().createNewInstance(Value.class);
		myVal.setName(this.getName());
		myVal.setType(this.getConceptualDatatypeForValue());
		return myVal;
	}
	
	/**
	 * @see edu.udo.cs.miningmart.m4.OpParam#getConceptualDatatypeForValue()
	 */
	public String getConceptualDatatypeForValue() throws M4Exception {
		if ( ! this.isValueParameter()) {
			return null;
		}
		Constraint c; String conceptualDatatypeForValue;
		if ((c = this.getMyConstraintOfType(Constraint.TYPE_DATATYPE)) != null) {
			if ((conceptualDatatypeForValue = c.getObj2()) == null) {
				throw new M4Exception("OpParam.createValueObject() for OpParam '" + 
						this.getName() + "': Found Constraint of type " +
						Constraint.TYPE_DATATYPE + " that has no entry for the datatype!");
			}
			else {
				return conceptualDatatypeForValue;				
			}
		}
		else {
			 // use a default data type if none can be determined:
			return edu.udo.cs.miningmart.m4.ConceptualDatatypes.CDT_NOMINAL; 
		} 
	}
	
	/**
	 * Convenience method to <code>getType()</code> and <code>getTypeS()</code>.
	 * @return TRUE iff this parameter is of type Relation.
	 */
	public boolean isRelationParameter() throws M4Exception {
		return (this.getType() == Parameter.TYPE_RELATION);
	}
	
	/**
	 * Convenience method to <code>getType()</code> and <code>getTypeS()</code>.
	 * @return TRUE iff this parameter is of type Feature.
	 */
	public boolean isFeatureParameter() throws M4Exception {
		return (this.getType() == Parameter.TYPE_FEATURE);
	}
	
	/**
	 * Convenience method to <code>getType()</code> and <code>getTypeS()</code>.
	 * @return TRUE iff this parameter is of type BaseAttribute.
	 */
	public boolean isBaseAttribParameter() throws M4Exception {
		return (this.getType() == Parameter.TYPE_BASEATTRIBUTE);
	}
	
	/**
	 * Convenience method to <code>getType()</code> and <code>getTypeS()</code>.
	 * @return TRUE iff this parameter is of type MultiColumnFeature.
	 */
	public boolean isMcfParameter() throws M4Exception {
		return (this.getType() == Parameter.TYPE_MULTICOLUMNFEATURE);
	}
	
	/**
	 * This method makes sense only if <code>isOneOf()</code> returns TRUE. In that
	 * case this method returns the list of possible values that this parameter can
	 * take. A collection of Strings is returned. Otherwise an empty collection is 
	 * returned.
	 * @return a collection of Strings
	 * @throws M4Exception
	 */
	public Collection getOneOfList() throws M4Exception {
		Constraint theOneOfConstr = this.getMyConstraintOfType(Constraint.TYPE_ONE_OF);
		if (theOneOfConstr == null) {
			return new Vector();
		}
		else {
			String valuesAsList = theOneOfConstr.getObj2();
			StringTokenizer st = new StringTokenizer(valuesAsList, " ,;\t\n\r\f");
			Vector theList = new Vector();
			while (st.hasMoreTokens()) {
				theList.add(st.nextToken());
			}
			return theList;
		}
	}
	
	/**
	 * This method makes sense only if <code>isArray()</code> returns TRUE.
	 * In that case this method returns the maximum number of positions of the
	 * array, if such a maximum exists; otherwise this method returns NULL.
	 * The minimum number of array positions can be taken to be 1 if this 
	 * parameter is not optional (see method <code>isArray()</code>).
	 * 
	 * @return The maximum number of array positions or NULL if the number is unconstrained.
	 * @throws M4Exception
	 */
	public Integer getMaximumNumberOfArrayFields() throws M4Exception {
		if (this.isArray()) {
			return new Integer(this.getMaxArg());
		}
		else {
			return null;
		}
	}
}
/*
 * Historie
 * --------
 * 
 * $Log: OpParam.java,v $
 * Revision 1.4  2006/04/11 14:10:14  euler
 * Updated license text.
 *
 * Revision 1.3  2006/04/06 16:31:14  euler
 * Prepended license remark.
 *
 * Revision 1.2  2006/03/02 16:49:59  euler
 * Many bugfixes
 *
 * Revision 1.1  2006/01/03 09:54:18  hakenjos
 * Initial version!
 *
 */
