001 /** 002 * ========================================= 003 * LibFormula : a free Java formula library 004 * ========================================= 005 * 006 * Project Info: http://reporting.pentaho.org/libformula/ 007 * 008 * (C) Copyright 2006-2007, by Pentaho Corporation and Contributors. 009 * 010 * This library is free software; you can redistribute it and/or modify it under the terms 011 * of the GNU Lesser General Public License as published by the Free Software Foundation; 012 * either version 2.1 of the License, or (at your option) any later version. 013 * 014 * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 015 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 016 * See the GNU Lesser General Public License for more details. 017 * 018 * You should have received a copy of the GNU Lesser General Public License along with this 019 * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, 020 * Boston, MA 02111-1307, USA. 021 * 022 * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 023 * in the United States and other countries.] 024 * 025 * 026 * ------------ 027 * $Id: Term.java 3521 2007-10-16 10:55:14Z tmorgner $ 028 * ------------ 029 * (C) Copyright 2006-2007, by Pentaho Corporation. 030 */ 031 package org.jfree.formula.lvalues; 032 033 import java.util.ArrayList; 034 035 import org.jfree.formula.EvaluationException; 036 import org.jfree.formula.FormulaContext; 037 import org.jfree.formula.operators.InfixOperator; 038 039 /** 040 * An term is a list of LValues connected by operators. For the sake of 041 * efficiency, this is not stored as tree. We store the term as a list in the 042 * following format: (headValue)(OP value)* ... 043 * 044 * @author Thomas Morgner 045 */ 046 public class Term extends AbstractLValue 047 { 048 private LValue optimizedHeadValue; 049 private LValue headValue; 050 private ArrayList operators; 051 private ArrayList operands; 052 private InfixOperator[] operatorArray; 053 private LValue[] operandsArray; 054 private boolean initialized; 055 private static final LValue[] EMPTY_L_VALUE = new LValue[0]; 056 private static final InfixOperator[] EMPTY_OPERATOR = new InfixOperator[0]; 057 058 public Term(final LValue headValue) 059 { 060 if (headValue == null) 061 { 062 throw new NullPointerException(); 063 } 064 065 this.headValue = headValue; 066 } 067 068 public TypeValuePair evaluate() throws EvaluationException 069 { 070 TypeValuePair result = optimizedHeadValue.evaluate(); 071 for (int i = 0; i < operandsArray.length; i++) 072 { 073 final LValue value = operandsArray[i]; 074 final InfixOperator op = operatorArray[i]; 075 result = op.evaluate(getContext(), result, value.evaluate()); 076 } 077 return result; 078 } 079 080 public void add(final InfixOperator operator, final LValue operand) 081 { 082 if (operator == null) 083 { 084 throw new NullPointerException(); 085 } 086 if (operand == null) 087 { 088 throw new NullPointerException(); 089 } 090 091 if (operands == null || operators == null) 092 { 093 this.operands = new ArrayList(); 094 this.operators = new ArrayList(); 095 } 096 097 operands.add(operand); 098 operators.add(operator); 099 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 }