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

import java.sql.SQLException;
import java.util.Iterator;
import java.util.Vector;

import edu.udo.cs.miningmart.compiler.utils.DrawSample;
import edu.udo.cs.miningmart.db.CompilerDatabaseService;
import edu.udo.cs.miningmart.exception.M4CompilerError;
import edu.udo.cs.miningmart.exception.M4Exception;
import edu.udo.cs.miningmart.m4.Column;
import edu.udo.cs.miningmart.m4.Columnset;
import edu.udo.cs.miningmart.m4.utils.Print;

/**
 * The wrapper class for mySVM/db, the version of mySVM that runs
 * inside the database. The wrapper assumes that mySVM/db is already
 * installed in the database.
 * 
 * @author Timm Euler
 * @version $Id: DB_SVM.java,v 1.4 2006/04/11 14:10:17 euler Exp $
 */
public abstract class DB_SVM extends SVM_Wrapper{

	private String defaultGamma = "0,1";      // used if kernel type is 'radial'  
	private boolean viewWasCreated = false;	
	private String keyCol = null;
	                        
    /**
     * Use this constructor if no sampling is wanted. The connections to the databases 
     * are needed because the SVMs need to read metadata as well as data and create an
     * intermediate table in the database.
     *
     * @param databaseObj the DB object to be used for database connections
     * @param printObj the Print object to be used for log messages
     * @param nameForDatabaseUse Prefix for all functions, view and tables that
     * 	      this wrapper creates in the database
     * @param nameOfDatabaseSchema Name of the database schema that holds the business 
     *        views and tables
     * @param stepId Id of the step
     * @param colNameKeyColumn Name of the column that is the primary key for the input data table
     *
     * @throws M4CompilerError A simple exception object with an error message.
     */ 
    public DB_SVM( CompilerDatabaseService databaseObj, 
    			   Print printObj, 
    			   String nameForDatabaseUse, 
    			   String nameOfDatabaseSchema,
    			   long stepId,
    			   String colNameKeyColumn) 
    	   throws M4CompilerError
    {  
    	super(databaseObj, printObj, nameForDatabaseUse, nameOfDatabaseSchema, stepId);  
    	this.keyCol = colNameKeyColumn;
    	this.sampleSize = -1;
    }      
          
    /**
     * Use this constructor if sampling on the input data is wanted. The connections to the databases 
     * are needed because the SVMs need to read metadata as well as data and create an
     * intermediate table in the database.
     *
     * @param databaseObj the DB object to be used for database connections
     * @param printObj the Print object to be used for log messages
     * @param nameForDatabaseUse Prefix for all functions, view and tables that
     * 	      this wrapper creates in the database
     * @param nameOfDatabaseSchema Name of the database schema that holds the business 
     *        views and tables
     * @param stepId Id of the step
     * @param colNameKeyColumn Name of the column that is the primary key for the input data table
     * @param sampleSize maximum number of rows to be used to train the SVM
     *
     * @throws M4CompilerError A simple exception object with an error message.
     */ 
    public DB_SVM( CompilerDatabaseService databaseObj, 
    			   Print printObj, 
    			   String nameForDatabaseUse, 
    			   String nameOfDatabaseSchema,
    			   long stepId,
    			   String colNameKeyColumn,
    			   long sampleSize) 
    	   throws M4CompilerError
    {  
    	this(databaseObj, printObj, nameForDatabaseUse, nameOfDatabaseSchema, stepId, colNameKeyColumn);
    	this.sampleSize = sampleSize;
    }

