/*
 * 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.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.TreeSet;
import java.util.Vector;

import edu.udo.cs.miningmart.compiler.utils.DrawSample;

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.Value;
import edu.udo.cs.miningmart.m4.utils.Print;

/**
 * @author Martin Scholz
 * @version $Id: Apriori.java,v 1.8 2006/04/11 14:10:10 euler Exp $
 */
public class Apriori extends SingleCSOperator {

	/* name of the binary file name (without the path) */
	private static final String APRIORI_FILENAME = "apriori";

	/* Suffixes for working files - fixed by the binary. */
	private static final String RESULT_SUFFIX_SETS  = ".sets";
	private static final String RESULT_SUFFIX_RULES = ".rules";
	
	/* Names of the physical columns created by this operator: */
	private static final String NEW_COLDT_NAME         = "STRING";
	private static final short  NEW_COL_DT             = 13;
	
	private String getSampleFileName() {
		return "apriori-sample_" + this.getStep().getId();
	}

	/** @return the file name (without path) the binary writes the rule set to. */
	private String getRulesFileName() {
		return this.getSampleFileName() + RESULT_SUFFIX_RULES;
	}

	/** @return the file name (without path) the binary writes the frequent itemsets to. */
	private String getSetsFileName() {
		return this.getSampleFileName() + RESULT_SUFFIX_SETS;		
	}

	/**
	 * This method contains the control structure of this operator.
	 * 
	 * @see ConceptOperator#createSingleColumnSet(int)
	 */
	protected Columnset createSingleColumnSet(int index)
		throws M4CompilerError
	{
        try
        {
	        final String columnSetName = this.getNewCSName();

	        /* --- run wrapper: --- */
	        this.callApriori(getInputConcept().getCurrentColumnSet().getSchema() + "."
	                        + columnSetName, this.getAprioriCommand());

	        /* --- create java columnset with link to concept: --- */
	        Columnset newCS = this.createJavaColumnset(columnSetName);

	        /* --- create java columns --- */
	        this.createJavaColumns(newCS);

	        /* --- store the new table in the garbage collection table --- */
	        long   step   = this.getStep().getId();
	        String schema = getInputConcept().getCurrentColumnSet().getSchema();
	        this.getM4Db().addTableToTrash(columnSetName, schema, step);

			/* --- set the branching information of columnsets --- */
			this.setNewCSMultiStepBranch(newCS, index);
			
	        /* --- return the columnset --- */
	        return newCS;
         }
         catch (M4Exception m4e)
         {  throw new M4CompilerError("M4 interface exception in '" + this.getName() + "': " + m4e.getMessage());  }
    }

    /** 
     * Creates an M4 Columnset linked to the output Concept. 
     */
    private Columnset createJavaColumnset(String columnSetName)
    	throws M4CompilerError
    {
    	try {
        	final Columnset newCS = (Columnset) this.getM4Db().createNewInstance(edu.udo.cs.miningmart.m4.core.Columnset.class);
	        newCS.setId(0);
			newCS.setName(columnSetName);
	        final edu.udo.cs.miningmart.m4.Columnset inputColumnSet = getInputConcept().getCurrentColumnSet();
	        newCS.setSchema(inputColumnSet.getSchema());
			newCS.setType(this.getTypeOfNewColumnSet());
			newCS.setSQLDefinition(columnSetName);
	        newCS.setTheConcept(this.getOutputConcept());
			newCS.setMultiStepBranch(inputColumnSet.getMultiStepBranch());
			
			this.getStep().addToTrash(newCS);
			return (newCS);
    	}
         catch (M4Exception m4e)
         {  throw new M4CompilerError("M4 interface exception in '" + this.getName() + "': " + m4e.getMessage());  }
    }

    /** Create and link columns: */
    private void createJavaColumns(Columnset newCS)
    	throws M4CompilerError
    {
		createM4Column(this.getPremiseBA(),    newCS, this.getNewColNamePremise());
		createM4Column(this.getConclusionBA(), newCS, this.getNewColNameConclusion());
    }

