content: /*
   *    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; if not, write to the Free Software
   *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
   */
  
  /*
   *    Expression.java
   *    Copyright (C) 2008 University of Waikato, Hamilton, New Zealand
   *
   */
  
  package weka.core.pmml;
  
  import java.io.Serializable;
  import java.util.ArrayList;
  import org.w3c.dom.Element;
  import org.w3c.dom.Node;
  import org.w3c.dom.NodeList;
  
  import weka.core.Attribute;
  
  public abstract class Expression  implements Serializable {
    
    /**
     * For serialization
     */
    private static final long serialVersionUID = 4448840549804800321L;
    
    /** The optype of this Expression */
    protected FieldMetaInfo.Optype m_opType;
  
    /** The field defs */
    protected ArrayList<Attribute> m_fieldDefs = null;
    
    // NOTE - might need to pass in mining schema in order
    // to determine values for nominal optypes
    public Expression(FieldMetaInfo.Optype opType, ArrayList<Attribute> fieldDefs) {
      m_opType = opType;
      m_fieldDefs = fieldDefs;
    }
    
    /**
     * Set the field definitions for this Expression to use
     * 
     * @param fieldDefs the field definitions to use
     * @throws Exception if there is a problem setting the field definitions
     */
    public void setFieldDefs(ArrayList<Attribute> fieldDefs) throws Exception {
      m_fieldDefs = fieldDefs;
    }
      
    /**
     * Get the result of evaluating the expression. In the case
     * of a continuous optype, a real number is returned; in
     * the case of a categorical/ordinal optype, the index of the nominal
     * value is returned as a double.
     * 
     * @param incoming the incoming parameter values
     * @return the result of evaluating the expression
     * @throws Exception if there is a problem computing the result
     */
    public abstract double getResult(double[] incoming) throws Exception;
    
    /**
     * Get the result of evaluating the expression for continuous
     * optype. Is the same as calling getResult() when the optype
     * is continuous.
     * 
     * @param incoming the incoming parameter values
     * mining schema
     * @return the result of evaluating the expression.
     * @throws Exception if the optype is not continuous.
     */
    public double getResultContinuous(double[] incoming) throws Exception {
      if (!(m_opType == FieldMetaInfo.Optype.CONTINUOUS)) {
        throw new Exception("[Expression] Can't return continuous result "
            + "as optype is not continuous");
      }
      return getResult(incoming);
    }
    
    /**
     * Gets the result of evaluating the expression when the
     * optype is categorical or ordinal as the actual String
     * value.
     * 
     * @param incoming the incoming parameter values 
     * @return the result of evaluating the expression
     * @throws Exception if the optype is continuous
     */
    public abstract String getResultCategorical(double[] incoming) 
      throws Exception;
    
    /**
     * Return the structure of the result of applying this Expression
     * as an Attribute.
     * 
     * @return the structure of the result of applying this Expression as an
     * Attribute.
     */
    protected abstract Attribute getOutputDef(); 
  
    /**
     * Static factory method that returns a subclass of Expression that
     * encapsulates the type of expression contained in the Element
     * supplied. Assumes that there is just one expression contained
     * in the supplied Element.
     * 
     * @param container the Node containing the expression
     * @param opType the optype of the value returned by this Expression.
     * @param fieldDefs an ArrayList of Attributes for the fields that this
     * Expression may need to access
     * Since Expressions are children of either DerivedFields or
     * DefineFuntions, they will have the same optype as their parent.
     * @param transDict the TransformationDictionary (may be null if there
     * is no dictionary)
     * @return an Expression object or null if there is no known expression in
     * the container 
     * @throws Exception for unsupported Expression types 
     */
    public static Expression getExpression(Node container, 
        FieldMetaInfo.Optype opType,
        ArrayList<Attribute> fieldDefs,
        TransformationDictionary transDict) throws Exception {
      
      // we need to examine children of this Node to find an expression,
      // not the entire subtree (as would be returned by Element.getElementsByTagName()
      
      Expression result = null;
      String tagName = "";
      
      NodeList children = container.getChildNodes();
      if (children.getLength() == 0) {
        throw new Exception("[Expression] container has no children!");
      }
      
      // at this level in the tree there should be only one expression type
      // specified - look for it here.
      for (int i = 0; i < children.getLength(); i++) {
        Node child = children.item(i);
        if (child.getNodeType() == Node.ELEMENT_NODE) {
          tagName = ((Element)child).getTagName();
          result = getExpression(tagName, child, opType, fieldDefs, transDict);
          if (result != null) {
            break;
          }
        }
      }
      
      return result;
    }
    
    /**
     * Static factory method that returns a subclass of Expression that
     * encapsulates the type of expression supplied as an argument.
     * 
     * @param name the name of the Expression to get
     * @param expression the Node containing the expression
     * @param opType the optype of the value returned by this Expression.
     * @param fieldDefs an ArrayList of Attributes for the fields that this
     * Expression may need to access
     * Since Expressions are children of either DerivedFields or
     * DefineFuntions, they will have the same optype as their parent.
     * @param transDict the TransformationDictionary (may be null if there
     * is no dictionary)
     * @return an Expression object or null if there is no known expression in
     * the container 
     * @throws Exception for unsupported Expression types 
     */
    public static Expression getExpression(String name, 
        Node expression,
        FieldMetaInfo.Optype opType,
        ArrayList<Attribute> fieldDefs,
        TransformationDictionary transDict) throws Exception {
     
      Expression result = null;
      
      if (name.equals("Constant")) {
        // construct a Constant expression
        result = new Constant((Element)expression, opType, fieldDefs);
      } else if (name.equals("FieldRef")) {
        // construct a FieldRef expression
        result = new FieldRef((Element)expression, opType, fieldDefs);
      } else if (name.equals("Apply")) {
        // construct an Apply expression
        result = new Apply((Element)expression, opType, fieldDefs, transDict);
      } else if (name.equals("NormDiscrete")) {
        result = new NormDiscrete((Element)expression, opType, fieldDefs);
      } else if (name.equals("NormContinuous")) {
        result = new NormContinuous((Element)expression, opType, fieldDefs);
      } else if (name.equals("Discretize")) {
        result = new Discretize((Element)expression, opType, fieldDefs);
      } else if (name.equals("MapValues") ||
          name.equals("Aggregate")) {
        throw new Exception("[Expression] Unhandled Expression type " + name);
      }
      return result;
    }
    
    /**
     * Return the named attribute from the list of reference fields.
     * 
     * @param attName the name of the attribute to retrieve
     * @return the named attribute (or null if it can't be found).
     */
    public Attribute getFieldDef(String attName) {
      Attribute returnV = null;
      for (int i = 0; i < m_fieldDefs.size(); i++) {
        if (m_fieldDefs.get(i).name().equals(attName)) {
          returnV = m_fieldDefs.get(i);
          break;
        }
      }
      return returnV;
    }
    
    public int getFieldDefIndex(String attName) {
      int returnV = -1;
      for (int i = 0; i < m_fieldDefs.size(); i++) {
        if (m_fieldDefs.get(i).name().equals(attName)) {
          returnV = i;
          break;
        }
      }
      return returnV;
    }
    
    /**
     * Get the optype of the result of applying this Expression.
     * 
     * @return the optype of the result of applying this Expression
     */
    public FieldMetaInfo.Optype getOptype() {
      return m_opType;
    }
    
    public String toString() {
      return toString("");
    }
    
    public String toString(String pad) {
      return pad + this.getClass().getName();
    }
  }
  