From: Sasa M. <sa...@us...> - 2005-02-17 10:43:27
|
Update of /cvsroot/jrobin/src/org/jrobin/core/timespec In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv5165/org/jrobin/core/timespec Added Files: Epoch.java TimeParser.java TimeScanner.java TimeSpec.java TimeToken.java Log Message: Classes used to parse AT-Style time specification moved to a separate package (org.jrobin.core.timespec). --- NEW FILE: TimeSpec.java --- /* ============================================================ * JRobin : Pure java implementation of RRDTool's functionality * ============================================================ * * Project Info: http://www.jrobin.org * Project Lead: Sasa Markovic (sa...@jr...); * * (C) Copyright 2003, by Sasa Markovic. * * Developers: Sasa Markovic (sa...@jr...) * Arne Vandamme (cob...@jr...) * * 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., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. */ package org.jrobin.core.timespec; import org.jrobin.core.RrdException; import org.jrobin.core.Util; import java.util.GregorianCalendar; import java.util.Date; import java.util.Calendar; /** * Simple class to represent time obtained by parsing at-style date specification (described * in detail on the rrdfetch man page. See javadoc {@link org.jrobin.core.timespec.TimeParser} for more information. */ public class TimeSpec { static final int TYPE_ABSOLUTE = 0; static final int TYPE_START = 1; static final int TYPE_END = 2; int type = TYPE_ABSOLUTE; int year, month, day, hour, min, sec; int wday; int dyear, dmonth, dday, dhour, dmin, dsec; String dateString; TimeSpec context; TimeSpec(String dateString) { this.dateString = dateString; } void localtime(long timestamp) { GregorianCalendar date = new GregorianCalendar(); date.setTime(new Date(timestamp * 1000L)); year = date.get(Calendar.YEAR) - 1900; month = date.get(Calendar.MONTH); day = date.get(Calendar.DAY_OF_MONTH); hour = date.get(Calendar.HOUR_OF_DAY); min = date.get(Calendar.MINUTE); sec = date.get(Calendar.SECOND); wday = date.get(Calendar.DAY_OF_WEEK) - Calendar.SUNDAY; } GregorianCalendar getTime() throws RrdException { GregorianCalendar gc; // absoulte time, this is easy if(type == TYPE_ABSOLUTE) { gc = new GregorianCalendar(year + 1900, month, day, hour, min, sec); } // relative time, we need a context to evaluate it else if(context != null && context.type == TYPE_ABSOLUTE) { gc = context.getTime(); } // how would I guess what time it was? else { throw new RrdException("Relative times like '" + dateString + "' require proper absolute context to be evaluated"); } gc.add(Calendar.YEAR, dyear); gc.add(Calendar.MONTH, dmonth); gc.add(Calendar.DAY_OF_MONTH, dday); gc.add(Calendar.HOUR_OF_DAY, dhour); gc.add(Calendar.MINUTE, dmin); gc.add(Calendar.SECOND, dsec); return gc; } /** * Returns the corresponding timestamp (seconds since Epoch). Example:<p> * <pre> * TimeParser p = new TimeParser("now-1day"); * TimeSpec ts = p.parse(); * System.out.println("Timestamp was: " + ts.getTimestamp(); * </pre> * @return Timestamp (in seconds, no milliseconds) * @throws RrdException Thrown if this TimeSpec object does not represent absolute time. */ public long getTimestamp() throws RrdException { return Util.getTimestamp(getTime()); } String dump() { return (type == TYPE_ABSOLUTE? "ABSTIME": type == TYPE_START? "START": "END") + ": " + year + "/" + month + "/" + day + "/" + hour + "/" + min + "/" + sec + " (" + dyear + "/" + dmonth + "/" + dday + "/" + dhour + "/" + dmin + "/" + dsec + ")"; } /** * Use this static method to resolve relative time references and obtain the corresponding * GregorianCalendar objects. Example:<p> * <pre> * TimeParser pStart = new TimeParser("now-1month"); // starting time * TimeParser pEnd = new TimeParser("start+1week"); // ending time * TimeSpec specStart = pStart.parse(); * TimeSpec specEnd = pEnd.parse(); * GregorianCalendar[] gc = TimeSpec.getTimes(specStart, specEnd); * </pre> * @param spec1 Starting time specification * @param spec2 Ending time specification * @return * @throws RrdException Thrown if relative time references cannot be resolved */ public static GregorianCalendar[] getTimes(TimeSpec spec1, TimeSpec spec2) throws RrdException { if(spec1.type == TYPE_START || spec2.type == TYPE_END) { throw new RrdException("Recursive time specifications not allowed"); } spec1.context = spec2; spec2.context = spec1; return new GregorianCalendar[] { spec1.getTime(), spec2.getTime() }; } /** * Use this static method to resolve relative time references and obtain the corresponding * timestamps (seconds since epoch). Example:<p> * <pre> * TimeParser pStart = new TimeParser("now-1month"); // starting time * TimeParser pEnd = new TimeParser("start+1week"); // ending time * TimeSpec specStart = pStart.parse(); * TimeSpec specEnd = pEnd.parse(); * long[] ts = TimeSpec.getTimestamps(specStart, specEnd); * </pre> * @param spec1 Starting time specification * @param spec2 Ending time specification * @return * @throws RrdException Thrown if relative time references cannot be resolved */ public static long[] getTimestamps(TimeSpec spec1, TimeSpec spec2) throws RrdException { GregorianCalendar[] gcs = getTimes(spec1, spec2); return new long[] { Util.getTimestamp(gcs[0]), Util.getTimestamp(gcs[1]) }; } } --- NEW FILE: TimeParser.java --- /* ============================================================ * JRobin : Pure java implementation of RRDTool's functionality * ============================================================ * * Project Info: http://www.jrobin.org * Project Lead: Sasa Markovic (sa...@jr...); * * (C) Copyright 2003, by Sasa Markovic. * * Developers: Sasa Markovic (sa...@jr...) * Arne Vandamme (cob...@jr...) * * 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., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, 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; return; case TimeToken.MONTHS: spec.dmonth += delta; return; case TimeToken.WEEKS: delta *= 7; /* FALLTHRU */ case TimeToken.DAYS: spec.dday += delta; return; case TimeToken.HOURS: spec.dhour += delta; return; case TimeToken.MINUTES: spec.dmin += delta; return; case TimeToken.SECONDS: default: // default is 'seconds' spec.dsec += delta; return; } // 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--; /* FALLTRHU */ 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; } /* public static void main(String[] args) throws IOException { BufferedReader r = new BufferedReader(new InputStreamReader(System.in)); while (true) { String s = r.readLine(); try { TimeParser p = new TimeParser(s); TimeSpec spec = p.parse(); System.out.println(spec.getTime().getTime()); // System.out.println(spec.dump()); } catch (RrdException e) { System.err.println("ERROR: " + e); } } } */ } --- NEW FILE: Epoch.java --- package org.jrobin.core.timespec; import org.jrobin.core.RrdException; import org.jrobin.core.Util; import javax.swing.*; import java.util.Date; import java.awt.*; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; import java.text.SimpleDateFormat; import java.text.ParseException; class Epoch extends JFrame implements Runnable { private static final String[] formats = { "MM/dd/yy HH:mm:ss", "dd.MM.yy HH:mm:ss", "yy-MM-dd HH:mm:ss", "MM/dd/yy HH:mm", "dd.MM.yy HH:mm", "yy-MM-dd HH:mm", "MM/dd/yy", "dd.MM.yy", "yy-MM-dd", "HH:mm MM/dd/yy", "HH:mm dd.MM.yy", "HH:mm yy-MM-dd", "HH:mm:ss MM/dd/yy", "HH:mm:ss dd.MM.yy", "HH:mm:ss yy-MM-dd" }; private static final SimpleDateFormat[] parsers = new SimpleDateFormat[formats.length]; private static final String helpText; static { for(int i = 0; i < parsers.length; i++) { parsers[i] = new SimpleDateFormat(formats[i]); parsers[i].setLenient(true); } StringBuffer tooltipBuff = new StringBuffer("<html><b>Supported input formats:</b><br>"); for(int i = 0; i < formats.length; i++) { tooltipBuff.append(formats[i] + "<br>"); } tooltipBuff.append("<b>RRDTool time format</b><br>"); tooltipBuff.append("... including timestamps</html>"); helpText = tooltipBuff.toString(); } private JLabel topLabel = new JLabel("Enter timestamp or readable date:"); private JTextField inputField = new JTextField(25); private JButton button = new JButton("Convert"); //private JLabel helpLabel = new JLabel(helpText); private static final SimpleDateFormat OUTPUT_DATE_FORMAT = new SimpleDateFormat("MM/dd/yy HH:mm:ss EEE"); Epoch() { super("Epoch"); constructUI(); setVisible(true); new Thread(this).start(); } private void constructUI() { JPanel c = (JPanel) getContentPane(); c.setLayout(new BorderLayout()); c.add(topLabel, BorderLayout.NORTH); c.add(inputField, BorderLayout.CENTER); c.add(button, BorderLayout.EAST); // c.add(helpLabel, BorderLayout.WEST); button.setToolTipText(helpText); button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { convert(); } }); inputField.requestFocus(); getRootPane().setDefaultButton(button); setResizable(false); setDefaultCloseOperation(EXIT_ON_CLOSE); pack(); centerOnScreen(); } void centerOnScreen() { Toolkit t = Toolkit.getDefaultToolkit(); Dimension screenSize = t.getScreenSize(); Dimension frameSize = getPreferredSize(); double x = (screenSize.getWidth() - frameSize.getWidth()) / 2; double y = (screenSize.getHeight() - frameSize.getHeight()) / 2; setLocation((int) x, (int) y); } private void convert() { String time = inputField.getText().trim(); if(time.length() > 0) { // try simple timestamp try { long timestamp = Long.parseLong(time); Date date = new Date(timestamp * 1000L); formatDate(date); } catch(NumberFormatException nfe) { // failed, try as a date try { inputField.setText("" + parseDate(time)); } catch (RrdException e) { inputField.setText("Could not convert, sorry"); } } } } public void run() { for(;;) { Date now = new Date(); long timestamp = now.getTime() / 1000L; setTitle(timestamp + " seconds since epoch"); try { Thread.sleep(1000L); } catch (InterruptedException e) { } } } void formatDate(Date date) { inputField.setText(OUTPUT_DATE_FORMAT.format(date)); } private long parseDate(String time) throws RrdException { for(int i = 0; i < parsers.length; i++) { try { return Util.getTimestamp(parsers[i].parse(time)); } catch (ParseException e) { } } return new TimeParser(time).parse().getTimestamp(); } public static void main(String[] args) { new Epoch(); } } --- NEW FILE: TimeToken.java --- /* ============================================================ * JRobin : Pure java implementation of RRDTool's functionality * ============================================================ * * Project Info: http://www.jrobin.org * Project Lead: Sasa Markovic (sa...@jr...); * * (C) Copyright 2003, by Sasa Markovic. * * Developers: Sasa Markovic (sa...@jr...) * Arne Vandamme (cob...@jr...) * * 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., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. */ package org.jrobin.core.timespec; class TimeToken { public static final int MIDNIGHT = 1; public static final int NOON = 2; public static final int TEATIME = 3; public static final int PM = 4; public static final int AM = 5; public static final int YESTERDAY = 6; public static final int TODAY = 7; public static final int TOMORROW = 8; public static final int NOW = 9; public static final int START = 10; public static final int END = 11; public static final int SECONDS = 12; public static final int MINUTES = 13; public static final int HOURS = 14; public static final int DAYS = 15; public static final int WEEKS = 16; public static final int MONTHS = 17; public static final int YEARS = 18; public static final int MONTHS_MINUTES = 19; public static final int NUMBER = 20; public static final int PLUS = 21; public static final int MINUS = 22; public static final int DOT = 23; public static final int COLON = 24; public static final int SLASH = 25; public static final int ID = 26; public static final int JUNK = 27; public static final int JAN = 28; public static final int FEB = 29; public static final int MAR = 30; public static final int APR = 31; public static final int MAY = 32; public static final int JUN = 33; public static final int JUL = 34; public static final int AUG = 35; public static final int SEP = 36; public static final int OCT = 37; public static final int NOV = 38; public static final int DEC = 39; public static final int SUN = 40; public static final int MON = 41; public static final int TUE = 42; public static final int WED = 43; public static final int THU = 44; public static final int FRI = 45; public static final int SAT = 46; public static final int EOF = -1; final String value; /* token name */ final int id; /* token id */ public TimeToken(String value, int id) { this.value = value; this.id = id; } public String toString() { return value + " [" + id + "]"; } } --- NEW FILE: TimeScanner.java --- /* ============================================================ * JRobin : Pure java implementation of RRDTool's functionality * ============================================================ * * Project Info: http://www.jrobin.org * Project Lead: Sasa Markovic (sa...@jr...); * * (C) Copyright 2003, by Sasa Markovic. * * Developers: Sasa Markovic (sa...@jr...) * Arne Vandamme (cob...@jr...) * * 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., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. */ package org.jrobin.core.timespec; class TimeScanner { private String dateString; private int pos, pos_save; private TimeToken token, token_save; static final TimeToken[] WORDS = { new TimeToken("midnight", TimeToken.MIDNIGHT), /* 00:00:00 of today or tomorrow */ new TimeToken("noon", TimeToken.NOON), /* 12:00:00 of today or tomorrow */ new TimeToken("teatime", TimeToken.TEATIME), /* 16:00:00 of today or tomorrow */ new TimeToken("am", TimeToken.AM), /* morning times for 0-12 clock */ new TimeToken("pm", TimeToken.PM), /* evening times for 0-12 clock */ new TimeToken("tomorrow", TimeToken.TOMORROW), new TimeToken("yesterday", TimeToken.YESTERDAY), new TimeToken("today", TimeToken.TODAY), new TimeToken("now", TimeToken.NOW), new TimeToken("n", TimeToken.NOW), new TimeToken("start", TimeToken.START), new TimeToken("s", TimeToken.START), new TimeToken("end", TimeToken.END), new TimeToken("e", TimeToken.END), new TimeToken("jan", TimeToken.JAN), new TimeToken("feb", TimeToken.FEB), new TimeToken("mar", TimeToken.MAR), new TimeToken("apr", TimeToken.APR), new TimeToken("may", TimeToken.MAY), new TimeToken("jun", TimeToken.JUN), new TimeToken("jul", TimeToken.JUL), new TimeToken("aug", TimeToken.AUG), new TimeToken("sep", TimeToken.SEP), new TimeToken("oct", TimeToken.OCT), new TimeToken("nov", TimeToken.NOV), new TimeToken("dec", TimeToken.DEC), new TimeToken("january", TimeToken.JAN), new TimeToken("february", TimeToken.FEB), new TimeToken("march", TimeToken.MAR), new TimeToken("april", TimeToken.APR), new TimeToken("may", TimeToken.MAY), new TimeToken("june", TimeToken.JUN), new TimeToken("july", TimeToken.JUL), new TimeToken("august", TimeToken.AUG), new TimeToken("september", TimeToken.SEP), new TimeToken("october", TimeToken.OCT), new TimeToken("november", TimeToken.NOV), new TimeToken("december", TimeToken.DEC), new TimeToken("sunday", TimeToken.SUN), new TimeToken("sun", TimeToken.SUN), new TimeToken("monday", TimeToken.MON), new TimeToken("mon", TimeToken.MON), new TimeToken("tuesday", TimeToken.TUE), new TimeToken("tue", TimeToken.TUE), new TimeToken("wednesday", TimeToken.WED), new TimeToken("wed", TimeToken.WED), new TimeToken("thursday", TimeToken.THU), new TimeToken("thu", TimeToken.THU), new TimeToken("friday", TimeToken.FRI), new TimeToken("fri", TimeToken.FRI), new TimeToken("saturday", TimeToken.SAT), new TimeToken("sat", TimeToken.SAT), new TimeToken(null, 0) /*** SENTINEL ***/ }; static TimeToken[] MULTIPLIERS = { new TimeToken("second", TimeToken.SECONDS), /* seconds multiplier */ new TimeToken("seconds", TimeToken.SECONDS), /* (pluralized) */ new TimeToken("sec", TimeToken.SECONDS), /* (generic) */ new TimeToken("s", TimeToken.SECONDS), /* (short generic) */ new TimeToken("minute", TimeToken.MINUTES), /* minutes multiplier */ new TimeToken("minutes", TimeToken.MINUTES), /* (pluralized) */ new TimeToken("min", TimeToken.MINUTES), /* (generic) */ new TimeToken("m", TimeToken.MONTHS_MINUTES), /* (short generic) */ new TimeToken("hour", TimeToken.HOURS), /* hours ... */ new TimeToken("hours", TimeToken.HOURS), /* (pluralized) */ new TimeToken("hr", TimeToken.HOURS), /* (generic) */ new TimeToken("h", TimeToken.HOURS), /* (short generic) */ new TimeToken("day", TimeToken.DAYS), /* days ... */ new TimeToken("days", TimeToken.DAYS), /* (pluralized) */ new TimeToken("d", TimeToken.DAYS), /* (short generic) */ new TimeToken("week", TimeToken.WEEKS), /* week ... */ new TimeToken("weeks", TimeToken.WEEKS), /* (pluralized) */ new TimeToken("wk", TimeToken.WEEKS), /* (generic) */ new TimeToken("w", TimeToken.WEEKS), /* (short generic) */ new TimeToken("month", TimeToken.MONTHS), /* week ... */ new TimeToken("months", TimeToken.MONTHS), /* (pluralized) */ new TimeToken("mon", TimeToken.MONTHS), /* (generic) */ new TimeToken("year", TimeToken.YEARS), /* year ... */ new TimeToken("years", TimeToken.YEARS), /* (pluralized) */ new TimeToken("yr", TimeToken.YEARS), /* (generic) */ new TimeToken("y", TimeToken.YEARS), /* (short generic) */ new TimeToken(null, 0) /*** SENTINEL ***/ }; TimeToken[] specials = WORDS; public TimeScanner(String dateString) { this.dateString = dateString; } void setContext(boolean parsingWords) { specials = parsingWords? WORDS: MULTIPLIERS; } TimeToken nextToken() { StringBuffer buffer = new StringBuffer(""); while (pos < dateString.length()) { char c = dateString.charAt(pos++); if (Character.isWhitespace(c) || c == '_' || c == ',') { continue; } buffer.append(c); if (Character.isDigit(c)) { // pick as many digits as possible while (pos < dateString.length()) { char next = dateString.charAt(pos); if (Character.isDigit(next)) { buffer.append(next); pos++; } else { break; } } String value = buffer.toString(); return token = new TimeToken(value, TimeToken.NUMBER); } if (Character.isLetter(c)) { // pick as many letters as possible while (pos < dateString.length()) { char next = dateString.charAt(pos); if (Character.isLetter(next)) { buffer.append(next); pos++; } else { break; } } String value = buffer.toString(); return token = new TimeToken(value, parseToken(value)); } switch (c) { case ':': return token = new TimeToken(":", TimeToken.COLON); case '.': return token = new TimeToken(".", TimeToken.DOT); case '+': return token = new TimeToken("+", TimeToken.PLUS); case '-': return token = new TimeToken("-", TimeToken.MINUS); case '/': return token = new TimeToken("/", TimeToken.SLASH); default: pos--; return token = new TimeToken(null, TimeToken.EOF); } } return token = new TimeToken(null, TimeToken.EOF); } TimeToken resolveMonthsMinutes(int newId) { assert token.id == TimeToken.MONTHS_MINUTES; assert newId == TimeToken.MONTHS || newId == TimeToken.MINUTES; return token = new TimeToken(token.value, newId); } void saveState() { token_save = token; pos_save = pos; } TimeToken restoreState() { pos = pos_save; return token = token_save; } private int parseToken(String arg) { for (int i = 0; specials[i].value != null; i++) { if (specials[i].value.equalsIgnoreCase(arg)) { return specials[i].id; } } return TimeToken.ID; } } |