View Javadoc

1   /* ============================================================
2    * JRobin : Pure java implementation of RRDTool's functionality
3    * ============================================================
4    *
5    * Project Info:  http://www.jrobin.org
6    * Project Lead:  Sasa Markovic (saxon@jrobin.org);
7    *
8    * (C) Copyright 2003-2005, by Sasa Markovic.
9    *
10   * Developers:    Sasa Markovic (saxon@jrobin.org)
11   *
12   *
13   * This library is free software; you can redistribute it and/or modify it under the terms
14   * of the GNU Lesser General Public License as published by the Free Software Foundation;
15   * either version 2.1 of the License, or (at your option) any later version.
16   *
17   * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
18   * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
19   * See the GNU Lesser General Public License for more details.
20   *
21   * You should have received a copy of the GNU Lesser General Public License along with this
22   * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
23   * Boston, MA 02111-1307, USA.
24   */
25  
26  package org.jrobin.data;
27  
28  import org.jrobin.core.RrdException;
29  import org.jrobin.core.Util;
30  
31  import java.util.Calendar;
32  import java.util.StringTokenizer;
33  
34  class RpnCalculator {
35  	private static final byte TKN_VAR = 0;
36  	private static final byte TKN_NUM = 1;
37  	private static final byte TKN_PLUS = 2;
38  	private static final byte TKN_MINUS = 3;
39  	private static final byte TKN_MULT = 4;
40  	private static final byte TKN_DIV = 5;
41  	private static final byte TKN_MOD = 6;
42  	private static final byte TKN_SIN = 7;
43  	private static final byte TKN_COS = 8;
44  	private static final byte TKN_LOG = 9;
45  	private static final byte TKN_EXP = 10;
46  	private static final byte TKN_FLOOR = 11;
47  	private static final byte TKN_CEIL = 12;
48  	private static final byte TKN_ROUND = 13;
49  	private static final byte TKN_POW = 14;
50  	private static final byte TKN_ABS = 15;
51  	private static final byte TKN_SQRT = 16;
52  	private static final byte TKN_RANDOM = 17;
53  	private static final byte TKN_LT = 18;
54  	private static final byte TKN_LE = 19;
55  	private static final byte TKN_GT = 20;
56  	private static final byte TKN_GE = 21;
57  	private static final byte TKN_EQ = 22;
58  	private static final byte TKN_IF = 23;
59  	private static final byte TKN_MIN = 24;
60  	private static final byte TKN_MAX = 25;
61  	private static final byte TKN_LIMIT = 26;
62  	private static final byte TKN_DUP = 27;
63  	private static final byte TKN_EXC = 28;
64  	private static final byte TKN_POP = 29;
65  	private static final byte TKN_UN = 30;
66  	private static final byte TKN_UNKN = 31;
67  	private static final byte TKN_NOW = 32;
68  	private static final byte TKN_TIME = 33;
69  	private static final byte TKN_PI = 34;
70  	private static final byte TKN_E = 35;
71  	private static final byte TKN_AND = 36;
72  	private static final byte TKN_OR = 37;
73  	private static final byte TKN_XOR = 38;
74  	private static final byte TKN_PREV = 39;
75  	private static final byte TKN_INF = 40;
76  	private static final byte TKN_NEGINF = 41;
77  	private static final byte TKN_STEP = 42;
78  	private static final byte TKN_YEAR = 43;
79  	private static final byte TKN_MONTH = 44;
80  	private static final byte TKN_DATE = 45;
81  	private static final byte TKN_HOUR = 46;
82  	private static final byte TKN_MINUTE = 47;
83  	private static final byte TKN_SECOND = 48;
84  	private static final byte TKN_WEEK = 49;
85  	private static final byte TKN_SIGN = 50;
86  	private static final byte TKN_RND = 51;
87  
88  	private String rpnExpression;
89  	private String sourceName;
90  	private DataProcessor dataProcessor;
91  
92  	private Token[] tokens;
93  	private RpnStack stack = new RpnStack();
94  	private double[] calculatedValues;
95  	private long[] timestamps;
96  	private double timeStep;
97  
98  	RpnCalculator(String rpnExpression, String sourceName, DataProcessor dataProcessor) throws RrdException {
99  		this.rpnExpression = rpnExpression;
100 		this.sourceName = sourceName;
101 		this.dataProcessor = dataProcessor;
102 		this.timestamps = dataProcessor.getTimestamps();
103 		this.timeStep = this.timestamps[1] - this.timestamps[0];
104 		this.calculatedValues = new double[this.timestamps.length];
105 		StringTokenizer st = new StringTokenizer(rpnExpression, ", ");
106 		tokens = new Token[st.countTokens()];
107 		for (int i = 0; st.hasMoreTokens(); i++) {
108 			tokens[i] = createToken(st.nextToken());
109 		}
110 	}
111 
112 	private Token createToken(String parsedText) throws RrdException {
113 		Token token = new Token();
114 		if (Util.isDouble(parsedText)) {
115 			token.id = TKN_NUM;
116 			token.number = Util.parseDouble(parsedText);
117 		}
118 		else if (parsedText.equals("+")) {
119 			token.id = TKN_PLUS;
120 		}
121 		else if (parsedText.equals("-")) {
122 			token.id = TKN_MINUS;
123 		}
124 		else if (parsedText.equals("*")) {
125 			token.id = TKN_MULT;
126 		}
127 		else if (parsedText.equals("/")) {
128 			token.id = TKN_DIV;
129 		}
130 		else if (parsedText.equals("%")) {
131 			token.id = TKN_MOD;
132 		}
133 		else if (parsedText.equals("SIN")) {
134 			token.id = TKN_SIN;
135 		}
136 		else if (parsedText.equals("COS")) {
137 			token.id = TKN_COS;
138 		}
139 		else if (parsedText.equals("LOG")) {
140 			token.id = TKN_LOG;
141 		}
142 		else if (parsedText.equals("EXP")) {
143 			token.id = TKN_EXP;
144 		}
145 		else if (parsedText.equals("FLOOR")) {
146 			token.id = TKN_FLOOR;
147 		}
148 		else if (parsedText.equals("CEIL")) {
149 			token.id = TKN_CEIL;
150 		}
151 		else if (parsedText.equals("ROUND")) {
152 			token.id = TKN_ROUND;
153 		}
154 		else if (parsedText.equals("POW")) {
155 			token.id = TKN_POW;
156 		}
157 		else if (parsedText.equals("ABS")) {
158 			token.id = TKN_ABS;
159 		}
160 		else if (parsedText.equals("SQRT")) {
161 			token.id = TKN_SQRT;
162 		}
163 		else if (parsedText.equals("RANDOM")) {
164 			token.id = TKN_RANDOM;
165 		}
166 		else if (parsedText.equals("LT")) {
167 			token.id = TKN_LT;
168 		}
169 		else if (parsedText.equals("LE")) {
170 			token.id = TKN_LE;
171 		}
172 		else if (parsedText.equals("GT")) {
173 			token.id = TKN_GT;
174 		}
175 		else if (parsedText.equals("GE")) {
176 			token.id = TKN_GE;
177 		}
178 		else if (parsedText.equals("EQ")) {
179 			token.id = TKN_EQ;
180 		}
181 		else if (parsedText.equals("IF")) {
182 			token.id = TKN_IF;
183 		}
184 		else if (parsedText.equals("MIN")) {
185 			token.id = TKN_MIN;
186 		}
187 		else if (parsedText.equals("MAX")) {
188 			token.id = TKN_MAX;
189 		}
190 		else if (parsedText.equals("LIMIT")) {
191 			token.id = TKN_LIMIT;
192 		}
193 		else if (parsedText.equals("DUP")) {
194 			token.id = TKN_DUP;
195 		}
196 		else if (parsedText.equals("EXC")) {
197 			token.id = TKN_EXC;
198 		}
199 		else if (parsedText.equals("POP")) {
200 			token.id = TKN_POP;
201 		}
202 		else if (parsedText.equals("UN")) {
203 			token.id = TKN_UN;
204 		}
205 		else if (parsedText.equals("UNKN")) {
206 			token.id = TKN_UNKN;
207 		}
208 		else if (parsedText.equals("NOW")) {
209 			token.id = TKN_NOW;
210 		}
211 		else if (parsedText.equals("TIME")) {
212 			token.id = TKN_TIME;
213 		}
214 		else if (parsedText.equals("PI")) {
215 			token.id = TKN_PI;
216 		}
217 		else if (parsedText.equals("E")) {
218 			token.id = TKN_E;
219 		}
220 		else if (parsedText.equals("AND")) {
221 			token.id = TKN_AND;
222 		}
223 		else if (parsedText.equals("OR")) {
224 			token.id = TKN_OR;
225 		}
226 		else if (parsedText.equals("XOR")) {
227 			token.id = TKN_XOR;
228 		}
229 		else if (parsedText.equals("PREV")) {
230 			token.id = TKN_PREV;
231 			token.variable = sourceName;
232 			token.values = calculatedValues;
233 		}
234 		else if (parsedText.startsWith("PREV(") && parsedText.endsWith(")")) {
235 			token.id = TKN_PREV;
236 			token.variable = parsedText.substring(5, parsedText.length() - 1);
237 			token.values = dataProcessor.getValues(token.variable);
238 		}
239 		else if (parsedText.equals("INF")) {
240 			token.id = TKN_INF;
241 		}
242 		else if (parsedText.equals("NEGINF")) {
243 			token.id = TKN_NEGINF;
244 		}
245 		else if (parsedText.equals("STEP")) {
246 			token.id = TKN_STEP;
247 		}
248 		else if (parsedText.equals("YEAR")) {
249 			token.id = TKN_YEAR;
250 		}
251 		else if (parsedText.equals("MONTH")) {
252 			token.id = TKN_MONTH;
253 		}
254 		else if (parsedText.equals("DATE")) {
255 			token.id = TKN_DATE;
256 		}
257 		else if (parsedText.equals("HOUR")) {
258 			token.id = TKN_HOUR;
259 		}
260 		else if (parsedText.equals("MINUTE")) {
261 			token.id = TKN_MINUTE;
262 		}
263 		else if (parsedText.equals("SECOND")) {
264 			token.id = TKN_SECOND;
265 		}
266 		else if (parsedText.equals("WEEK")) {
267 			token.id = TKN_WEEK;
268 		}
269 		else if (parsedText.equals("SIGN")) {
270 			token.id = TKN_SIGN;
271 		}
272 		else if (parsedText.equals("RND")) {
273 			token.id = TKN_RND;
274 		}
275 		else {
276 			token.id = TKN_VAR;
277 			token.variable = parsedText;
278 			token.values = dataProcessor.getValues(token.variable);
279 		}
280 		return token;
281 	}
282 
283 	double[] calculateValues() throws RrdException {
284 		for (int slot = 0; slot < timestamps.length; slot++) {
285 			resetStack();
286 			for (Token token : tokens) {
287 				double x1, x2, x3;
288 				switch (token.id) {
289 					case TKN_NUM:
290 						push(token.number);
291 						break;
292 					case TKN_VAR:
293 						push(token.values[slot]);
294 						break;
295 					case TKN_PLUS:
296 						push(pop() + pop());
297 						break;
298 					case TKN_MINUS:
299 						x2 = pop();
300 						x1 = pop();
301 						push(x1 - x2);
302 						break;
303 					case TKN_MULT:
304 						push(pop() * pop());
305 						break;
306 					case TKN_DIV:
307 						x2 = pop();
308 						x1 = pop();
309 						push(x1 / x2);
310 						break;
311 					case TKN_MOD:
312 						x2 = pop();
313 						x1 = pop();
314 						push(x1 % x2);
315 						break;
316 					case TKN_SIN:
317 						push(Math.sin(pop()));
318 						break;
319 					case TKN_COS:
320 						push(Math.cos(pop()));
321 						break;
322 					case TKN_LOG:
323 						push(Math.log(pop()));
324 						break;
325 					case TKN_EXP:
326 						push(Math.exp(pop()));
327 						break;
328 					case TKN_FLOOR:
329 						push(Math.floor(pop()));
330 						break;
331 					case TKN_CEIL:
332 						push(Math.ceil(pop()));
333 						break;
334 					case TKN_ROUND:
335 						push(Math.round(pop()));
336 						break;
337 					case TKN_POW:
338 						x2 = pop();
339 						x1 = pop();
340 						push(Math.pow(x1, x2));
341 						break;
342 					case TKN_ABS:
343 						push(Math.abs(pop()));
344 						break;
345 					case TKN_SQRT:
346 						push(Math.sqrt(pop()));
347 						break;
348 					case TKN_RANDOM:
349 						push(Math.random());
350 						break;
351 					case TKN_LT:
352 						x2 = pop();
353 						x1 = pop();
354 						push(x1 < x2 ? 1 : 0);
355 						break;
356 					case TKN_LE:
357 						x2 = pop();
358 						x1 = pop();
359 						push(x1 <= x2 ? 1 : 0);
360 						break;
361 					case TKN_GT:
362 						x2 = pop();
363 						x1 = pop();
364 						push(x1 > x2 ? 1 : 0);
365 						break;
366 					case TKN_GE:
367 						x2 = pop();
368 						x1 = pop();
369 						push(x1 >= x2 ? 1 : 0);
370 						break;
371 					case TKN_EQ:
372 						x2 = pop();
373 						x1 = pop();
374 						push(x1 == x2 ? 1 : 0);
375 						break;
376 					case TKN_IF:
377 						x3 = pop();
378 						x2 = pop();
379 						x1 = pop();
380 						push(x1 != 0 ? x2 : x3);
381 						break;
382 					case TKN_MIN:
383 						push(Math.min(pop(), pop()));
384 						break;
385 					case TKN_MAX:
386 						push(Math.max(pop(), pop()));
387 						break;
388 					case TKN_LIMIT:
389 						x3 = pop();
390 						x2 = pop();
391 						x1 = pop();
392 						push(x1 < x2 || x1 > x3 ? Double.NaN : x1);
393 						break;
394 					case TKN_DUP:
395 						push(peek());
396 						break;
397 					case TKN_EXC:
398 						x2 = pop();
399 						x1 = pop();
400 						push(x2);
401 						push(x1);
402 						break;
403 					case TKN_POP:
404 						pop();
405 						break;
406 					case TKN_UN:
407 						push(Double.isNaN(pop()) ? 1 : 0);
408 						break;
409 					case TKN_UNKN:
410 						push(Double.NaN);
411 						break;
412 					case TKN_NOW:
413 						push(Util.getTime());
414 						break;
415 					case TKN_TIME:
416 						push((long) Math.round(timestamps[slot]));
417 						break;
418 					case TKN_PI:
419 						push(Math.PI);
420 						break;
421 					case TKN_E:
422 						push(Math.E);
423 						break;
424 					case TKN_AND:
425 						x2 = pop();
426 						x1 = pop();
427 						push((x1 != 0 && x2 != 0) ? 1 : 0);
428 						break;
429 					case TKN_OR:
430 						x2 = pop();
431 						x1 = pop();
432 						push((x1 != 0 || x2 != 0) ? 1 : 0);
433 						break;
434 					case TKN_XOR:
435 						x2 = pop();
436 						x1 = pop();
437 						push(((x1 != 0 && x2 == 0) || (x1 == 0 && x2 != 0)) ? 1 : 0);
438 						break;
439 					case TKN_PREV:
440 						push((slot == 0) ? Double.NaN : token.values[slot - 1]);
441 						break;
442 					case TKN_INF:
443 						push(Double.POSITIVE_INFINITY);
444 						break;
445 					case TKN_NEGINF:
446 						push(Double.NEGATIVE_INFINITY);
447 						break;
448 					case TKN_STEP:
449 						push(timeStep);
450 						break;
451 					case TKN_YEAR:
452 						push(getCalendarField(pop(), Calendar.YEAR));
453 						break;
454 					case TKN_MONTH:
455 						push(getCalendarField(pop(), Calendar.MONTH));
456 						break;
457 					case TKN_DATE:
458 						push(getCalendarField(pop(), Calendar.DAY_OF_MONTH));
459 						break;
460 					case TKN_HOUR:
461 						push(getCalendarField(pop(), Calendar.HOUR_OF_DAY));
462 						break;
463 					case TKN_MINUTE:
464 						push(getCalendarField(pop(), Calendar.MINUTE));
465 						break;
466 					case TKN_SECOND:
467 						push(getCalendarField(pop(), Calendar.SECOND));
468 						break;
469 					case TKN_WEEK:
470 						push(getCalendarField(pop(), Calendar.WEEK_OF_YEAR));
471 						break;
472 					case TKN_SIGN:
473 						x1 = pop();
474 						push(Double.isNaN(x1) ? Double.NaN : x1 > 0 ? +1 : x1 < 0 ? -1 : 0);
475 						break;
476 					case TKN_RND:
477 						push(Math.floor(pop() * Math.random()));
478 						break;
479 					default:
480 						throw new RrdException("Unexpected RPN token encountered, token.id=" + token.id);
481 				}
482 			}
483 			calculatedValues[slot] = pop();
484 			// check if stack is empty only on the first try
485 			if (slot == 0 && !isStackEmpty()) {
486 				throw new RrdException("Stack not empty at the end of calculation. " +
487 						"Probably bad RPN expression [" + rpnExpression + "]");
488 			}
489 		}
490 		return calculatedValues;
491 	}
492 
493 	private double getCalendarField(double timestamp, int field) {
494 		Calendar calendar = Util.getCalendar((long) (timestamp * 1000));
495 		return calendar.get(field);
496 	}
497 
498 	private void push(double x) throws RrdException {
499 		stack.push(x);
500 	}
501 
502 	private double pop() throws RrdException {
503 		return stack.pop();
504 	}
505 
506 	private double peek() throws RrdException {
507 		return stack.peek();
508 	}
509 
510 	private void resetStack() {
511 		stack.reset();
512 	}
513 
514 	private boolean isStackEmpty() {
515 		return stack.isEmpty();
516 	}
517 
518 	private class RpnStack {
519 		private static final int MAX_STACK_SIZE = 1000;
520 		private double[] stack = new double[MAX_STACK_SIZE];
521 		private int pos = 0;
522 
523 		void push(double x) throws RrdException {
524 			if (pos >= MAX_STACK_SIZE) {
525 				throw new RrdException("PUSH failed, RPN stack full [" + MAX_STACK_SIZE + "]");
526 			}
527 			stack[pos++] = x;
528 		}
529 
530 		double pop() throws RrdException {
531 			if (pos <= 0) {
532 				throw new RrdException("POP failed, RPN stack is empty ");
533 			}
534 			return stack[--pos];
535 		}
536 
537 		double peek() throws RrdException {
538 			if (pos <= 0) {
539 				throw new RrdException("PEEK failed, RPN stack is empty ");
540 			}
541 			return stack[pos - 1];
542 		}
543 
544 		void reset() {
545 			pos = 0;
546 		}
547 
548 		boolean isEmpty() {
549 			return pos <= 0;
550 		}
551 	}
552 
553 	private class Token {
554 		byte id = -1;
555 		double number = Double.NaN;
556 		String variable = null;
557 		double[] values = null;
558 	}
559 }