Package org.jrobin.data

Source Code of org.jrobin.data.RpnCalculator$Token

/*******************************************************************************
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
* Copyright (c) 2011 The OpenNMS Group, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*******************************************************************************/

package org.jrobin.data;

import org.jrobin.core.RrdException;
import org.jrobin.core.Util;

import java.util.Calendar;
import java.util.StringTokenizer;

class RpnCalculator {
  private static final byte TKN_VAR = 0;
  private static final byte TKN_NUM = 1;
  private static final byte TKN_PLUS = 2;
  private static final byte TKN_MINUS = 3;
  private static final byte TKN_MULT = 4;
  private static final byte TKN_DIV = 5;
  private static final byte TKN_MOD = 6;
  private static final byte TKN_SIN = 7;
  private static final byte TKN_COS = 8;
  private static final byte TKN_LOG = 9;
  private static final byte TKN_EXP = 10;
  private static final byte TKN_FLOOR = 11;
  private static final byte TKN_CEIL = 12;
  private static final byte TKN_ROUND = 13;
  private static final byte TKN_POW = 14;
  private static final byte TKN_ABS = 15;
  private static final byte TKN_SQRT = 16;
  private static final byte TKN_RANDOM = 17;
  private static final byte TKN_LT = 18;
  private static final byte TKN_LE = 19;
  private static final byte TKN_GT = 20;
  private static final byte TKN_GE = 21;
  private static final byte TKN_EQ = 22;
  private static final byte TKN_IF = 23;
  private static final byte TKN_MIN = 24;
  private static final byte TKN_MAX = 25;
  private static final byte TKN_LIMIT = 26;
  private static final byte TKN_DUP = 27;
  private static final byte TKN_EXC = 28;
  private static final byte TKN_POP = 29;
  private static final byte TKN_UN = 30;
  private static final byte TKN_UNKN = 31;
  private static final byte TKN_NOW = 32;
  private static final byte TKN_TIME = 33;
  private static final byte TKN_PI = 34;
  private static final byte TKN_E = 35;
  private static final byte TKN_AND = 36;
  private static final byte TKN_OR = 37;
  private static final byte TKN_XOR = 38;
  private static final byte TKN_PREV = 39;
  private static final byte TKN_INF = 40;
  private static final byte TKN_NEGINF = 41;
  private static final byte TKN_STEP = 42;
  private static final byte TKN_YEAR = 43;
  private static final byte TKN_MONTH = 44;
  private static final byte TKN_DATE = 45;
  private static final byte TKN_HOUR = 46;
  private static final byte TKN_MINUTE = 47;
  private static final byte TKN_SECOND = 48;
  private static final byte TKN_WEEK = 49;
  private static final byte TKN_SIGN = 50;
  private static final byte TKN_RND = 51;

  private String rpnExpression;
  private String sourceName;
  private DataProcessor dataProcessor;

  private Token[] tokens;
  private RpnStack stack = new RpnStack();
  private double[] calculatedValues;
  private long[] timestamps;
  private double timeStep;

  RpnCalculator(String rpnExpression, String sourceName, DataProcessor dataProcessor) throws RrdException {
    this.rpnExpression = rpnExpression;
    this.sourceName = sourceName;
    this.dataProcessor = dataProcessor;
    this.timestamps = dataProcessor.getTimestamps();
    this.timeStep = this.timestamps[1] - this.timestamps[0];
    this.calculatedValues = new double[this.timestamps.length];
    StringTokenizer st = new StringTokenizer(rpnExpression, ", ");
    tokens = new Token[st.countTokens()];
    for (int i = 0; st.hasMoreTokens(); i++) {
      tokens[i] = createToken(st.nextToken());
    }
  }

