Source for org.jfree.formula.lvalues.FormulaFunction

   1: /**
   2:  * =========================================
   3:  * LibFormula : a free Java formula library
   4:  * =========================================
   5:  *
   6:  * Project Info:  http://reporting.pentaho.org/libformula/
   7:  *
   8:  * (C) Copyright 2006-2007, by Pentaho Corporation and Contributors.
   9:  *
  10:  * This library is free software; you can redistribute it and/or modify it under the terms
  11:  * of the GNU Lesser General Public License as published by the Free Software Foundation;
  12:  * either version 2.1 of the License, or (at your option) any later version.
  13:  *
  14:  * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
  15:  * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  16:  * See the GNU Lesser General Public License for more details.
  17:  *
  18:  * You should have received a copy of the GNU Lesser General Public License along with this
  19:  * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
  20:  * Boston, MA 02111-1307, USA.
  21:  *
  22:  * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
  23:  * in the United States and other countries.]
  24:  *
  25:  *
  26:  * ------------
  27:  * $Id: FormulaFunction.java,v 1.15 2007/05/07 22:51:12 mimil Exp $
  28:  * ------------
  29:  * (C) Copyright 2006-2007, by Pentaho Corporation.
  30:  */
  31: package org.jfree.formula.lvalues;
  32: 
  33: import org.jfree.formula.EvaluationException;
  34: import org.jfree.formula.FormulaContext;
  35: import org.jfree.formula.LibFormulaErrorValue;
  36: import org.jfree.formula.function.Function;
  37: import org.jfree.formula.function.FunctionDescription;
  38: import org.jfree.formula.function.FunctionRegistry;
  39: import org.jfree.formula.function.ParameterCallback;
  40: import org.jfree.formula.typing.Type;
  41: import org.jfree.formula.typing.TypeRegistry;
  42: import org.jfree.util.Log;
  43: 
  44: /**
  45:  * A function. Formulas consist of functions, references or static values, which
  46:  * are connected by operators.
  47:  * <p/>
  48:  * Functions always have a cannonical name, which must be unique and which
  49:  * identifies the function. Functions can have a list of parameters. The number
  50:  * of parameters can vary, and not all parameters need to be filled.
  51:  * <p/>
  52:  * Functions can have required and optional parameters. Mixing required and
  53:  * optional parameters is not allowed. Optional parameters cannot be ommited,
  54:  * unless they are the last parameter in the list.
  55:  * <p/>
  56:  * This class provides the necessary wrapper functionality to fill in the
  57:  * parameters.
  58:  *
  59:  * @author Thomas Morgner
  60:  */
  61: public class FormulaFunction extends AbstractLValue
  62: {
  63:   private static class FormulaParameterCallback implements ParameterCallback
  64:   {
  65:     private TypeValuePair[] backend;
  66:     private FormulaFunction function;
  67: 
  68:     public FormulaParameterCallback(final FormulaFunction function)
  69:     {
  70:       this.function = function;
  71:       this.backend = new TypeValuePair[function.parameters.length];
  72:     }
  73: 
  74:     private TypeValuePair get(int pos) throws EvaluationException
  75:     {
  76:       final LValue parameter = function.parameters[pos];
  77:       final Type paramType = function.metaData.getParameterType(pos);
  78:       if (parameter != null)
  79:       {
  80:         final TypeValuePair result = parameter.evaluate();
  81:         // lets do some type checking, right?
  82:         final TypeRegistry typeRegistry = function.getContext().getTypeRegistry();
  83:         final TypeValuePair converted = typeRegistry.convertTo(paramType, result);
  84:         if (converted == null)
  85:         {
  86:           Log.debug("Failed to evaluate parameter " + pos + " on function " + function);
  87:           throw new EvaluationException(LibFormulaErrorValue.ERROR_INVALID_AUTO_ARGUMENT_VALUE);
  88:         }
  89:         return converted;
  90:       }
  91:       else
  92:       {
  93:         return new TypeValuePair(paramType, function.metaData.getDefaultValue(pos));
  94:       }
  95:     }
  96: 
  97:     public LValue getRaw(int position)
  98:     {
  99:       return function.parameters[position];
 100:     }
 101: 
 102:     public Object getValue(int position) throws EvaluationException
 103:     {
 104:       final TypeValuePair retval = backend[position];
 105:       if (retval != null)
 106:       {
 107:         return retval.getValue();
 108:       }
 109: 
 110:       final TypeValuePair pair = get(position);
 111:       backend[position] = pair;
 112:       return pair.getValue();
 113:     }
 114: 
 115:     public Type getType(int position) throws EvaluationException
 116:     {
 117:       final TypeValuePair retval = backend[position];
 118:       if (retval != null)
 119:       {
 120:         return retval.getType();
 121:       }
 122: 
 123:       final TypeValuePair pair = get(position);
 124:       backend[position] = pair;
 125:       return pair.getType();
 126:     }
 127: 
 128:     public int getParameterCount()
 129:     {
 130:       return backend.length;
 131:     }
 132:   }
 133: 
 134:   private String functionName;
 135:   private LValue[] parameters;
 136:   private Function function;
 137:   private FunctionDescription metaData;
 138: 
 139:   public FormulaFunction(final String functionName, final LValue[] parameters)
 140:   {
 141:     this.functionName = functionName;
 142:     this.parameters = parameters;
 143:   }
 144: 
 145:   public void initialize(final FormulaContext context) throws EvaluationException
 146:   {
 147:     super.initialize(context);
 148:     final FunctionRegistry registry = context.getFunctionRegistry();
 149:     function = registry.createFunction(functionName);
 150:     metaData = registry.getMetaData(functionName);
 151: 
 152:     for (int i = 0; i < parameters.length; i++)
 153:     {
 154:       parameters[i].initialize(context);
 155:     }
 156:   }
 157: 
 158:   /**
 159:    * Returns the function's name. This is the normalized name and may not be
 160:    * suitable for the user. Query the function's metadata to retrieve a
 161:    * display-name.
 162:    *
 163:    * @return the function's name.
 164:    */
 165:   public String getFunctionName()
 166:   {
 167:     return functionName;
 168:   }
 169: 
 170:   /**
 171:    * Returns the initialized function. Be aware that this method will return
 172:    * null if this LValue instance has not yet been initialized.
 173:    *
 174:    * @return the function instance or null, if the FormulaFunction instance has
 175:    * not yet been initialized.
 176:    */
 177:   public Function getFunction()
 178:   {
 179:     return function;
 180:   }
 181: 
 182:   /**
 183:    * Returns the function's meta-data. Be aware that this method will return
 184:    * null if this LValue instance has not yet been initialized.
 185:    *
 186:    * @return the function description instance or null, if the FormulaFunction
 187:    * instance has not yet been initialized.
 188:    */
 189:   public FunctionDescription getMetaData()
 190:   {
 191:     return metaData;
 192:   }
 193: 
 194:   public Object clone() throws CloneNotSupportedException
 195:   {
 196:     final FormulaFunction fn = (FormulaFunction) super.clone();
 197:     fn.parameters = (LValue[]) parameters.clone();
 198:     for (int i = 0; i < parameters.length; i++)
 199:     {
 200:       final LValue parameter = parameters[i];
 201:       fn.parameters[i] = (LValue) parameter.clone();
 202:     }
 203:     return fn;
 204:   }
 205: 
 206:   public TypeValuePair evaluate() throws EvaluationException
 207:   {
 208:     // First, grab the parameters and their types.
 209:     final FormulaContext context = getContext();
 210:     // And if everything is ok, compute the stuff ..
 211:     if (function == null)
 212:     {
 213:       throw new EvaluationException(LibFormulaErrorValue.ERROR_INVALID_FUNCTION_VALUE);
 214:     }
 215:     try
 216:     {
 217:       return function.evaluate(context, new FormulaParameterCallback(this));
 218:     }
 219:     catch(EvaluationException e)
 220:     {
 221:       throw e;
 222:     }
 223:     catch(Exception e)
 224:     {
 225:       throw new EvaluationException(LibFormulaErrorValue.ERROR_UNEXPECTED_VALUE);
 226:     }
 227:   }
 228: 
 229:   /**
 230:    * Returns any dependent lvalues (parameters and operands, mostly).
 231:    *
 232:    * @return
 233:    */
 234:   public LValue[] getChildValues()
 235:   {
 236:     return (LValue[]) parameters.clone();
 237:   }
 238: 
 239: 
 240:   public String toString()
 241:   {
 242:     StringBuffer b = new StringBuffer();
 243:     b.append(functionName);
 244:     b.append("(");
 245:     for (int i = 0; i < parameters.length; i++)
 246:     {
 247:       if (i > 0)
 248:       {
 249:         b.append(";");
 250:       }
 251:       LValue parameter = parameters[i];
 252:       b.append(parameter);
 253:     }
 254:     b.append(")");
 255:     return b.toString();
 256:   }
 257: 
 258:   /**
 259:    * Checks, whether the LValue is constant. Constant lvalues always return the
 260:    * same value.
 261:    *
 262:    * @return
 263:    */
 264:   public boolean isConstant()
 265:   {
 266:     if (metaData.isVolatile())
 267:     {
 268:       return false;
 269:     }
 270:     for (int i = 0; i < parameters.length; i++)
 271:     {
 272:       LValue value = parameters[i];
 273:       if (value.isConstant() == false)
 274:       {
 275:         return false;
 276:       }
 277:     }
 278:     return true;
 279:   }
 280: 
 281: 
 282: }