	/**
     * This is the wrapper. This method is used to call the mySVM/db
     * algorithm in the database for training with the specified parameters. 
     *
     * @param inputColumnset ColumnSet in the database
     *        that belongs to the input Concept of the
     *        operator that uses this wrapper. The Columns that the SVM learns
     *        from belong to this ColumnSet.
     * @param targetColumn the Column in the database that
     *        belongs to the target attribute of the operator that uses this
     *        wrapper.
     * @param conceptId Unique M4 Id of the input Concept of the operator that
     *        uses this wrapper.
     * @param c The parameter C for the support vector machine.
     * @param kernelType One of "dot" or "radial".
     * @param epsilon The parameter epsilon for the support vector machine.
     * @param forClassification If TRUE, the SVM is used in classification mode;
     * 		  if FALSE, it is used in regression mode.
     * @param predictingColumns A Vector with the Columns in the database that the support
     *        vector machine will use as learning attributes.
     * 
     * @throws M4CompilerError A simple exception object with an error message.
     */
	 public void callSVM( Columnset inputColumnset,
			              Column targetColumn,
			              long conceptId,
		                  String c,
				          String kernelType,
				          String epsilon,
				          Vector predictingColumns
			           ) throws M4CompilerError
    {	
        // The wrapper has the following tasks.        
        
        // Step 1. To be able to read the training data,
        //         check and convert datatypes where the learning algo requires it.

        // add columns which need to be converted to a Vector:

        // final Vector convertThisColumn = new Vector();
        // String[] columns = 
        checkForConversion( inputColumnset,
        					predictingColumns,
                            targetColumn);

		// must have at least one predicting attr. and the target attr.:                                          
        // if ((columns == null) || (columns.length < 2))
	    // {   throw new M4CompilerError("Error in DB-SVM Wrapper. No business data read.");  }

        // Step 2. Compose the input table for mySVM/db.
        /*
        Vector colNames = new Vector();
        for (int i = 0; i < predictingColumns.size(); i++)
        {   colNames.add(((Column) predictingColumns.get(i)).getName());  }
        colNames.add(targetColumn);
        */
        makeParameterTable( inputColumnset,
        					c,
        					epsilon,
        					kernelType);
        
        // Step 3. Call mySVM/db.
        String[] procedureParams = new String[] { CompilerDatabaseService.quote(this.getParTableName()) };
        
        // boolean useMySVMoutsideDB = false;  // for debugging; should be false!
        /*
        if (useMySVMoutsideDB)
        {
        	String[] args = new String[4];
        	args[0] = this.getParTableName();
        	args[1] = "db_url:" + this.getDatabaseObj().getDataUrl();
        	args[2] = "db_login:" + this.getDatabaseObj().getDataUser();
        	args[3] = "db_password:" + this.getDatabaseObj().getDataPw();
        	
        	try
        	{
	        	// edu.udo.cs.mySVMdb.Learn.learn(args);
        	}
        	catch (Exception e)
        	{  throw new M4CompilerError("DB-SVM wrapper: could not execute mySVM/db outside DB, exception: " + e.getMessage());  }
        }
        else */
        {
	        this.getDatabaseObj().executeDBProcedure("mySVM", procedureParams, true); 
        }
            
        // mySVM/db has created new db objects:
        // this.getDatabaseObj().addViewToTrash(this.getOutputViewName(), this.schema, this.myStepId);
		this.getDatabaseObj().addTableToTrash(this.getModelTableName(), this.schema, this.myStepId);
		this.getDatabaseObj().addTableToTrash(this.getLogTableName(), this.schema, this.myStepId);
		
        // Step 4. Create SQL decision function
		this.readSVMParameterB();
		
		if (this.forClassification)  // no XiAlpha estimation for regression!
		{
			this.extractXiAlpha();
		}
		this.extractNumberSV();
		
		Vector colNamesPredictingAttributes = new Vector();
        for (int i = 0; i < predictingColumns.size(); i++)
        {   colNamesPredictingAttributes.add(((Column) predictingColumns.get(i)).getName());  }
        
		this.createDecisionFunctionAsSQL_Function(kernelType);
    } // end public void callSVM   
    
