/* * 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 3 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. If not, see <http://www.gnu.org/licenses/>. */ /* * DefineFunction.java * Copyright (C) 2008-2012 University of Waikato, Hamilton, New Zealand * */ package weka.core.pmml; import java.util.ArrayList; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import weka.core.Attribute; /** * Class encapsulating DefineFunction (used in TransformationDictionary). * * @author Mark Hall (mhall{[at]}pentaho{[dot]}com * @version $Revision 1.0 $ */ public class DefineFunction extends Function { /** * For serialization */ private static final long serialVersionUID = -1976646917527243888L; /** * Inner class for handling Parameters */ protected class ParameterField extends FieldMetaInfo { /** * For serialization */ private static final long serialVersionUID = 3918895902507585558L; protected ParameterField(Element field) { super(field); } public Attribute getFieldAsAttribute() { if (m_optype == Optype.CONTINUOUS) { return new Attribute(m_fieldName); } // return a string attribute for categorical/ordinal optypes return new Attribute(m_fieldName, (ArrayList<String>)null); } } /** * The list of parameters expected by this function. We can use this to do * some error/type checking when users call setParameterDefs() on us */ protected ArrayList<ParameterField> m_parameters = new ArrayList<ParameterField>(); /** The optype for this function */ FieldMetaInfo.Optype m_optype = FieldMetaInfo.Optype.NONE; /** The Expression for this function to use */ protected Expression m_expression = null; public DefineFunction(Element container, TransformationDictionary transDict) throws Exception { m_functionName = container.getAttribute("name"); // get the optype for this function String opType = container.getAttribute("optype"); if (opType != null && opType.length() > 0) { for (FieldMetaInfo.Optype o : FieldMetaInfo.Optype.values()) { if (o.toString().equals(opType)) { m_optype = o; break; } } } else { throw new Exception("[DefineFunction] no optype specified!!"); } m_parameterDefs = new ArrayList<Attribute>(); // get all the parameters NodeList paramL = container.getElementsByTagName("ParameterField"); for (int i = 0; i < paramL.getLength(); i++) { Node paramN = paramL.item(i); if (paramN.getNodeType() == Node.ELEMENT_NODE) { ParameterField newP = new ParameterField((Element)paramN); m_parameters.add(newP); // set up default parameter definitions - these will probably get replaced // by more informative ones (i.e. possibly nominal attributes instead of // string attributes) later m_parameterDefs.add(newP.getFieldAsAttribute()); } } m_expression = Expression.getExpression(container, m_optype, m_parameterDefs, transDict); // check that the optype of the Expression is compatible with ours if (m_optype == FieldMetaInfo.Optype.CONTINUOUS && m_expression.getOptype() != m_optype) { throw new Exception("[DefineFunction] optype is continuous but our Expression's optype " + "is not."); } if ((m_optype == FieldMetaInfo.Optype.CATEGORICAL || m_optype == FieldMetaInfo.Optype.ORDINAL) != (m_expression.getOptype() == FieldMetaInfo.Optype.CATEGORICAL || m_expression.getOptype() == FieldMetaInfo.Optype.ORDINAL)) { throw new Exception("[DefineFunction] optype is categorical/ordinal but our Expression's optype " + "is not."); } } public void pushParameterDefs() throws Exception { if (m_parameterDefs == null) { throw new Exception("[DefineFunction] parameter definitions are null! Can't " + "push them to encapsulated expression."); } m_expression.setFieldDefs(m_parameterDefs); } /** * Get the structure of the result produced by this function. * * @return the structure of the result produced by this function. */ public Attribute getOutputDef() { return m_expression.getOutputDef(); } /** * Returns an array of the names of the parameters expected * as input by this function. May return null if this function * can take an unbounded number of parameters (i.e. min, max, etc.). * * @return an array of the parameter names or null if there are an * unbounded number of parameters. */ public String[] getParameterNames() { String[] result = new String[m_parameters.size()]; for (int i = 0; i < m_parameters.size(); i++) { result[i] = m_parameters.get(i).getFieldName(); } return result; } /** * Get the result of applying this function. * * @param incoming the arguments to this function (supplied in order to match that * of the parameter definitions * @return the result of applying this function. When the optype is * categorical or ordinal, an index into the values of the output definition * is returned. * @throws Exception if there is a problem computing the result of this function */ public double getResult(double[] incoming) throws Exception { if (incoming.length != m_parameters.size()) { throw new IllegalArgumentException("[DefineFunction] wrong number of arguments: expected " + m_parameters.size() + ", recieved " + incoming.length); } return m_expression.getResult(incoming); } /** * Set the structure of the parameters that are expected as input by * this function. This must be called before getOutputDef() is called. * * @param paramDefs the structure of the input parameters * @throws Exception if the number or types of parameters are not acceptable by * this function */ public void setParameterDefs(ArrayList<Attribute> paramDefs) throws Exception { if (paramDefs.size() != m_parameters.size()) { throw new Exception("[DefineFunction] number of parameter definitions does not match " + "number of parameters!"); } // check these defs against the optypes of the parameters for (int i = 0; i < m_parameters.size(); i++) { if (m_parameters.get(i).getOptype() == FieldMetaInfo.Optype.CONTINUOUS) { if (!paramDefs.get(i).isNumeric()) { throw new Exception("[DefineFunction] parameter " + m_parameters.get(i).getFieldName() + " is continuous, but corresponding " + "supplied parameter def " + paramDefs.get(i).name() + " is not!"); } } else { if (!paramDefs.get(i).isNominal() && !paramDefs.get(i).isString()) { throw new Exception("[DefineFunction] parameter " + m_parameters.get(i).getFieldName() + " is categorical/ordinal, but corresponding " + "supplied parameter def " + paramDefs.get(i).name() + " is not!"); } } } // now we need to rename these argument definitions to match the names of // the actual parameters ArrayList<Attribute> newParamDefs = new ArrayList<Attribute>(); for (int i = 0; i < paramDefs.size(); i++) { Attribute a = paramDefs.get(i); newParamDefs.add(a.copy(m_parameters.get(i).getFieldName())); } m_parameterDefs = newParamDefs; // update the Expression m_expression.setFieldDefs(m_parameterDefs); } public String toString() { return toString(""); } public String toString(String pad) { StringBuffer buff = new StringBuffer(); buff.append(pad + "DefineFunction (" + m_functionName + "):\n" + pad + "nparameters:\n"); for (ParameterField p : m_parameters) { buff.append(pad + p.getFieldAsAttribute() + "\n"); } buff.append(pad + "expression:\n" + m_expression.toString(pad + " ")); return buff.toString(); } }