Package org.jrobin.core.timespec

Source Code of org.jrobin.core.timespec.TimeParser

/*******************************************************************************
* 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
*******************************************************************************/

/*
* Java port of Tobi's original parsetime.c routine
*/
package org.jrobin.core.timespec;

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

/**
* Class which parses at-style time specification (describided in detail on the rrdfetch man page),
* used in all RRDTool commands. This code is in most parts just a java port of Tobi's parsetime.c
* code.
*/
public class TimeParser {
  private static final int PREVIOUS_OP = -1;

  TimeToken token;
  TimeScanner scanner;
  TimeSpec spec;

  int op = TimeToken.PLUS;
  int prev_multiplier = -1;

  /**
   * Constructs TimeParser instance from the given input string.
   *
   * @param dateString at-style time specification (read rrdfetch man page
   *                   for the complete explanation)
   */
  public TimeParser(String dateString) {
    scanner = new TimeScanner(dateString);
    spec = new TimeSpec(dateString);
  }

  private void expectToken(int desired, String errorMessage) throws RrdException {
    token = scanner.nextToken();
    if (token.id != desired) {
      throw new RrdException(errorMessage);
    }
  }

  private void plusMinus(int doop) throws RrdException {
    if (doop >= 0) {
      op = doop;
      expectToken(TimeToken.NUMBER, "There should be number after " +
          (op == TimeToken.PLUS ? '+' : '-'));
      prev_multiplier = -1; /* reset months-minutes guessing mechanics */
    }
    int delta = Integer.parseInt(token.value);
    token = scanner.nextToken();
    if (token.id == TimeToken.MONTHS_MINUTES) {
      /* hard job to guess what does that -5m means: -5mon or -5min? */
      switch (prev_multiplier) {
        case TimeToken.DAYS:
        case TimeToken.WEEKS:
        case TimeToken.MONTHS:
        case TimeToken.YEARS:
          token = scanner.resolveMonthsMinutes(TimeToken.MONTHS);
          break;
        case TimeToken.SECONDS:
        case TimeToken.MINUTES:
        case TimeToken.HOURS:
          token = scanner.resolveMonthsMinutes(TimeToken.MINUTES);
          break;
        default:
          if (delta < 6) {
            token = scanner.resolveMonthsMinutes(TimeToken.MONTHS);
          }
          else {
            token = scanner.resolveMonthsMinutes(TimeToken.MINUTES);
          }
      }
    }
    prev_multiplier = token.id;
    delta *= (op == TimeToken.PLUS) ? +1 : -1;
    switch (token.id) {
      case TimeToken.YEARS:
        spec.dyear += delta;
        break;
      case TimeToken.MONTHS:
        spec.dmonth += delta;
        break;
      case TimeToken.WEEKS:
        delta *= 7;
        spec.dday += delta;
        break;
      case TimeToken.DAYS:
        spec.dday += delta;
        break;
      case TimeToken.HOURS:
        spec.dhour += delta;
        break;
      case TimeToken.MINUTES:
        spec.dmin += delta;
        break;
      case TimeToken.SECONDS:
      default: // default is 'seconds'
        spec.dsec += delta;
        break;
    }
    // unreachable statement
    // throw new RrdException("Well-known time unit expected after " + delta);
  }

  private void timeOfDay() throws RrdException {
    int hour, minute = 0;
    /* save token status in case we must abort */
    scanner.saveState();
    /* first pick out the time of day - we assume a HH (COLON|DOT) MM time */
    if (token.value.length() > 2) {
      return;
    }
    hour = Integer.parseInt(token.value);
    token = scanner.nextToken();
    if (token.id == TimeToken.SLASH || token.id == TimeToken.DOT) {
      /* guess we are looking at a date */
      token = scanner.restoreState();
      return;
    }
    if (token.id == TimeToken.COLON) {
      expectToken(TimeToken.NUMBER, "Parsing HH:MM syntax, expecting MM as number, got none");
      minute = Integer.parseInt(token.value);
      if (minute > 59) {
        throw new RrdException("Parsing HH:MM syntax, got MM = " +
            minute + " (>59!)");
      }
      token = scanner.nextToken();
    }
    /* check if an AM or PM specifier was given */
    if (token.id == TimeToken.AM || token.id == TimeToken.PM) {
      if (hour > 12) {
        throw new RrdException("There cannot be more than 12 AM or PM hours");
      }
      if (token.id == TimeToken.PM) {
        if (hour != 12) {
          /* 12:xx PM is 12:xx, not 24:xx */
          hour += 12;
        }
      }
      else {
        if (hour == 12) {
          /* 12:xx AM is 00:xx, not 12:xx */
          hour = 0;
        }
      }
      token = scanner.nextToken();
    }
    else if (hour > 23) {
      /* guess it was not a time then ... */
      token = scanner.restoreState();
      return;
    }
    spec.hour = hour;
    spec.min = minute;
    spec.sec = 0;
    if (spec.hour == 24) {
      spec.hour = 0;
      spec.day++;
    }
  }