    /**
     * Method to create new M4 column objects.
     * 
     * @param outputBA the base attribute of the output concept the
     *        new column should belong to
     * @param newCS the new ColumnSet created by the operator
     * @param newColumnName the name of the new column
     * @return the new Column
    */
    protected Column createM4Column
    	(BaseAttribute outputBA, Columnset newCS, String newColumnName)
		throws M4CompilerError
	{
		try {
			Column newColumn = (Column) this.getM4Db().createNewInstance(edu.udo.cs.miningmart.m4.core.Column.class);
			newColumn.setId(0);
			newColumn.setName(newColumnName);
			newColumn.setColumnset(newCS);
			newColumn.setBaseAttribute(outputBA);
			newColumn.setColumnDataType(NEW_COL_DT);
			newColumn.setColumnDataTypeName(NEW_COLDT_NAME);
			newColumn.setSQLDefinition(newColumnName);
			// outputBA.addColumn(newColumn);
			// newCS.addColumn(newColumn);
			
			this.getStep().addToTrash(newColumn);
			return newColumn;
		}
        catch (M4Exception m4e)
        {  throw new M4CompilerError("M4 interface exception in '" + this.getName() + "': " + m4e.getMessage());  }
	}

	/**
	 * @see SingleCSOperator#getTypeOfNewColumnSet()
	 */
	public String getTypeOfNewColumnSet() {
		return Columnset.CS_TYPE_TABLE; // It's a table.
	}
	
	/**
	 * This method is only implemented for the java compiler, it is never called.
	 * 
	 * @see SingleCSOperator#generateSQLDefinition(String)
	 */
	public String generateSQLDefinition(String selectPart) throws M4CompilerError
	{ return null; }
	
	/** 
	 * @see ConceptOperator#mustCopyFeature(String) 
	 */
	protected boolean mustCopyFeature(String nameOfFeature) throws M4CompilerError {
		return false; // do not copy any features
	}

	private String getNewColNamePremise() throws M4CompilerError {
		return this.getPremiseBA().getName();	
	}
	
	private String getNewColNameConclusion() throws M4CompilerError {
		return this.getConclusionBA().getName();
	}

    // ---------------------------------------------------------------------

	/**
	 * @return the Apriori directory
	 * @throws M4CompilerError if the environment variable <i>ML_HOME</i>
	 * is not set
	 * */
	private String getAprioriPath() throws M4CompilerError{
		final String runtime = System.getProperty("ML_HOME");
		if ((runtime == null) || runtime.length() == 0) {
			throw new M4CompilerError
			("Operator Apriori: Missing environment variable 'ML_HOME' !\n"
			+"Unable to find the binary!");
		}
		
		final String osName;
		{
			final String prop = System.getProperty("os.name");
		    if (prop.substring(0, 3).equalsIgnoreCase("win")) {
					osName = "Windows";	    	
	    	}
	        else if (!prop.equals("SunOS") && !prop.equals("Linux"))
    	    {
        	   throw new M4CompilerError(
				 "Apriori-Wrapper: No implementation found for your operating system.\n" +
				 "Currently supported: SunOS, Linux and Windows.\n" +
				 "Install Apriori for your Operating System and change the " +
				 "method getAprioriPath in class 'Apriori.java'." +
				 "Your operating system is: " + System.getProperty("os.name")
				 );
    	    }
    	    else osName = prop;
        }
		final String sp = File.separator;
		return (runtime + "bin" + sp + osName + sp);
	}

	/**
	 * @return full path to the Apriori binary
	 * @throws M4CompilerError if the environment variable <i>ML_HOME</i>
	 * is not set
	 * */
	private String getAprioriCommand() throws M4CompilerError {
		return this.getAprioriPath() + APRIORI_FILENAME;
	}

	/** 
	 * @return the directory specified by the environment variable TMP_DIR,
	 * if set, <code>this.getAprioriPath()</code> otherwise.
	 * */
	private String getTempDir() throws M4CompilerError {
		String tmp = System.getProperty("TEMP_DIR");
		if (tmp == null || tmp.length() == 0) {
			tmp = this.getAprioriPath();
		}
		return tmp;
	}


    // ---------------------------------------------------------------------