    private void makeParameterTable( Columnset inputColumnset,
        					         String c,
        					   		 String epsilon,
        					  		 String kernelType)
        		throws M4CompilerError
    {
    	String parTableName = this.getParTableName();
    	// String outputViewName = this.getOutputViewName();
    	String modelName = this.getModelTableName();
    	String logTableName = this.getLogTableName();
    	
    	checkKernel(kernelType);
    	c = checkDouble(c);
    	epsilon = checkDouble(epsilon);
    	if ( ! this.forClassification) {
	    	this.lossPos = checkDouble(this.lossPos);
	    	this.lossNeg = checkDouble(this.lossNeg);
    	}
    	
    	boolean inputIsView = false;
    	String inputViewName = null;
    	
    	if (shouldCreateInputView()) {   
    		inputIsView = true;
    		inputViewName = createInputView(inputColumnset);  
    	}
    	
    	createParameterTable(parTableName);
    	
    	Vector theKernelParams = this.getKernelParams(kernelType);
    	
    	// to be changed as soon as more kernel types than just dot and radial are allowed:
  		if ((theKernelParams != null) && (! theKernelParams.isEmpty()))
  		{   String g = (String) theKernelParams.get(0);
  			this.defaultGamma = g.substring(g.indexOf(" ") + 1);
  		}
    	
    	try
    	{
    		final CompilerDatabaseService db = this.getDatabaseObj();
    		
    		if (inputIsView)
    		{  db.executeBusinessSqlWrite(insertionString(parTableName, "trainset", inputViewName));  }
    		else
    		{  db.executeBusinessSqlWrite(insertionString(parTableName, "trainset", inputTableName));  }
    		
    		db.executeBusinessSqlWrite(insertionString(parTableName, "verbosity", "0"));
    		if (forClassification)
    		{
	    		db.executeBusinessSqlWrite(insertionString(parTableName, "svm_type", "pattern"));
	    		if ((this.targetPositive != null) && ( ! this.targetPositive.equals("")))
	    		{   db.executeBusinessSqlWrite(insertionString(parTableName, "target_concept", this.targetPositive));  }
    		}
    		else
    		{   db.executeBusinessSqlWrite(insertionString(parTableName, "svm_type", "regression")); }
    		db.executeBusinessSqlWrite(insertionString(parTableName, "kernel:type", kernelType));
    		if (kernelType.equalsIgnoreCase("radial"))
    		{  db.executeBusinessSqlWrite(insertionString(parTableName, "kernel:gamma", this.defaultGamma)); }
    		
    		db.executeBusinessSqlWrite(insertionString(parTableName, "c", c));
    		db.executeBusinessSqlWrite(insertionString(parTableName, "epsilon", epsilon));
    		
    		if ( ! this.forClassification)
			{
				db.executeBusinessSqlWrite(insertionString(parTableName, "Cpos", this.lossPos));
	    		db.executeBusinessSqlWrite(insertionString(parTableName, "Cneg", this.lossNeg));
			}
    	
    		// predicting attributes:
    		if (inputIsView)
	    	{	for (int i = 0; i < this.theColumnInfos.length - 1; i++)
    			{   db.executeBusinessSqlWrite(insertionString(parTableName, "x_column", "X" + i));  }
	    	}
	    	else
	    	{	for (int i = 0; i < this.theColumnInfos.length - 1; i++)
    			{   db.executeBusinessSqlWrite(insertionString(parTableName, "x_column", this.theColumnInfos[i].getColumnName()));  }
	    	}
	    	    		
    		// target attribute:
    		if (inputIsView)
			{  db.executeBusinessSqlWrite(insertionString(parTableName, "y_column", "Y"));  }
			else
			{  db.executeBusinessSqlWrite(insertionString(parTableName, "y_column", this.theColumnInfos[this.theColumnInfos.length-1].getColumnName()));  }
    		
    		// add view name for output (not a must)
    		// stmt.addBatch(insertionString(parTableName, "view_name", outputViewName));
    		
    		// add model name for svm-model table
    		db.executeBusinessSqlWrite(insertionString(parTableName, "model_name", modelName));
    		
    		// add key column if it is given:
    		if (this.keyCol != null)
    		{   db.executeBusinessSqlWrite(insertionString(parTableName, "key_column", this.keyCol));  }
    		
    		// add info that xiAlpha value is wanted:
    		db.executeBusinessSqlWrite(insertionString(parTableName, "xi_alpha", ""));
    		
    		// add table name for a table with log information
    		db.executeBusinessSqlWrite(insertionString(parTableName, "log_name", logTableName));    		
    		
    		db.commitBusinessTransactions();
    	}
    	catch (SQLException sqle)
    	{ throw new M4CompilerError("DB-SVM wrapper: could not fill parameter table '" + parTableName +
    		                        "' for mySVM/db; SQL exception: " + sqle.getMessage()); 
    	}    	
    } // end private void makeParameterTable
    
    private void createParameterTable(String name) throws M4CompilerError
    {
    	try
    	{
    		// drop the table first if it exists
    		try {
    			this.getDatabaseObj().dropBusinessTable(name);
    		}
    		catch (M4Exception m4e) {
    			throw new M4CompilerError(m4e.getMessage());
    		}
    		
	    	String query = "CREATE TABLE " + name + 
	    	               "(PARAMETER VARCHAR2(100) NOT NULL, VALUE VARCHAR2(100))";
	    	this.getDatabaseObj().executeBusinessSqlWrite(query);
    	}
    	catch (SQLException sqle)
    	{ throw new M4CompilerError("DB-SVM wrapper: could not create parameter table '" + name +
    		                        "' for mySVM/db; SQL exception: " + sqle.getMessage()); 
    	}
    	// add table to trash:
    	this.getDatabaseObj().addTableToTrash(name, this.schema, this.myStepId);
    } // end private void createParameterTable
    