  private void assignDate(long mday, long mon, long year) throws RrdException {
    if (year > 138) {
      if (year > 1970) {
        year -= 1900;
      }
      else {
        throw new RrdException("Invalid year " + year +
            " (should be either 00-99 or >1900)");
      }
    }
    else if (year >= 0 && year < 38) {
      year += 100;     /* Allow year 2000-2037 to be specified as   */
    }             /* 00-37 until the problem of 2038 year will */
    /* arise for unices with 32-bit time_t     */
    if (year < 70) {
      throw new RrdException("Won't handle dates before epoch (01/01/1970), sorry");
    }
    spec.year = (int) year;
    spec.month = (int) mon;
    spec.day = (int) mday;
  }

  private void day() throws RrdException {
    long mday = 0, wday, mon, year = spec.year;
    switch (token.id) {
      case TimeToken.YESTERDAY:
        spec.day--;
        token = scanner.nextToken();
        break;
      case TimeToken.TODAY:  /* force ourselves to stay in today - no further processing */
        token = scanner.nextToken();
        break;
      case TimeToken.TOMORROW:
        spec.day++;
        token = scanner.nextToken();
        break;
      case TimeToken.JAN:
      case TimeToken.FEB:
      case TimeToken.MAR:
      case TimeToken.APR:
      case TimeToken.MAY:
      case TimeToken.JUN:
      case TimeToken.JUL:
      case TimeToken.AUG:
      case TimeToken.SEP:
      case TimeToken.OCT:
      case TimeToken.NOV:
      case TimeToken.DEC:
        /* do month mday [year] */
        mon = (token.id - TimeToken.JAN);
        expectToken(TimeToken.NUMBER, "the day of the month should follow month name");
        mday = Long.parseLong(token.value);
        token = scanner.nextToken();
        if (token.id == TimeToken.NUMBER) {
          year = Long.parseLong(token.value);
          token = scanner.nextToken();
        }
        else {
          year = spec.year;
        }
        assignDate(mday, mon, year);
        break;
      case TimeToken.SUN:
      case TimeToken.MON:
      case TimeToken.TUE:
      case TimeToken.WED:
      case TimeToken.THU:
      case TimeToken.FRI:
      case TimeToken.SAT:
        /* do a particular day of the week */
        wday = (token.id - TimeToken.SUN);
        spec.day += (wday - spec.wday);
        token = scanner.nextToken();
        break;
      case TimeToken.NUMBER:
        /* get numeric <sec since 1970>, MM/DD/[YY]YY, or DD.MM.[YY]YY */
        // int tlen = token.value.length();
        mon = Long.parseLong(token.value);
        if (mon > 10L * 365L * 24L * 60L * 60L) {
          spec.localtime(mon);
          token = scanner.nextToken();
          break;
        }
        if (mon > 19700101 && mon < 24000101) { /*works between 1900 and 2400 */
          year = mon / 10000;
          mday = mon % 100;
          mon = (mon / 100) % 100;
          token = scanner.nextToken();
        }
        else {
          token = scanner.nextToken();
          if (mon <= 31 && (token.id == TimeToken.SLASH || token.id == TimeToken.DOT)) {
            int sep = token.id;
            expectToken(TimeToken.NUMBER, "there should be " +
                (sep == TimeToken.DOT ? "month" : "day") +
                " number after " +
                (sep == TimeToken.DOT ? '.' : '/'));
            mday = Long.parseLong(token.value);
            token = scanner.nextToken();
            if (token.id == sep) {
              expectToken(TimeToken.NUMBER, "there should be year number after " +
                  (sep == TimeToken.DOT ? '.' : '/'));
              year = Long.parseLong(token.value);
              token = scanner.nextToken();
            }
            /* flip months and days for European timing */
            if (sep == TimeToken.DOT) {
              long x = mday;
              mday = mon;
              mon = x;
            }
          }
        }
        mon--;
        if (mon < 0 || mon > 11) {
          throw new RrdException("Did you really mean month " + (mon + 1));
        }
        if (mday < 1 || mday > 31) {
          throw new RrdException("I'm afraid that " + mday +
              " is not a valid day of the month");
        }
        assignDate(mday, mon, year);
        break;
    }
  }

