/libformula-1.1.3/source/org/pentaho/reporting/libraries/formula/lvalues/Term.java
Java | 359 lines | 246 code | 34 blank | 79 comment | 53 complexity | 3b6a90a60471cb4d9e33523e2bb17b04 MD5 | raw file
Possible License(s): LGPL-2.1
1/*
2 * This program is free software; you can redistribute it and/or modify it under the
3 * terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
4 * Foundation.
5 *
6 * You should have received a copy of the GNU Lesser General Public License along with this
7 * program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
8 * or from the Free Software Foundation, Inc.,
9 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
10 *
11 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
12 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the GNU Lesser General Public License for more details.
14 *
15 * Copyright (c) 2006 - 2009 Pentaho Corporation and Contributors. All rights reserved.
16 */
17
18package org.pentaho.reporting.libraries.formula.lvalues;
19
20import java.util.ArrayList;
21
22import org.pentaho.reporting.libraries.formula.EvaluationException;
23import org.pentaho.reporting.libraries.formula.FormulaContext;
24import org.pentaho.reporting.libraries.formula.operators.InfixOperator;
25
26/**
27 * An term is a list of LValues connected by operators. For the sake of efficiency, this is not stored as tree. We store
28 * the term as a list in the following format: (headValue)(OP value)* ...
29 *
30 * @author Thomas Morgner
31 */
32public class Term extends AbstractLValue
33{
34 private static final LValue[] EMPTY_L_VALUE = new LValue[0];
35 private static final InfixOperator[] EMPTY_OPERATOR = new InfixOperator[0];
36 private static final long serialVersionUID = -1854082494425470979L;
37
38 private LValue optimizedHeadValue;
39 private LValue headValue;
40 private ArrayList operators;
41 private ArrayList operands;
42 private InfixOperator[] operatorArray;
43 private LValue[] operandsArray;
44 private boolean initialized;
45
46 public Term(final LValue headValue)
47 {
48 if (headValue == null)
49 {
50 throw new NullPointerException();
51 }
52
53 this.headValue = headValue;
54 }
55
56 public TypeValuePair evaluate() throws EvaluationException
57 {
58 TypeValuePair result = optimizedHeadValue.evaluate();
59 for (int i = 0; i < operandsArray.length; i++)
60 {
61 final LValue value = operandsArray[i];
62 final InfixOperator op = operatorArray[i];
63 result = op.evaluate(getContext(), result, value.evaluate());
64 }
65 return result;
66 }
67
68 public void add(final InfixOperator operator, final LValue operand)
69 {
70 if (operator == null)
71 {
72 throw new NullPointerException();
73 }
74 if (operand == null)
75 {
76 throw new NullPointerException();
77 }
78
79 if (operands == null || operators == null)
80 {
81 this.operands = new ArrayList();
82 this.operators = new ArrayList();
83 }
84
85 operands.add(operand);
86 operators.add(operator);
87 initialized = false;
88 }
89
90 public void initialize(final FormulaContext context) throws EvaluationException
91 {
92 super.initialize(context);
93 if (operands == null || operators == null)
94 {
95 this.optimizedHeadValue = headValue;
96 this.optimizedHeadValue.initialize(context);
97 this.operandsArray = EMPTY_L_VALUE;
98 this.operatorArray = EMPTY_OPERATOR;
99 return;
100 }
101
102 if (initialized)
103 {
104 optimizedHeadValue.initialize(context);
105 for (int i = 0; i < operandsArray.length; i++)
106 {
107 final LValue lValue = operandsArray[i];
108 lValue.initialize(context);
109 }
110 return;
111 }
112
113 optimize();
114 this.optimizedHeadValue.initialize(context);
115 for (int i = 0; i < operandsArray.length; i++)
116 {
117 final LValue value = operandsArray[i];
118 value.initialize(context);
119 }
120 initialized = true;
121 }
122
123 private void optimize()
124 {
125 if (operands == null || operators == null)
126 {
127 this.optimizedHeadValue = headValue;
128 this.operandsArray = EMPTY_L_VALUE;
129 this.operatorArray = EMPTY_OPERATOR;
130 return;
131 }
132 final ArrayList operators = (ArrayList) this.operators.clone();
133 final ArrayList operands = (ArrayList) this.operands.clone();
134 this.optimizedHeadValue = headValue;
135
136 while (true)
137 {
138 // now start to optimize everything.
139 // first, search the operator with the highest priority..
140 final InfixOperator op = (InfixOperator) operators.get(0);
141 int level = op.getLevel();
142 boolean moreThanOne = false;
143 for (int i = 1; i < operators.size(); i++)
144 {
145 final InfixOperator operator = (InfixOperator) operators.get(i);
146 final int opLevel = operator.getLevel();
147 if (opLevel != level)
148 {
149 moreThanOne = true;
150 level = Math.min(opLevel, level);
151 }
152 }
153
154 if (moreThanOne == false)
155 {
156 // No need to optimize the operators ..
157 break;
158 }
159
160 // There are at least two op-levels in this term.
161 Term subTerm = null;
162 for (int i = 0; i < operators.size(); i++)
163 {
164 final InfixOperator operator = (InfixOperator) operators.get(i);
165 if (operator.getLevel() != level)
166 {
167 subTerm = null;
168 continue;
169 }
170
171 if (subTerm == null)
172 {
173 if (i == 0)
174 {
175 subTerm = new Term(optimizedHeadValue);
176 optimizedHeadValue = subTerm;
177 }
178 else
179 {
180 final LValue lval = (LValue) operands.get(i - 1);
181 subTerm = new Term(lval);
182 operands.set(i - 1, subTerm);
183 }
184 }
185
186 // OK, now a term exists, and we should join it.
187 final LValue operand = (LValue) operands.get(i);
188 subTerm.add(operator, operand);
189 operands.remove(i);
190 operators.remove(i);
191 // Rollback the current index ..
192 //noinspection AssignmentToForLoopParameter
193 i -= 1;
194 }
195 }
196
197 this.operatorArray = (InfixOperator[])
198 operators.toArray(new InfixOperator[operators.size()]);
199 this.operandsArray = (LValue[])
200 operands.toArray(new LValue[operands.size()]);
201 }
202
203 /**
204 * Returns any dependent lvalues (parameters and operands, mostly).
205 *
206 * @return
207 */
208 public LValue[] getChildValues()
209 {
210 if (operandsArray == null)
211 {
212 optimize();
213 }
214 final LValue[] values = new LValue[operandsArray.length + 1];
215 values[0] = headValue;
216 System.arraycopy(operandsArray, 0, values, 1, operandsArray.length);
217 return values;
218 }
219
220
221 public String toString()
222 {
223 final StringBuffer b = new StringBuffer(100);
224
225 b.append('(');
226 b.append(headValue);
227 if (operands != null && operators != null)
228 {
229 for (int i = 0; i < operands.size(); i++)
230 {
231 final InfixOperator op = (InfixOperator) operators.get(i);
232 final LValue value = (LValue) operands.get(i);
233 b.append(op);
234 b.append(value);
235 }
236 }
237 b.append(')');
238//
239// b.append(";OPTIMIZED(");
240// b.append(optimizedHeadValue);
241// if (operandsArray != null && operatorArray != null)
242// {
243// for (int i = 0; i < operandsArray.length; i++)
244// {
245// final InfixOperator op = operatorArray[i];
246// final LValue value = operandsArray[i];
247// b.append(op);
248// b.append(value);
249// }
250// }
251// b.append(")");
252
253 return b.toString();
254 }
255
256 /**
257 * Checks whether the LValue is constant. Constant lvalues always return the 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 final 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 LValue[] getOperands()
299 {
300 return (LValue[]) operands.toArray(new LValue[operands.size()]);
301 }
302
303 public InfixOperator[] getOperators()
304 {
305 return (InfixOperator[]) operators.toArray(new InfixOperator[operators.size()]);
306 }
307
308 public LValue getHeadValue()
309 {
310 return headValue;
311 }
312
313 /**
314 * Allows access to the post optimized head value note that without the optimization, it's difficult to traverse
315 * libformula's object model.
316 *
317 * @return optimized head value
318 */
319 public LValue getOptimizedHeadValue()
320 {
321 return optimizedHeadValue;
322 }
323//
324// /**
325// * Allows access to the post optimized operator array
326// *
327// * @return optimized operator array
328// */
329// public InfixOperator[] getOptimizedOperators()
330// {
331// return operatorArray;
332// }
333//
334// /**
335// * Allows access to the post optimized operand array
336// *
337// * @return optimized operand array
338// */
339// public LValue[] getOptimizedOperands()
340// {
341// return operandsArray;
342// }
343
344 public ParsePosition getParsePosition()
345 {
346 final ParsePosition parsePosition = super.getParsePosition();
347 if (parsePosition == null)
348 {
349 final int startColumn = headValue.getParsePosition().getStartColumn();
350 final int startLine = headValue.getParsePosition().getStartLine();
351 final ParsePosition lastParsePos =
352 operandsArray[operandsArray.length - 1].getParsePosition();
353 final int endColumn = lastParsePos.getEndColumn();
354 final int endLine = lastParsePos.getEndLine();
355 setParsePosition(new ParsePosition(startLine, startColumn, endLine, endColumn));
356 }
357 return super.getParsePosition();
358 }
359}