    /* This method decides whether an extra view on the input data 
     * should be created for mySVM/db. 
     * This is necessary when 
     * a) sampling on the input data is needed
     * b) the column definitions are complex (start with "(" etc.), or 
     * c) non-numeric columns must be converted
     * d) the target column contains missing values
     * Since this extra view means a performance loss, it should only be used when necessary.
     */
    private boolean shouldCreateInputView()
    {
    	// is sampling needed?
    	if (this.sampleSize > -1)
    	{  return true;  }
    	
    	for (int i = 0; i < this.theColumnInfos.length; i++)
    	{
    		// if a column must be converted, a view is needed:
    		if (this.theColumnInfos[i].mappingIsUsed())
    		{  return true;  }
    		
    		// if a column definition is complex, a view is needed:
    		if ( ! Character.isLetter(this.theColumnInfos[i].getSelectString().charAt(0)))
    		{  return true;  }
    		
    		// if a column definition uses a function, a view is needed
    		if (this.theColumnInfos[i].getSelectString().startsWith("to_number"))
    		{  return true;  }
    		
    		if (this.theColumnInfos[i].hasMissingValues()) {
    			return true;
    		}
    	}  	
    	
		return false;    	
    } // end private boolean shouldCreateInputView
    
    // this method creates a view on the input data which uses simple
    // column names: x1, x2 etc., plus y for the target
    private String createInputView(Columnset inputColumnset) throws M4CompilerError {
    	String inViewName = this.getInputViewName();    	
    	
    	createNominalConversionFunctions();
    									  
    	try
    	{
    		this.getDatabaseObj().dropBusinessTable(inViewName);
    		
			final String ttable = SAMPLE_TABLE_PREFIX + this.myStepId;	
			boolean useSample = (this.sampleSize > -1);
			Vector allColNames = new Vector();
			for (int i = 0; i < this.theColumnInfos.length; i++) {
				allColNames.add(this.theColumnInfos[i].getColumnName());
			}
    		if (useSample)
    		{	
    			new DrawSample( inputColumnset,
			 			        allColNames,
						        ttable,
			       			 	TEMP_TABLE_PREFIX + this.myStepId,
								null,
								sampleSize,
								null,
								this.getDatabaseObj()
			           		   );
			    this.getDatabaseObj().addTableToTrash(ttable, inputColumnset.getSchema(), this.myStepId);
    		}
	    	String query = "CREATE TABLE " + inViewName + " AS (";
	    	
	    	// create select statement:
	    	String select = "SELECT ";
	    	for (int i = 0; i < this.theColumnInfos.length; i++)
		    {   
	    		boolean thisIsTargetColumn = (i == this.theColumnInfos.length - 1);
	    		String alias = (thisIsTargetColumn ? ("X" + i + ", ") : "Y");
		    	if (this.theColumnInfos[i].mappingIsUsed())	{
		    		select += this.getConversionFunctionName(i) + "(" +
		    		         this.theColumnInfos[i].getSelectString() + ") AS " + alias;
		    	}
		    	else {
		    		select += this.theColumnInfos[i].getSelectString() + " AS " + alias;  
		    	}		
		    }	    	
	    	
	    	// add key:
	    	if (this.keyCol == null)
	    	{  throw new M4CompilerError("DB-SVM: need to construct input view, but found no key column for input table!");  }
	    	
	    	select += ", " + this.keyCol;
	    	
	    	if (useSample)
	    	{   query += select + " FROM " + ttable; 	}
	    	else    	
	    	{   query += select + " FROM " + inputTableName;  	}	    	   	
	    	
	    	query += " WHERE " + this.theColumnInfos[this.theColumnInfos.length - 1].getSelectString() + " IS NOT NULL)";
	    	
	    	this.getDatabaseObj().executeBusinessSqlWrite(query);
    	}
    	catch (SQLException sqle)
    	{ throw new M4CompilerError("DB-SVM wrapper: could not create inputView '" + inViewName +
    		                        "' for mySVM/db; SQL exception: " + sqle.getMessage()); 
    	}
    	catch (M4Exception m4e) {
    		throw new M4CompilerError(m4e.getMessage());
    	}
    	
    	// add table to trash:
    	this.getDatabaseObj().addTableToTrash(inViewName, this.schema, this.myStepId);
    	
    	this.viewWasCreated = true;
    	
    	return inViewName;
    } // end private String createInputView	
    