	/**
	 * This is the main method realizing the operator on executional level.
	 * Parameters from the table <i>PARAMETER_T</i> are used to ...<ul>
	 * <li>draw a sample of appropriate size from the input concept
	 *     and writing it to a flat file.</li>
	 * <li>start the binary with read parameters on the sample file.</li>
	 * <li>parsing the binaries output and writing the results to a new
	 *     business database table.</li>
	 * </ul>
	 * 
	 * @param newCsName the name of the new business database table
	 * @param command path to the binary to be executed
	 * @throws M4CompilerError
	 * */
	private void callApriori(final String newCsName, final String command)
		throws M4CompilerError
	{
		final String inCustCol, inTidCol, inItemCol, ttable;
		final long stepId;
		try {
			inCustCol = this.getCustomerIdBa().getCurrentColumn().getName();
			inTidCol  = this.getTransIdBa().getCurrentColumn().getName();
			inItemCol = this.getItemBa().getCurrentColumn().getName();
			stepId    = this.getStep().getId();
			ttable    = "tmp1_" + stepId; 
		
			// --- Draw a sample and write it to a temporary database table: ---
			Collection columnNames = new Vector();
			columnNames.add(inCustCol.toUpperCase());
			columnNames.add(inTidCol.toUpperCase());
			columnNames.add(inItemCol.toUpperCase());
			
			new DrawSample(this.getInputConcept().getCurrentColumnSet(),
			 			   columnNames,
						   ttable,
			       		   "tmp2_" + stepId,
						   null,
						   this.getSampleSize(),
						   null,
						   this.getM4Db()
						);
		}
        catch (M4Exception m4e)
        {  throw new M4CompilerError("M4 interface exception in '" + this.getName() + "': " + m4e.getMessage());  }
         
		// --- Move contents of the temporary table to a flat file: --- 
    	final String sampleFileName = this.getTempDir() + this.getSampleFileName();		
        final String sql_FromWhereCond = " FROM "  + ttable + " WHERE ("
                           		         + inCustCol + " IS NOT NULL) AND ("
                           		 	     + inTidCol  + " IS NOT NULL) AND ("
                           		 		 + inItemCol + " IS NOT NULL)";

		this.writeSampleToFile(sampleFileName, sql_FromWhereCond);
		try {
			this.getM4Db().dropBusinessTable(ttable);
		}
		catch (M4Exception m4e) {
			throw new M4CompilerError(m4e.getMessage());
		}
			
        // --- Call external learning algo ---
        
		this.runBinary(command, this.getTempDir(), this.getSampleFileName());
	    
	    // --- Parsing flat file with results, writing results to database table ---

	    this.doPrint(Print.OPERATOR, "Removing duplicates from association rules array.");	    
	    final String resultRulesFile = this.getTempDir() + this.getRulesFileName();
	    final String resultSetsFile  = this.getTempDir() + this.getSetsFileName();
	    Collection rules = this.parseRules(this.openResultFile(resultRulesFile));
	    this.writeResultsToTable(rules, newCsName);

		// --- Delete the temporary files. ---
		this.deleteFile(sampleFileName);
		this.deleteFile(resultRulesFile);
		this.deleteFile(resultSetsFile);
	}

    // ---------------------------------------------------------------------

	/**
	 * Method writeSampleToFile. This method reads a sample of specified size
	 * from a database table and writes it to a flat file (space delimited).
	 * 
	 * @param sampleFileName absolute path to the destination file name
	 * @param sql_FromWhereCond a substring of SQL queries for not null entries of
	 *        the target table
	 * @throws M4CompilerError
	 */
	private void writeSampleToFile(String sampleFileName, String sql_FromWhereCond)
		throws M4CompilerError
	{
        final String sql_getData = "SELECT * " + sql_FromWhereCond;
		
		ResultSet rs = null;
		PrintWriter out = null;
		try {
			rs = this.getM4Db().executeBusinessSqlRead(sql_getData);
			
			{
				FileWriter fw = new FileWriter(sampleFileName);
				out = new PrintWriter(new BufferedWriter(fw));
			}
			while (rs.next()) {
				String custId = rs.getString(1);
				String TidId = rs.getString(2);
				String Item = rs.getString(3);
				out.println(custId + " " + TidId + " " + Item);
			}
		}
		catch (SQLException sqle) {
			throw new M4CompilerError(
				"Error in Apriori Wrapper when trying to read a sample from DB:\n"
					+ sqle.getMessage());
		}
		catch (IOException e) {
			throw new M4CompilerError(
				"Error in Apriori Wrapper when trying to write a DB sample to file:\n"
					+ e.getMessage());
		}
		finally {
			DB.closeResultSet(rs);
			out.close();
		}
	}

