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