    private String insertionString(String tableName, String paramName, String value) 
    {
    	return ("insert into " + tableName + " values ('" + paramName + 
    	        "', '" + value + "')");
    } // end private void insertionString    
    
    protected void checkColSetType(String type) throws M4CompilerError
    {
    	if (type.equals(Columnset.CS_TYPE_VIEW) && (this.keyCol == null))
    	{  throw new M4CompilerError("DB-SVM wrapper: No parameter 'TheKey' found, but input columnset is View!"); }
    }
    
    // implements the abstract method from the superclass
    protected void checkKernel(String kern) throws M4CompilerError
    {
        if ( ! ( kern.equalsIgnoreCase("dot") ||
                 kern.equalsIgnoreCase("radial") ) )
	    {   throw new M4CompilerError("DB-SVM wrapper: unknown or disallowed kernel type: " + kern + "!");  }
        return;
    } // end protected void checkKernel  
    
    // implements the abstract method from the superclass
    protected Vector getColNamesInput() {
    	Vector inputNames = new Vector(this.theColumnInfos.length - 1);
        for (int i = 0; i < this.theColumnInfos.length - 1; i++) {
        	if (this.viewWasCreated) {
        		inputNames.add(("X" + i));
        	}
        	else {
        		inputNames.add(this.theColumnInfos[i].getColumnName());
        	}
	    }	    		    	
	    return inputNames;
    }
    
    // implements the abstract method from the superclass
    protected String getModelTablePlusCondition() throws M4CompilerError
    {
    	String s = "";
    	if (this.viewWasCreated)
		{    
			s = this.getInputViewName() + ", " +
				this.getModelTableName() + " WHERE " + 
				this.getInputViewName() +  "." + this.keyCol + " = " + 
    		    this.getModelTableName() + ".KEY";
		}
		else
		{
			String key = null;
			if (this.keyCol != null) {  
				key = this.keyCol;  
			}
			else {  
				if (this.usePostgres) {   
					throw new M4CompilerError("DB_SVM: Cannot work on input table without specified key under Postgres!");  
				}
				key = "ROWID";  // this is the Oracle pseudo-key used by mySVM/db
			} 
			
			s = this.inputTableName + ", " +
			    this.getModelTableName() + " WHERE " + 
				this.inputTableName + "." + key + " = " + 
    		    this.getModelTableName() + ".KEY";
		}
		return s;
    }
    
    private void createNominalConversionFunctions() throws M4CompilerError {
    	try	{    		
	    	for (int i = 0; i < this.theColumnInfos.length; i++) {
    			if (this.theColumnInfos[i].mappingIsUsed())	{   
			    	
			    	// Value[] vs = this.getDatabaseObj().getDistinctElements(colid);			    	
					
					String conversionFunction = createConversionFunction( this.theColumnInfos[i], 
																		  this.schema,
																		  this.getConversionFunctionName(i));
					this.getDatabaseObj().executeBusinessSqlWrite(conversionFunction);
					this.getDatabaseObj().addFunctionToTrash( this.getConversionFunctionName(i), 
							                                  this.schema, 
															  this.myStepId);
    			}
    		}
    	}
    	catch (SQLException sqle)
    	{  throw new M4CompilerError("DB-SVM wrapper: could not create boolean conversion functions, " +
    		 					     "SQL exception: " + sqle.getMessage());  
    	}
    } // end private void createNominalConversionFunctions
    
