Source for org.jfree.formula.lvalues.Term

   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: Term.java 3521 2007-10-16 10:55:14Z tmorgner $
  28:  * ------------
  29:  * (C) Copyright 2006-2007, by Pentaho Corporation.
  30:  */
  31: package org.jfree.formula.lvalues;
  32: 
  33: import java.util.ArrayList;
  34: 
  35: import org.jfree.formula.EvaluationException;
  36: import org.jfree.formula.FormulaContext;
  37: import org.jfree.formula.operators.InfixOperator;
  38: 
  39: /**
  40:  * An term is a list of LValues connected by operators. For the sake of
  41:  * efficiency, this is not stored as tree. We store the term as a list in the
  42:  * following format: (headValue)(OP value)* ...
  43:  *
  44:  * @author Thomas Morgner
  45:  */
  46: public class Term extends AbstractLValue
  47: {
  48:   private LValue optimizedHeadValue;
  49:   private LValue headValue;
  50:   private ArrayList operators;
  51:   private ArrayList operands;
  52:   private InfixOperator[] operatorArray;
  53:   private LValue[] operandsArray;
  54:   private boolean initialized;
  55:   private static final LValue[] EMPTY_L_VALUE = new LValue[0];
  56:   private static final InfixOperator[] EMPTY_OPERATOR = new InfixOperator[0];
  57: 
  58:   public Term(final LValue headValue)
  59:   {
  60:     if (headValue == null)
  61:     {
  62:       throw new NullPointerException();
  63:     }
  64: 
  65:     this.headValue = headValue;
  66:   }
  67: 
  68:   public TypeValuePair evaluate() throws EvaluationException
  69:   {
  70:     TypeValuePair result = optimizedHeadValue.evaluate();
  71:     for (int i = 0; i < operandsArray.length; i++)
  72:     {
  73:       final LValue value = operandsArray[i];
  74:       final InfixOperator op = operatorArray[i];
  75:       result = op.evaluate(getContext(), result, value.evaluate());
  76:     }
  77:     return result;
  78:   }
  79: 
  80:   public void add(final InfixOperator operator, final LValue operand)
  81:   {
  82:     if (operator == null)
  83:     {
  84:       throw new NullPointerException();
  85:     }
  86:     if (operand == null)
  87:     {
  88:       throw new NullPointerException();
  89:     }
  90: 
  91:     if (operands == null || operators == null)
  92:     {
  93:       this.operands = new ArrayList();
  94:       this.operators = new ArrayList();
  95:     }
  96: 
  97:     operands.add(operand);
  98:     operators.add(operator);
  99:     initialized = false;
 100:   }
 101: 
 102:   public void initialize(final FormulaContext context) throws EvaluationException
 103:   {
 104:     super.initialize(context);
 105:     if (operands == null || operators == null)
 106:     {
 107:       this.optimizedHeadValue = headValue;
 108:       this.optimizedHeadValue.initialize(context);
 109:       this.operandsArray = EMPTY_L_VALUE;
 110:       this.operatorArray = EMPTY_OPERATOR;
 111:       return;
 112:     }
 113: 
 114:     if (initialized)
 115:     {
 116:       optimizedHeadValue.initialize(context);
 117:       for (int i = 0; i < operandsArray.length; i++)
 118:       {
 119:         final LValue lValue = operandsArray[i];
 120:         lValue.initialize(context);
 121:       }
 122:       return;
 123:     }
 124: 
 125:     optimize(context);
 126:   }
 127: 
 128:   private void optimize(final FormulaContext context) throws EvaluationException
 129:   {
 130:     final ArrayList operators = (ArrayList) this.operators.clone();
 131:     final ArrayList operands = (ArrayList) this.operands.clone();
 132:     this.optimizedHeadValue = headValue;
 133: 
 134:     while (true)
 135:     {
 136:       // now start to optimize everything.
 137:       // first, search the operator with the highest priority..
 138:       final InfixOperator op = (InfixOperator) operators.get(0);
 139:       int level = op.getLevel();
 140:       boolean moreThanOne = false;
 141:       for (int i = 1; i < operators.size(); i++)
 142:       {
 143:         final InfixOperator operator = (InfixOperator) operators.get(i);
 144:         final int opLevel = operator.getLevel();
 145:         if (opLevel != level)
 146:         {
 147:           moreThanOne = true;
 148:           level = Math.min(opLevel, level);
 149:         }
 150:       }
 151: 
 152:       if (moreThanOne == false)
 153:       {
 154:         // No need to optimize the operators ..
 155:         break;
 156:       }
 157: 
 158:       // There are at least two op-levels in this term.
 159:       Term subTerm = null;
 160:       for (int i = 0; i < operators.size(); i++)
 161:       {
 162:         final InfixOperator operator = (InfixOperator) operators.get(i);
 163:         if (operator.getLevel() != level)
 164:         {
 165:           subTerm = null;
 166:           continue;
 167:         }
 168: 
 169:         if (subTerm == null)
 170:         {
 171:           if (i == 0)
 172:           {
 173:             subTerm = new Term(optimizedHeadValue);
 174:             optimizedHeadValue = subTerm;
 175:           }
 176:           else
 177:           {
 178:             final LValue lval = (LValue) operands.get(i - 1);
 179:             subTerm = new Term(lval);
 180:             operands.set(i - 1, subTerm);
 181:           }
 182:         }
 183: 
 184:         // OK, now a term exists, and we should join it.
 185:         final LValue operand = (LValue) operands.get(i);
 186:         subTerm.add(operator, operand);
 187:         operands.remove(i);
 188:         operators.remove(i);
 189:         // Rollback the current index ..
 190:         //noinspection AssignmentToForLoopParameter
 191:         i -= 1;
 192:       }
 193:     }
 194: 
 195:     this.operatorArray = (InfixOperator[])
 196:         operators.toArray(new InfixOperator[operators.size()]);
 197:     this.operandsArray = (LValue[])
 198:         operands.toArray(new LValue[operands.size()]);
 199:     this.optimizedHeadValue.initialize(context);
 200:     for (int i = 0; i < operandsArray.length; i++)
 201:     {
 202:       final LValue value = operandsArray[i];
 203:       value.initialize(context);
 204:     }
 205: 
 206:   }
 207: 
 208:   /**
 209:    * Returns any dependent lvalues (parameters and operands, mostly).
 210:    *
 211:    * @return
 212:    */
 213:   public LValue[] getChildValues()
 214:   {
 215:     final LValue[] values = new LValue[operandsArray.length + 1];
 216:     values[0] = headValue;
 217:     System.arraycopy(operandsArray, 0, values, 1, operandsArray.length);
 218:     return values;
 219:   }
 220: 
 221: 
 222:   public String toString()
 223:   {
 224:     final StringBuffer b = new StringBuffer();
 225: 
 226:     b.append("(");
 227:     b.append(headValue);
 228:     if (operands != null && operators != null)
 229:     {
 230:       for (int i = 0; i < operands.size(); i++)
 231:       {
 232:         final InfixOperator op = (InfixOperator) operators.get(i);
 233:         final LValue value = (LValue) operands.get(i);
 234:         b.append(op);
 235:         b.append(value);
 236:       }
 237:     }
 238:     b.append(")");
 239: //
 240: //    b.append(";OPTIMIZED(");
 241: //    b.append(optimizedHeadValue);
 242: //    if (operandsArray != null && operatorArray != null)
 243: //    {
 244: //      for (int i = 0; i < operandsArray.length; i++)
 245: //      {
 246: //        final InfixOperator op = operatorArray[i];
 247: //        final LValue value = operandsArray[i];
 248: //        b.append(op);
 249: //        b.append(value);
 250: //      }
 251: //    }
 252: //    b.append(")");
 253: 
 254:     return b.toString();
 255:   }
 256: 
 257:   /**
 258:    * Checks, whether the LValue is constant. Constant lvalues always return the
 259:    * same value.
 260:    *
 261:    * @return
 262:    */
 263:   public boolean isConstant()
 264:   {
 265:     if (headValue.isConstant() == false)
 266:     {
 267:       return false;
 268:     }
 269: 
 270:     for (int i = 0; i < operands.size(); i++)
 271:     {
 272:       final LValue value = (LValue) operands.get(i);
 273:       if (value.isConstant() == false)
 274:       {
 275:         return false;
 276:       }
 277:     }
 278:     return true;
 279:   }
 280: 
 281:   public Object clone() throws CloneNotSupportedException
 282:   {
 283:     final Term o = (Term) super.clone();
 284:     if (operands != null)
 285:     {
 286:       o.operands = (ArrayList) operands.clone();
 287:     }
 288:     if (operators != null)
 289:     {
 290:       o.operators = (ArrayList) operators.clone();
 291:     }
 292:     o.headValue = (LValue) headValue.clone();
 293:     o.optimizedHeadValue = null;
 294:     o.operandsArray = null;
 295:     o.operatorArray = null;
 296:     o.initialized = false;
 297:     return o;
 298:   }
 299: 
 300:   public InfixOperator[] getOperands ()
 301:   {
 302:     return (InfixOperator[]) operands.toArray(new InfixOperator[operands.size()]);
 303:   }
 304: 
 305:   public LValue[] getOperators ()
 306:   {
 307:     return (LValue[]) operators.toArray(new LValue[operators.size()]);
 308:   }
 309: 
 310:   public LValue getHeadValue()
 311:   {
 312:     return headValue;
 313:   }
 314:   
 315:   /**
 316:    * Allows access to the post optimized head value
 317:    * note that without the optimization, it's difficult to traverse
 318:    * libformula's object model.
 319:    *
 320:    * @return optimized head value
 321:    */
 322:   public LValue getOptimizedHeadValue()
 323:   {
 324:     return optimizedHeadValue;
 325:   }
 326:   
 327:   /**
 328:    * Allows access to the post optimized operator array
 329:    * 
 330:    * @return optimized operator array
 331:    */
 332:   public InfixOperator[] getOptimizedOperators()
 333:   {
 334:     return operatorArray;
 335:   }
 336:   
 337:   /**
 338:    * Allows access to the post optimized operand array
 339:    *
 340:    * @return optimized operand array
 341:    */
 342:   public LValue[] getOptimizedOperands()
 343:   {
 344:     return operandsArray;
 345:   }
 346: }