  private Token createToken(String parsedText) throws RrdException {
    Token token = new Token();
    if (Util.isDouble(parsedText)) {
      token.id = TKN_NUM;
      token.number = Util.parseDouble(parsedText);
    }
    else if (parsedText.equals("+")) {
      token.id = TKN_PLUS;
    }
    else if (parsedText.equals("-")) {
      token.id = TKN_MINUS;
    }
    else if (parsedText.equals("*")) {
      token.id = TKN_MULT;
    }
    else if (parsedText.equals("/")) {
      token.id = TKN_DIV;
    }
    else if (parsedText.equals("%")) {
      token.id = TKN_MOD;
    }
    else if (parsedText.equals("SIN")) {
      token.id = TKN_SIN;
    }
    else if (parsedText.equals("COS")) {
      token.id = TKN_COS;
    }
    else if (parsedText.equals("LOG")) {
      token.id = TKN_LOG;
    }
    else if (parsedText.equals("EXP")) {
      token.id = TKN_EXP;
    }
    else if (parsedText.equals("FLOOR")) {
      token.id = TKN_FLOOR;
    }
    else if (parsedText.equals("CEIL")) {
      token.id = TKN_CEIL;
    }
    else if (parsedText.equals("ROUND")) {
      token.id = TKN_ROUND;
    }
    else if (parsedText.equals("POW")) {
      token.id = TKN_POW;
    }
    else if (parsedText.equals("ABS")) {
      token.id = TKN_ABS;
    }
    else if (parsedText.equals("SQRT")) {
      token.id = TKN_SQRT;
    }
    else if (parsedText.equals("RANDOM")) {
      token.id = TKN_RANDOM;
    }
    else if (parsedText.equals("LT")) {
      token.id = TKN_LT;
    }
    else if (parsedText.equals("LE")) {
      token.id = TKN_LE;
    }
    else if (parsedText.equals("GT")) {
      token.id = TKN_GT;
    }
    else if (parsedText.equals("GE")) {
      token.id = TKN_GE;
    }
    else if (parsedText.equals("EQ")) {
      token.id = TKN_EQ;
    }
    else if (parsedText.equals("IF")) {
      token.id = TKN_IF;
    }
    else if (parsedText.equals("MIN")) {
      token.id = TKN_MIN;
    }
    else if (parsedText.equals("MAX")) {
      token.id = TKN_MAX;
    }
    else if (parsedText.equals("LIMIT")) {
      token.id = TKN_LIMIT;
    }
    else if (parsedText.equals("DUP")) {
      token.id = TKN_DUP;
    }
    else if (parsedText.equals("EXC")) {
      token.id = TKN_EXC;
    }
    else if (parsedText.equals("POP")) {
      token.id = TKN_POP;
    }
    else if (parsedText.equals("UN")) {
      token.id = TKN_UN;
    }
    else if (parsedText.equals("UNKN")) {
      token.id = TKN_UNKN;
    }
    else if (parsedText.equals("NOW")) {
      token.id = TKN_NOW;
    }
    else if (parsedText.equals("TIME")) {
      token.id = TKN_TIME;
    }
    else if (parsedText.equals("PI")) {
      token.id = TKN_PI;
    }
    else if (parsedText.equals("E")) {
      token.id = TKN_E;
    }
    else if (parsedText.equals("AND")) {
      token.id = TKN_AND;
    }
    else if (parsedText.equals("OR")) {
      token.id = TKN_OR;
    }
    else if (parsedText.equals("XOR")) {
      token.id = TKN_XOR;
    }
    else if (parsedText.equals("PREV")) {
      token.id = TKN_PREV;
      token.variable = sourceName;
      token.values = calculatedValues;
    }
    else if (parsedText.startsWith("PREV(") && parsedText.endsWith(")")) {
      token.id = TKN_PREV;
      token.variable = parsedText.substring(5, parsedText.length() - 1);
      token.values = dataProcessor.getValues(token.variable);
    }
    else if (parsedText.equals("INF")) {
      token.id = TKN_INF;
    }
    else if (parsedText.equals("NEGINF")) {
      token.id = TKN_NEGINF;
    }
    else if (parsedText.equals("STEP")) {
      token.id = TKN_STEP;
    }
    else if (parsedText.equals("YEAR")) {
      token.id = TKN_YEAR;
    }
    else if (parsedText.equals("MONTH")) {
      token.id = TKN_MONTH;
    }
    else if (parsedText.equals("DATE")) {
      token.id = TKN_DATE;
    }
    else if (parsedText.equals("HOUR")) {
      token.id = TKN_HOUR;
    }
    else if (parsedText.equals("MINUTE")) {
      token.id = TKN_MINUTE;
    }
    else if (parsedText.equals("SECOND")) {
      token.id = TKN_SECOND;
    }
    else if (parsedText.equals("WEEK")) {
      token.id = TKN_WEEK;
    }
    else if (parsedText.equals("SIGN")) {
      token.id = TKN_SIGN;
    }
    else if (parsedText.equals("RND")) {
      token.id = TKN_RND;
    }
    else {
      token.id = TKN_VAR;
      token.variable = parsedText;
      token.values = dataProcessor.getValues(token.variable);
    }
    return token;
  }