    private String createConversionFunction( ColumnInfo ci, 
    										 String schema,
    										 String functionName)
    {
    	String theFunction = "CREATE OR REPLACE function ";
    	if (this.useOracle)
    	{  
    		theFunction += functionName + "\n(\n";    		  
			theFunction += "X IN VARCHAR"; 
	        theFunction += "\n)\nRETURN NUMBER \nAS\n";
	        theFunction += "BEGIN\n";
    	}
    	if (this.usePostgres)
    	{  
    		theFunction += schema + "." + functionName + "\n(\n"; 
    		theFunction += "NUMERIC";  
	        theFunction += "\n)\nRETURNS NUMERIC \nAS '\n";
    	}
        
        theFunction += "  DECLARE\n";
        
        if (this.useOracle)
        {   theFunction += "    retValue NUMBER;\n";   }
        if (this.usePostgres)
        {  
        	theFunction += "    X ALIAS FOR $1;\n";  
            theFunction += "    retValue NUMERIC;\n";
        }
        
        theFunction += "  BEGIN\n";
//		
//		String posValue = theSymbols[0].getValue();
//		String negValue = theSymbols[1].getValue();
		
		// if this is the target column and the positive value is given,
		// make sure it gets 1 instead of -1:
        /*
		if (isTarget && this.forClassification && (this.targetPositive != null))
		{   
			if ( ! this.targetPositive.equalsIgnoreCase(posValue))
			{   String a = posValue;
				posValue = negValue;
				negValue = a;
			}
			if ( ! this.targetPositive.equalsIgnoreCase(posValue))
			{ throw new M4CompilerError("SVM: Positive Target Value '" +
										this.getPositiveTargetValue() +
										"' not found in target attribute!"); }
		}
		*/
        if (ci.mappingIsUsed()) { // should be the case! otherwise why convert?
        	Iterator it = ci.getValuesIterator();
        	boolean first = true;
        	while (it.hasNext()) {
        		String value = (String) it.next();
        		String command = (first ? "IF" : "ELSIF");
        		first = false;
        		theFunction += "    " + command + " X = '" + value + "'\n" +
				               "      THEN retValue := " + ci.getMappedValue(value) + ";\n";				
        	}
        	theFunction += "    END IF;\n";
        }

        theFunction += "    RETURN retValue;\n";  

        theFunction += "  END;\n"; // end body
        if (this.useOracle)
        {   theFunction += "END;\n";   } // end function (declaration + body)  
        if (this.usePostgres) {
			// in postgres PL/Sql, only the body has a BEGIN;
			// so we need no second END but other funny stuff
			theFunction += "' LANGUAGE 'plpgsql';";	 
        }

        return theFunction;
    } // end private String createConversionFunction
    
    private String getConversionFunctionName(int index)
    {   return (this.getDecisionFunctionName() + index);  }
    
    // read the parameter b for the decision function
    private void readSVMParameterB() throws M4CompilerError
    {
    	try
    	{
    		String query = "SELECT alpha FROM " + this.getModelTableName() +
    					   " WHERE key IS NULL";
			String valueD = this.getDatabaseObj().executeBusinessSingleValueSqlRead(query);
			if (valueD != null) {
				this.b = Double.parseDouble(valueD);
    		}
    		else
    		{  throw new M4CompilerError("DB-SVM wrapper: could not find parameter b in SVM output!" +
    									 " Tried to check in table: " + this.getModelTableName());  }
    	}
    	catch (SQLException sqle)
    	{  throw new M4CompilerError("DB-SVM wrapper: error trying to read parameter b: " + sqle.getMessage());  }	
    	catch (NumberFormatException nfe)
    	{  throw new M4CompilerError("DB-SVM wrapper: error trying to read parameter b: " + nfe.getMessage());  }	
    } 
    
    // read the number of support vectors
    // (implements the method from the superclass)
    protected void extractNumberSV() throws M4CompilerError
    {
    	try
    	{
    		String query = "SELECT COUNT(*) FROM " + this.getModelTableName() +
    					   " WHERE key IS NOT NULL";
			Long count = this.getDatabaseObj().executeBusinessSingleValueSqlReadL(query);
    		if (count != null)
    		{
				this.noSV = count.intValue();
    		}
    		else
    		{  throw new M4CompilerError("DB-SVM wrapper: could not extract number of support vectors in SVM output!" +
    									 " Tried to check in table: " + this.getModelTableName());  }
    	}
    	catch (SQLException sqle)
    	{  throw new M4CompilerError("DB-SVM wrapper: error trying to read number of support vectors: " + sqle.getMessage());  }	
    } 
}
/*
 * Historie
 * --------
 * 
 * $Log: DB_SVM.java,v $
 * Revision 1.4  2006/04/11 14:10:17  euler
 * Updated license text.
 *
 * Revision 1.3  2006/04/06 16:31:17  euler
 * Prepended license remark.
 *
 * Revision 1.2  2006/03/29 09:50:47  euler
 * Added installation robustness.
 *
 * Revision 1.1  2006/01/03 09:54:36  hakenjos
 * Initial version!
 *
 */