	/**
	 * This method executes the Apriori binary in a given directory.
	 * @param command the command (just the path to the binary) to be executed
	 * @param execDir the directory where to run the binary
	 * @param dataFileName the name of the file with the data sample.
	 * This file should be located in directory execDir, so no extra path
	 * information preceding the plain file name should be necessary.
	 *  */
	private void runBinary(String command, String execDir, String dataFileName)
        throws M4CompilerError	
	{
		String[] cmdArray = { command,
							  this.getSampleFileName(),
							  Long.toString(this.getMinSupport()),
						      Double.toString(this.getMinConfidence()),
						      "-ascii",
						      "-print"
						    };
	
        Process process = null;
  		this.doPrint(Print.OPERATOR, "Starting external algorithm (Apriori) "
								   + "(command: '" + command + "')...");
        try	{
			process =
				Runtime.getRuntime().exec(cmdArray, null, new File(execDir));
			int exitCode;

			this.doPrint(Print.MIN, "\n--- Apriori binary output: ---");
			BufferedReader in =
				new BufferedReader(
					new InputStreamReader(process.getInputStream()));
			String line;
			while ((line = in.readLine()) != null) {
				this.doPrint(Print.MIN, line);
			}
			this.doPrint(Print.MIN, "------------------------------\n");
			BufferedReader err =
				new BufferedReader(
						new InputStreamReader(process.getErrorStream()));
			while ((line = err.readLine()) != null) {
					this.doPrint(Print.MIN, line);
				}
			try {
				process.waitFor();
				exitCode = process.exitValue();
	        	this.doPrint(Print.OPERATOR, "Call to " + command + " exited with"
		        	+ ((exitCode == 0) ? "out errors." : (" error code " + exitCode)));
        	}
        	catch (IllegalThreadStateException e) {
	            throw new M4CompilerError
    	        ("IllegalThreadStateException caught after Apriori has closed stdout.\n"
    	        + e.getMessage());
    	    }
        	catch (InterruptedException e) {
	            throw new M4CompilerError
    	        ("InterruptionException caught after Apriori has closed stdout.\n"
    	        + e.getMessage());
    	    }
        }
		catch (IOException ioe) {
            throw new M4CompilerError
            ("Error accessing Apriori binary: " + ioe.getMessage());
		}
		finally {
		    if (process != null) {
		    	process.destroy();
		    }	
		}
	}


	private Collection parseRules(BufferedReader in)
		throws M4CompilerError
	{	
		TreeSet hs = new TreeSet();
		String line;
		try {
			try {
				while ((line = in.readLine()) != null) {
					int[][] rule = this.readRuleFromLine(line);
					int[] pr = rule[0];
					int[] ant = rule[1];
					StringBuffer sbuf = new StringBuffer("('");
					{
						for (int i = 0; i < pr.length - 1; i++) {
							sbuf.append(pr[i] + " ");
						}
						if (pr.length > 0) {
							sbuf.append(pr[pr.length - 1]);
						}
						sbuf.append("', '");
						for (int i = 0; i < ant.length - 1; i++) {
							sbuf.append(ant[i] + " ");
						}
						if (ant.length > 0) {
							sbuf.append(ant[ant.length - 1]);
						}
						sbuf.append("')");
					}
					hs.add(sbuf.toString());
				}
			}
			finally {
				in.close();
			}
			return hs;
		}
		catch (IOException e) {
			throw new M4CompilerError(
				"Apriori-Wrapper: Could not read from successfully opened result file"
					+ " after call to binary.\nException message is:\n"
					+ e.getMessage());
		}		
	}
	