  double[] calculateValues() throws RrdException {
    for (int slot = 0; slot < timestamps.length; slot++) {
      resetStack();
      for (Token token : tokens) {
        double x1, x2, x3;
        switch (token.id) {
          case TKN_NUM:
            push(token.number);
            break;
          case TKN_VAR:
            push(token.values[slot]);
            break;
          case TKN_PLUS:
            push(pop() + pop());
            break;
          case TKN_MINUS:
            x2 = pop();
            x1 = pop();
            push(x1 - x2);
            break;
          case TKN_MULT:
            push(pop() * pop());
            break;
          case TKN_DIV:
            x2 = pop();
            x1 = pop();
            push(x1 / x2);
            break;
          case TKN_MOD:
            x2 = pop();
            x1 = pop();
            push(x1 % x2);
            break;
          case TKN_SIN:
            push(Math.sin(pop()));
            break;
          case TKN_COS:
            push(Math.cos(pop()));
            break;
          case TKN_LOG:
            push(Math.log(pop()));
            break;
          case TKN_EXP:
            push(Math.exp(pop()));
            break;
          case TKN_FLOOR:
            push(Math.floor(pop()));
            break;
          case TKN_CEIL:
            push(Math.ceil(pop()));
            break;
          case TKN_ROUND:
            push(Math.round(pop()));
            break;
          case TKN_POW:
            x2 = pop();
            x1 = pop();
            push(Math.pow(x1, x2));
            break;
          case TKN_ABS:
            push(Math.abs(pop()));
            break;
          case TKN_SQRT:
            push(Math.sqrt(pop()));
            break;
          case TKN_RANDOM:
            push(Math.random());
            break;
          case TKN_LT:
            x2 = pop();
            x1 = pop();
            push(x1 < x2 ? 1 : 0);
            break;
          case TKN_LE:
            x2 = pop();
            x1 = pop();
            push(x1 <= x2 ? 1 : 0);
            break;
          case TKN_GT:
            x2 = pop();
            x1 = pop();
            push(x1 > x2 ? 1 : 0);
            break;
          case TKN_GE:
            x2 = pop();
            x1 = pop();
            push(x1 >= x2 ? 1 : 0);
            break;
          case TKN_EQ:
            x2 = pop();
            x1 = pop();
            push(x1 == x2 ? 1 : 0);
            break;
          case TKN_IF:
            x3 = pop();
            x2 = pop();
            x1 = pop();
            push(x1 != 0 ? x2 : x3);
            break;
          case TKN_MIN:
            push(Math.min(pop(), pop()));
            break;
          case TKN_MAX:
            push(Math.max(pop(), pop()));
            break;
          case TKN_LIMIT:
            x3 = pop();
            x2 = pop();
            x1 = pop();
            push(x1 < x2 || x1 > x3 ? Double.NaN : x1);
            break;
          case TKN_DUP:
            push(peek());
            break;
          case TKN_EXC:
            x2 = pop();
            x1 = pop();
            push(x2);
            push(x1);
            break;
          case TKN_POP:
            pop();
            break;
          case TKN_UN:
            push(Double.isNaN(pop()) ? 1 : 0);
            break;
          case TKN_UNKN:
            push(Double.NaN);
            break;
          case TKN_NOW:
            push(Util.getTime());
            break;
          case TKN_TIME:
            push((long) Math.round(timestamps[slot]));
            break;
          case TKN_PI:
            push(Math.PI);
            break;
          case TKN_E:
            push(Math.E);
            break;
          case TKN_AND:
            x2 = pop();
            x1 = pop();
            push((x1 != 0 && x2 != 0) ? 1 : 0);
            break;
          case TKN_OR:
            x2 = pop();
            x1 = pop();
            push((x1 != 0 || x2 != 0) ? 1 : 0);
            break;
          case TKN_XOR:
            x2 = pop();
            x1 = pop();
            push(((x1 != 0 && x2 == 0) || (x1 == 0 && x2 != 0)) ? 1 : 0);
            break;
          case TKN_PREV:
            push((slot == 0) ? Double.NaN : token.values[slot - 1]);
            break;
          case TKN_INF:
            push(Double.POSITIVE_INFINITY);
            break;
          case TKN_NEGINF:
            push(Double.NEGATIVE_INFINITY);
            break;
          case TKN_STEP:
            push(timeStep);
            break;
          case TKN_YEAR:
            push(getCalendarField(pop(), Calendar.YEAR));
            break;
          case TKN_MONTH:
            push(getCalendarField(pop(), Calendar.MONTH));
            break;
          case TKN_DATE:
            push(getCalendarField(pop(), Calendar.DAY_OF_MONTH));
            break;
          case TKN_HOUR:
            push(getCalendarField(pop(), Calendar.HOUR_OF_DAY));
            break;
          case TKN_MINUTE:
            push(getCalendarField(pop(), Calendar.MINUTE));
            break;
          case TKN_SECOND:
            push(getCalendarField(pop(), Calendar.SECOND));
            break;
          case TKN_WEEK:
            push(getCalendarField(pop(), Calendar.WEEK_OF_YEAR));
            break;
          case TKN_SIGN:
            x1 = pop();
            push(Double.isNaN(x1) ? Double.NaN : x1 > 0 ? +1 : x1 < 0 ? -1 : 0);
            break;
          case TKN_RND:
            push(Math.floor(pop() * Math.random()));
            break;
          default:
            throw new RrdException("Unexpected RPN token encountered, token.id=" + token.id);
        }
      }
      calculatedValues[slot] = pop();
      // check if stack is empty only on the first try
      if (slot == 0 && !isStackEmpty()) {
        throw new RrdException("Stack not empty at the end of calculation. " +
            "Probably bad RPN expression [" + rpnExpression + "]");
      }
    }
    return calculatedValues;
  }