  /**
   * Parses the input string specified in the constructor.
   *
   * @return Object representing parsed date/time.
   * @throws RrdException Thrown if the date string cannot be parsed.
   */
  public TimeSpec parse() throws RrdException {
    long now = Util.getTime();
    int hr = 0;
    /* this MUST be initialized to zero for midnight/noon/teatime */
    /* establish the default time reference */
    spec.localtime(now);
    token = scanner.nextToken();
    switch (token.id) {
      case TimeToken.PLUS:
      case TimeToken.MINUS:
        break; /* jump to OFFSET-SPEC part */
      case TimeToken.START:
        spec.type = TimeSpec.TYPE_START;
        /* FALLTHRU */
      case TimeToken.END:
        if (spec.type != TimeSpec.TYPE_START) {
          spec.type = TimeSpec.TYPE_END;
        }
        spec.year = spec.month = spec.day = spec.hour = spec.min = spec.sec = 0;
        /* FALLTHRU */
      case TimeToken.NOW:
        int time_reference = token.id;
        token = scanner.nextToken();
        if (token.id == TimeToken.PLUS || token.id == TimeToken.MINUS) {
          break;
        }
        if (time_reference != TimeToken.NOW) {
          throw new RrdException("Words 'start' or 'end' MUST be followed by +|- offset");
        }
        else if (token.id != TimeToken.EOF) {
          throw new RrdException("If 'now' is followed by a token it must be +|- offset");
        }
        break;
        /* Only absolute time specifications below */
      case TimeToken.NUMBER:
        timeOfDay();
        if (token.id != TimeToken.NUMBER) {
          break;
        }
        /* fix month parsing */
      case TimeToken.JAN:
      case TimeToken.FEB:
      case TimeToken.MAR:
      case TimeToken.APR:
      case TimeToken.MAY:
      case TimeToken.JUN:
      case TimeToken.JUL:
      case TimeToken.AUG:
      case TimeToken.SEP:
      case TimeToken.OCT:
      case TimeToken.NOV:
      case TimeToken.DEC:
        day();
        if (token.id != TimeToken.NUMBER) {
          break;
        }
        timeOfDay();
        break;

        /* evil coding for TEATIME|NOON|MIDNIGHT - we've initialized
         * hr to zero up above, then fall into this case in such a
         * way so we add +12 +4 hours to it for teatime, +12 hours
         * to it for noon, and nothing at all for midnight, then
         * set our rettime to that hour before leaping into the
         * month scanner
         */
      case TimeToken.TEATIME:
        hr += 4;
        /* FALLTHRU */
      case TimeToken.NOON:
        hr += 12;
        /* FALLTHRU */
      case TimeToken.MIDNIGHT:
        spec.hour = hr;
        spec.min = 0;
        spec.sec = 0;
        token = scanner.nextToken();
        day();
        break;
      default:
        throw new RrdException("Unparsable time: " + token.value);
    }

    /*
     * the OFFSET-SPEC part
     *
     * (NOTE, the sc_tokid was prefetched for us by the previous code)
     */
    if (token.id == TimeToken.PLUS || token.id == TimeToken.MINUS) {
      scanner.setContext(false);
      while (token.id == TimeToken.PLUS || token.id == TimeToken.MINUS ||
          token.id == TimeToken.NUMBER) {
        if (token.id == TimeToken.NUMBER) {
          plusMinus(PREVIOUS_OP);
        }
        else {
          plusMinus(token.id);
        }
        token = scanner.nextToken();
        /* We will get EOF eventually but that's OK, since
        token() will return us as many EOFs as needed */
      }
    }
    /* now we should be at EOF */
    if (token.id != TimeToken.EOF) {
      throw new RrdException("Unparsable trailing text: " + token.value);
    }
    return spec;
  }
}
TOP

Related Classes of org.jrobin.core.timespec.TimeParser

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.