	private void writeResultsToTable(Collection rules, String tableName)
		throws M4CompilerError
	{
		try {
				this.createTable(tableName);
				final String insertPrefix =
					"INSERT INTO " + tableName + " (" + getNewColNamePremise()
					+ ", " + this.getNewColNameConclusion() + ") VALUES ";
				final Iterator it = rules.iterator();
				while (it.hasNext()) {
					String sqlCommand = insertPrefix + (String) it.next();
					this.getM4Db().executeBusinessSqlWrite(sqlCommand);
				}
				this.getM4Db().commitBusinessTransactions();
				this.doPrint(Print.OPERATOR,
					"Results were successfully written to database table '"
					+ tableName + "'.");
		}
		catch (SQLException e) {
			throw new M4CompilerError
			("An exception occured when trying to write Apriori results to a "
			+ "database table:\n" + e.getMessage());
		}		
	}
	
	private void createTable(String tableName)
		throws M4CompilerError
	{
		try {
			/* First of all: Drop table, if it already exists: */
			this.getM4Db().dropBusinessTable(tableName);
					
			/* Then create the new table: */
			String cmd = "CREATE TABLE " + tableName + " ("
				+ getNewColNamePremise()    + " VARCHAR(1000), "
				+ getNewColNameConclusion() + " VARCHAR(1000) )";
			this.getM4Db().executeBusinessSqlWrite(cmd);				
		}
		catch (SQLException e)
		{   throw new M4CompilerError("SQL error when creating table for " + this.getName() + ": " + e.getMessage());  }
		catch (M4Exception m4e) {
			throw new M4CompilerError(m4e.getMessage());
		}
	}

	/**
	 * This method conveniently opens a file and translates exceptions into
	 * <code>M4CompilerError</code>s
	 * @param fileName the name and path of the line to open
	 * @return a <code>BufferedReader</code> for the file
	 * @throws M4CompilerError
	 */
	private BufferedReader openResultFile(String fileName) throws M4CompilerError {
		try {
			return new BufferedReader(new FileReader(fileName));
		} catch (FileNotFoundException e) {
			throw new M4CompilerError
			("Apriori Wrapper: Could not open result file '" + fileName +
			 "' after execution of binary.\nException message is:\n" + e.getMessage());	
		}
	}
	
	/**
	 * @param line a whitespace delimited line of integer values,
	 *        starting with '{' and ending with '}'.
	 * @return the values as an array of <code>int</int>
	 */
	private int[] readSetFromLine(String line) {
		final String setString;
		{
			int start, stop;
			if (  line == null
			   || (start = line.indexOf('{')) == -1 
			   || (stop = line.indexOf('}', start)) == -1)
			{ return null; }
			setString = line.substring(start + 1, stop);
		}
		Vector v = new Vector();
		int i = 0;
		do {
			while (i<setString.length() && Character.isWhitespace(setString.charAt(i)))
				i++;
			int j = i;
			while (j<setString.length() && !Character.isWhitespace(setString.charAt(j)))
				j++;
			if (j > i) {
				v.add(setString.substring(i, j));
				i = j;
			}
		} while (i<setString.length());

		int[] r = new int[v.size()];
		i = 0;
		Iterator it = v.iterator();
		while (it.hasNext()) {
			r[i++] = Integer.parseInt((String) it.next());
		}
		Arrays.sort(r);
		return r;
	}

	/**
	 * @param  line a line of two sets of whitespace delimited integer values.
	 *         Both sets are in brackets ( '{' and '}' and between the two sets
	 *         there is a &quot;-&gt;&quot; as a delimiter.
	 * @return both sets' values as arrays of <code>int</int>, so we effectively
	 *         have an object of type <code>int[2][]</code>.
	 */
	private int[][] readRuleFromLine(String line) {
		int index;
		if (  line == null
		   || (index = line.indexOf("->")) == -1)
		{ return null; }
		final String left  = line.substring(0, index);
		final String right = line.substring(index + 2);
		int[][] r = new int[2][];
		r[0] = this.readSetFromLine(left);
		r[1] = this.readSetFromLine(right);
		return r;
	}
	
	/**
	 * Helper method to remove temporary files from the file system. 
	 * @param fileName the complete path to a file to be deleted
	 */
	private void deleteFile(String fileName) {	
		File f = new File(fileName);
		f.delete();
	}
	