  private double getCalendarField(double timestamp, int field) {
    Calendar calendar = Util.getCalendar((long) (timestamp * 1000));
    return calendar.get(field);
  }

  private void push(double x) throws RrdException {
    stack.push(x);
  }

  private double pop() throws RrdException {
    return stack.pop();
  }

  private double peek() throws RrdException {
    return stack.peek();
  }

  private void resetStack() {
    stack.reset();
  }

  private boolean isStackEmpty() {
    return stack.isEmpty();
  }

  private static final class RpnStack {
    private static final int MAX_STACK_SIZE = 1000;
    private double[] stack = new double[MAX_STACK_SIZE];
    private int pos = 0;

    void push(final double x) throws RrdException {
      if (pos >= MAX_STACK_SIZE) {
        throw new RrdException("PUSH failed, RPN stack full [" + MAX_STACK_SIZE + "]");
      }
      stack[pos++] = x;
    }

    double pop() throws RrdException {
      if (pos <= 0) {
        throw new RrdException("POP failed, RPN stack is empty ");
      }
      return stack[--pos];
    }

    double peek() throws RrdException {
      if (pos <= 0) {
        throw new RrdException("PEEK failed, RPN stack is empty ");
      }
      return stack[pos - 1];
    }

    void reset() {
      pos = 0;
    }

    boolean isEmpty() {
      return pos <= 0;
    }
  }

  private static final class Token {
    byte id = -1;
    double number = Double.NaN;
    String variable = null;
    double[] values = null;
  }
}
TOP

Related Classes of org.jrobin.data.RpnCalculator$Token

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.