    // ---------------------------------------------------------------------

	/**
	 * Parameter getter.
	 * 
	 * @return BaseAttribute as specified by paramter "CustID"
	 */
	public BaseAttribute getCustomerIdBa() throws M4CompilerError {
		return (BaseAttribute) this.getSingleParameter("CustID");
	}

	/**
	 * Parameter getter.
	 * 
	 * @return BaseAttribute as specified by paramter "TransID"
	 */
	public BaseAttribute getTransIdBa() throws M4CompilerError{
		return (BaseAttribute) this.getSingleParameter("TransID");
	}

	/**
	 * Parameter getter.
	 * 
	 * @return BaseAttribute as specified by parameter "Item"
	 */
	public BaseAttribute getItemBa() throws M4CompilerError {
		return (BaseAttribute) this.getSingleParameter("Item");
	}

	/**
	 * Parameter getter.
	 * 
	 * @return value of parameter "MinSupport"
	 */
	public long getMinSupport() throws M4CompilerError {
		Value v = (Value) this.getSingleParameter("MinSupport");
		Long l;
		if (v != null && (l = v.getLong()) != null && l.longValue() >= 0) {
			long minSup = l.longValue();
			return minSup;
		}
		else {
			throw new M4CompilerError
			("Operator Apriori: Parameter 'MinSupport' must be a positive integer!\n"
			+ "Found: " + ((v == null) ? "<null>" : v.getValue()));
		}
	}

	/**
	 * Parameter getter.
	 * 
	 * @return value of parameter "MinConfidence"
	 */
	public double getMinConfidence() throws M4CompilerError {
		Value v = (Value) this.getSingleParameter("MinConfidence");
		Double d;
		if (v != null && (d = v.getDouble()) != null 
		   && 0 <= d.doubleValue() && d.doubleValue() <= 1)
		{
			return d.doubleValue();
		}
		else {
			throw new M4CompilerError
			("Operator Apriori: Parameter 'MinConfidence' must be a double value in [0, 1] !\n"
			+ "Found: " + ((v == null) ? "<null>" : v.getValue()));
		}
	}

	/**
	 * Parameter getter.
	 * 
	 * @return value of parameter "SampleSize"
	 */
	public long getSampleSize() throws M4CompilerError {
		Value v = (Value) this.getSingleParameter("SampleSize");
		Long l;
		if (v != null && (l = v.getLong()) != null && l.longValue() > 0) {
			return l.longValue();
		}
		else throw new M4CompilerError
		("Operator Apriori: Parameter 'SampleSize' must be a positive integer!\n"
		+ "Found: " + ((v == null) ? "<null>" : v.getValue()));	
	}

	/**
	 * Parameter getter.
	 * 
	 * @return BaseAttribute as specified by parameter "PremiseBA"
	 */
	public BaseAttribute getPremiseBA() throws M4CompilerError {
		return (BaseAttribute) this.getSingleParameter("PremiseBA");
	}
	
	/**
	 * Parameter getter.
	 * 
	 * @return BaseAttribute as specified by parameter "ConclusionBA"
	 */
	public BaseAttribute getConclusionBA() throws M4CompilerError {
		return (BaseAttribute) this.getSingleParameter("ConclusionBA");
	}	
}
/*
 * $Log: Apriori.java,v $
 * Revision 1.8  2006/04/11 14:10:10  euler
 * Updated license text.
 *
 * Revision 1.7  2006/04/06 16:31:10  euler
 * Prepended license remark.
 *
 * Revision 1.6  2006/03/30 16:07:13  scholz
 * fixed author tags for release
 *
 * Revision 1.5  2006/03/29 09:50:47  euler
 * Added installation robustness.
 *
 * Revision 1.4  2006/03/23 11:13:45  euler
 * Improved exception handling.
 *
 * Revision 1.3  2006/01/06 16:28:50  euler
 * Bugfixes
 *
 * Revision 1.2  2006/01/05 10:27:38  hakenjos
 * Removed Javadoc Warnings!
 *
 * Revision 1.1  2006/01/03 09:54:20  hakenjos
 * Initial version!
 *
 */
