KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > nightlabs > util > timepattern > TimePattern


1 /* ************************************************************************** *
2  * Copyright (C) 2004 NightLabs GmbH, Marco Schulze, Alexander Bieber *
3  * All rights reserved. *
4  * http://www.NightLabs.de *
5  * *
6  * This program and the accompanying materials are free software; you can re- *
7  * distribute it and/or modify it under the terms of the GNU General Public *
8  * License as published by the Free Software Foundation; either ver 2 of the *
9  * License, or any later version. *
10  * *
11  * This module is distributed in the hope that it will be useful, but WITHOUT *
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FIT- *
13  * NESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more *
14  * details. *
15  * *
16  * You should have received a copy of the GNU General Public License along *
17  * with this module; if not, write to the Free Software Foundation, Inc.: *
18  * 59 Temple Place, Suite 330 *
19  * Boston MA 02111-1307 *
20  * USA *
21  * *
22  * Or get it online: *
23  * http://www.opensource.org/licenses/gpl-license.php *
24  * *
25  * In case, you want to use this module or parts of it in a proprietary pro- *
26  * ject, you can purchase it under the NightLabs Commercial License. Please *
27  * contact NightLabs GmbH under info AT nightlabs DOT com for more infos or *
28  * visit http://www.NightLabs.com *
29  * ************************************************************************** */

30
31 /*
32  * Created on 02.06.2004
33  */

34 package com.nightlabs.util.timepattern;
35
36 import java.io.Serializable JavaDoc;
37 import java.util.ArrayList JavaDoc;
38 import java.util.Calendar JavaDoc;
39 import java.util.GregorianCalendar JavaDoc;
40 import java.util.Iterator JavaDoc;
41 import java.util.List JavaDoc;
42 import java.util.StringTokenizer JavaDoc;
43
44 /**
45  * This class holds periods of time for the following fields: month, day, dayOfWeek, hour, minute.
46  * <br/><br/>
47  *
48  * This class can be used to check whether a certain moment of time matches a pattern defined
49  * by the time periods within these fields. This is useful for scheduled execution of a timer
50  * job or for the definition of the validity of an admittance.
51  * <br/><br/>
52  *
53  * The periods are linux cron formatted.
54  * That means, you can define lists of values by separating the values with
55  * commas (",") or define ranges by defining the from and the to value separated
56  * by a minus ("-"). You can combine them. Use the "*" as wildcard for all possible
57  * values (month: * = 1-12; hour: * = 1-24).
58  * <br/><br/>
59  * Please note that there is one extension that is not linux cron conform: If
60  * you want a job being executed e.g. every 5 minutes, you can use "/5". A slash
61  * ("/") followed by a number means all values that can be divided by the number
62  * without a rest (value modulo dividor is 0).
63  * <br/><br/>
64  * An empty field (means the field contains <code>null</code>) equals a field containing "*".
65  * Thus, a new instance of this class (generated with the no-parameter-constructor) will
66  * match all timestamps.
67  *
68  * <br/><br/>
69  * Examples:<br/>
70  * &nbsp;&nbsp;month="*"
71  * &nbsp;&nbsp;day="1-7"
72  * &nbsp;&nbsp;dayOfWeek="sun"
73  * &nbsp;&nbsp;hour="0,6,12,18"
74  * &nbsp;&nbsp;minute="0"
75  * <br/>
76  * This pattern could define an execution of a job on the first sunday of every
77  * month 4 times a day at midnight, 6:00, 12:00 and 18:00.
78  * <br/><br/>
79  *
80  * &nbsp;&nbsp;month="4-10"
81  * &nbsp;&nbsp;day="*"
82  * &nbsp;&nbsp;dayOfWeek="mon-fri"
83  * &nbsp;&nbsp;hour="8-14,16-22"
84  * &nbsp;&nbsp;minute="*"
85  * <br/>
86  * This pattern could be used to control the admittance to a location that is open from
87  * April to October on weekdays (monday to friday). On these days, it opens at 8:00, closes
88  * at 14:59:59 and reopens at 16:00 again to finally close at 22:59:59.
89  *
90  * @author Marco Shulze
91  * @author Alexander Bieber
92  *
93  */

94 public abstract class TimePattern
95     implements Serializable JavaDoc
96 {
97
98     public static final byte SUNDAY_NUMBER = 1;
99     public static final byte MONDAY_NUMBER = 2;
100     public static final byte TUESDAY_NUMBER = 3;
101     public static final byte WEDNESDAY_NUMBER = 4;
102     public static final byte THURSDAY_NUMBER = 5;
103     public static final byte FRIDAY_NUMBER = 6;
104     public static final byte SATURDAY_NUMBER = 7;
105     
106     public static final String JavaDoc SUNDAY_NAME = "sun";
107     public static final String JavaDoc MONDAY_NAME = "mon";
108     public static final String JavaDoc TUESDAY_NAME = "tue";
109     public static final String JavaDoc WEDNESDAY_NAME = "wed";
110     public static final String JavaDoc THURSDAY_NAME = "thu";
111     public static final String JavaDoc FRIDAY_NAME = "fri";
112     public static final String JavaDoc SATURDAY_NAME = "sat";
113     
114     protected static String JavaDoc[] DAYOFWEEK_NAMES = new String JavaDoc[] {
115             "",
116             SUNDAY_NAME, MONDAY_NAME, TUESDAY_NAME,
117             WEDNESDAY_NAME, THURSDAY_NAME, FRIDAY_NAME, SATURDAY_NAME
118     };
119     
120     public static byte dayOfWeekNameToNumber(String JavaDoc dayOfWeek)
121     {
122         if (dayOfWeek == null)
123             throw new NullPointerException JavaDoc("dayOfWeek must not be null!");
124         
125         for (byte wd = 1; wd < DAYOFWEEK_NAMES.length; ++wd) {
126             if (DAYOFWEEK_NAMES[wd].equals(dayOfWeek))
127                 return wd;
128         }
129         throw new IllegalArgumentException JavaDoc("dayOfWeek \""+dayOfWeek+"\" is unknown!");
130     }
131     
132     public static String JavaDoc dayOfWeekNumberToName(int dayOfWeekNumber)
133     {
134         if (dayOfWeekNumber < 1 || dayOfWeekNumber > 7)
135             throw new IllegalArgumentException JavaDoc("dayOfWeekNumber must be >=1 and <=7!!!");
136         
137         return DAYOFWEEK_NAMES[dayOfWeekNumber];
138     }
139
140     public static byte CONVERT_DAYOFWEEK_NUMBER_TO_NAME = 1;
141     public static byte CONVERT_DAYOFWEEK_NAME_TO_NUMBER = 2;
142     public static String JavaDoc convertDayOfWeekPeriodString(String JavaDoc periodString, byte conversionDirection)
143         throws TimePatternFormatException
144     {
145         if (conversionDirection != CONVERT_DAYOFWEEK_NAME_TO_NUMBER &&
146                 conversionDirection != CONVERT_DAYOFWEEK_NUMBER_TO_NAME)
147             throw new IllegalArgumentException JavaDoc("conversionDirection is invalid! Use one of these constants: conversionDirection != CONVERT_DAYOFWEEK_NAME_TO_NUMBER, CONVERT_DAYOFWEEK_NUMBER_TO_NAME");
148
149         if (periodString == null)
150             return null;
151
152         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
153         StringTokenizer JavaDoc tok = new StringTokenizer JavaDoc(periodString, " ,-*", true);
154         while (tok.hasMoreTokens()) {
155             String JavaDoc token = tok.nextToken();
156             if (token.equals(" ")) ;
157             else if (token.equals(",")) sb.append(token);
158             else if (token.equals("-")) sb.append(token);
159             else if (token.equals("*")) sb.append(token);
160             else {
161                 if (token.matches("(\\d)*")) {
162                     if (conversionDirection == CONVERT_DAYOFWEEK_NUMBER_TO_NAME) {
163                         int weekDayNumber = Integer.parseInt(token);
164                         sb.append(dayOfWeekNumberToName(weekDayNumber));
165                     }
166                     else
167                         sb.append(token);
168                 } // if token is a number
169
else { // token is a weekday name or is invalid
170
token = token.toLowerCase();
171                     byte dayOfWeekNumber = dayOfWeekNameToNumber(token);
172                     if (conversionDirection == CONVERT_DAYOFWEEK_NAME_TO_NUMBER)
173                         sb.append(dayOfWeekNumber);
174                     else
175                         sb.append(token);
176                 }
177             }
178                 
179         }
180         return sb.toString();
181     }
182
183     public static final byte YEAR = 0;
184     public static final byte MONTH = 1;
185     public static final byte DAY = 2;
186     public static final byte DAY_OF_WEEK = 3;
187     public static final byte HOUR = 4;
188     public static final byte MINUTE = 5;
189
190     private TimePattern() { }
191     
192     public TimePattern(TimePatternSet _timePatternSet)
193     {
194         this.setTimePatternSet(_timePatternSet);
195     }
196
197     public TimePattern(TimePatternSet _timePatternSet, String JavaDoc _year, String JavaDoc _month, String JavaDoc _day, String JavaDoc _dayOfWeek, String JavaDoc _hour, String JavaDoc _minute)
198         throws TimePatternFormatException
199     {
200         this.setTimePatternSet(_timePatternSet);
201         this.setYear(_year);
202         this.setMonth(_month);
203         this.setDay(_day);
204         this.setDayOfWeek(_dayOfWeek);
205         this.setHour(_hour);
206         this.setMinute(_minute);
207     }
208
209     public static class TimePeriod
210     {
211         public TimePeriod(int from, int to)
212         {
213             this.from = from;
214             this.to = to;
215         }
216
217         private int from;
218         private int to;
219
220         public int getFrom() { return this.from; }
221         public int getTo() { return this.to; }
222         
223         public void setFrom(int _from)
224         {
225             this.from = _from;
226             thisString = null;
227         }
228         public void setTo(int _to)
229         {
230             this.to = _to;
231             thisString = null;
232         }
233
234         protected String JavaDoc thisString = null;
235         /**
236          * @see java.lang.Object#toString()
237          */

238         public String JavaDoc toString() {
239             if (thisString == null) {
240                 StringBuffer JavaDoc sb = new StringBuffer JavaDoc(this.getClass().getName());
241                 sb.append('{');
242                 sb.append(from);
243                 sb.append(',');
244                 sb.append(to);
245                 sb.append('}');
246                 thisString = sb.toString();
247             }
248
249             return thisString;
250         }
251     }
252
253
254     protected List JavaDoc getPeriods(String JavaDoc value, int minVal, int maxVal)
255         throws TimePatternFormatException
256     {
257         if (value == null)
258             value = "";
259         else
260             value = value.replaceAll(" ", "");
261         
262         ArrayList JavaDoc periods = new ArrayList JavaDoc();
263
264         StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(value, ",", false);
265
266         if (!st.hasMoreTokens())
267             periods.add(
268                 new TimePeriod(minVal, maxVal)
269             );
270
271         while (st.hasMoreTokens()) {
272             String JavaDoc currPeriod = st.nextToken();
273             if (currPeriod.equals("*"))
274                 periods.add(
275                     new TimePeriod(minVal, maxVal)
276                 );
277             else if (currPeriod.startsWith("/")) {
278                 int div = Integer.parseInt(currPeriod.substring(1));
279                 for (int v = minVal; v <= maxVal; v++) {
280                     if (v % div == 0)
281                         periods.add(
282                             new TimePeriod(v, v)
283                         );
284                 }
285             } // else if (currPeriod.startsWith("/")) {
286
else {
287                 StringTokenizer JavaDoc pt = new StringTokenizer JavaDoc(currPeriod, "-", false);
288                 if (!pt.hasMoreTokens())
289                     throw new TimePatternFormatException("Empty value in list!");
290
291                 int from = Integer.parseInt(pt.nextToken());
292                 int to;
293
294                 if (!pt.hasMoreTokens())
295                     to = from;
296                 else
297                     to = Integer.parseInt(pt.nextToken());
298
299                 if (pt.hasMoreTokens())
300                     throw new TimePatternFormatException("Multiple minus signs in period definition! Only one allowed: from-to!");
301
302                 periods.add(
303                     new TimePeriod(from, to)
304                 );
305
306             }
307         } // while (st.hasMoreTokens()) {
308

309         return periods;
310     }
311     
312     /**
313      * cachedPeriods is used to speed up the matches(...) method by shortcutting
314      * getPeriods(byte). This array is set to <code>null</code> whenever the pattern
315      * is changed. The method getPeriods(byte) initializes it and parses the strings
316      * only if there is no cached value existing.
317      *
318      * @jdo.field persistence-modifier="none"
319      */

320     protected transient List JavaDoc[] cachedPeriods = null;
321
322     /**
323      * This method parses the requested field and returns its values as periods.
324      *
325      * @param field The ID of the field you want to get. Use one of the following
326      * constants: MONTH, DAY, DAY_OF_WEEK, HOUR, MINUTE
327      *
328      * @return A List of TimePeriod objects.
329      *
330      * @throws TimePatternFormatException If the definition strings are not clean.
331      */

332     protected List JavaDoc getPeriods(byte field)
333         throws TimePatternFormatException
334     {
335         String JavaDoc value;
336         int minVal;
337         int maxVal;
338         switch (field) {
339             case YEAR:
340                 value = getYear();
341                 minVal = 0;
342                 maxVal = Integer.MAX_VALUE;
343             break;
344             case MONTH:
345                 value = getMonth();
346                 minVal = 1;
347                 maxVal = 12;
348             break;
349             case DAY:
350                 value = getDay();
351                 minVal = 1;
352                 maxVal = 31;
353             break;
354             case DAY_OF_WEEK:
355                 value = convertDayOfWeekPeriodString(getDayOfWeek(), CONVERT_DAYOFWEEK_NAME_TO_NUMBER);
356                 minVal = 1;
357                 maxVal = 7;
358             break;
359             case HOUR:
360                 value = getHour();
361                 minVal = 0;
362                 maxVal = 23;
363             break;
364             case MINUTE:
365                 value = getMinute();
366                 minVal = 0;
367                 maxVal = 59;
368             break;
369             default:
370                 throw new IllegalArgumentException JavaDoc("Parameter field contains an invalid value!");
371         } // switch (field) {
372

373         if (cachedPeriods == null)
374             cachedPeriods = new List JavaDoc[6];
375
376         if (cachedPeriods[field] == null)
377             cachedPeriods[field] = getPeriods(value, minVal, maxVal);
378
379         return cachedPeriods[field];
380     }
381
382     /**
383      * Use this method to check whether a given time stamp (in milliseconds) is
384      * within this pattern.
385      * The value of maxInclude defines down to wich period the check should go use here a value
386      * between {@link #YEAR} and {{@link #MINUTE}} inclusive. For example to check weather the
387      * time pattern matches for a certain day use
388      * matches(yourTimeStamp, TimePattern.DAY);
389      *
390      * @param timeStamp The timeStamp you want to check.
391      * @param maxInclude The maximum depth that should be taken into account.
392      *
393      * @return true, if the given time is within the pattern held by this object.
394      *
395      * @throws TimePatternFormatException If the definition contains invalid period strings.
396      */

397     public boolean matches(long timeStamp, byte maxInclude)
398     {
399         try {
400             Calendar JavaDoc cal = new GregorianCalendar JavaDoc();
401             cal.setTimeInMillis(timeStamp);
402
403             int[] values = new int[MINUTE + 1];
404
405             values[YEAR] = cal.get(Calendar.YEAR);
406             values[MONTH] = cal.get(Calendar.MONTH) + 1;
407             values[DAY] = cal.get(Calendar.DAY_OF_MONTH);
408             values[DAY_OF_WEEK] = cal.get(Calendar.DAY_OF_WEEK);
409             values[HOUR] = cal.get(Calendar.HOUR_OF_DAY);
410             values[MINUTE] = cal.get(Calendar.MINUTE);
411     
412             for (byte field = 0; field <= maxInclude; field++) {
413                 List JavaDoc periods = getPeriods(field);
414     
415                 boolean match = false;
416                 for (Iterator JavaDoc it = periods.iterator(); it.hasNext(); ) {
417                     TimePeriod period = (TimePeriod) it.next();
418     
419                     if (period.getFrom() <= values[field] &&
420                             values[field] <= period.getTo()) {
421                         match = true;
422                         break;
423                     }
424                 } // for (int i = 0; i < periods.length; i++) {
425

426                 if (!match)
427                     return false;
428             } // for (byte field = 0; field <= MINUTE; field++) {
429

430             return true;
431         } catch (TimePatternFormatException x) {
432             // This should never happen, because the pattern strings are checked when set.
433
throw new RuntimeException JavaDoc(x);
434         }
435     }
436     
437     /**
438      * Calls {@link #matches(long, byte)} with the given timeStamp
439      * and {@link #MINUTE}.
440      *
441      * @param timeStamp
442      * @return matches(timeStamp, MINUTE)
443      */

444     public boolean matches(long timeStamp) {
445         return matches(timeStamp, MINUTE);
446     }
447
448     /**
449      * @return Returns the timePatternSet.
450      */

451     public abstract TimePatternSet getTimePatternSet();
452     
453     protected abstract void setTimePatternSet(TimePatternSet set);
454
455     /**
456      * @return Returns the year.
457      */

458     public abstract String JavaDoc getYear();
459     
460     protected abstract void _setYear(String JavaDoc _year);
461     
462     
463     /**
464      * @param month The month to set.
465      */

466     public void setYear(String JavaDoc _year)
467         throws TimePatternFormatException
468     {
469         getPeriods(_year, -1, -1);
470         _setYear(_year);
471         changed();
472     }
473
474     /**
475      * @return Returns the month.
476      */

477     public abstract String JavaDoc getMonth();
478     
479     protected abstract void _setMonth(String JavaDoc _month);
480     
481     /**
482      * @param month The month to set.
483      */

484     public void setMonth(String JavaDoc _month)
485         throws TimePatternFormatException
486     {
487         getPeriods(_month, -1, -1);
488         _setMonth(_month);
489         changed();
490     }
491     
492     /**
493      * @return Returns the day.
494      */

495     public abstract String JavaDoc getDay();
496     
497     protected abstract void _setDay(String JavaDoc _day);
498     
499     /**
500      * @param day The day to set.
501      */

502     public void setDay(String JavaDoc _day)
503         throws TimePatternFormatException
504     {
505         getPeriods(_day, -1, -1);
506         _setDay(_day);
507         changed();
508     }
509     /**
510      * @return Returns the dayOfWeek.
511      */

512     public abstract String JavaDoc getDayOfWeek();
513     protected abstract void _setDayOfWeek(String JavaDoc _dayOfWeek);
514     /**
515      * @param dayOfWeek The dayOfWeek to set.
516      */

517     public void setDayOfWeek(String JavaDoc _dayOfWeek)
518         throws TimePatternFormatException
519     {
520         String JavaDoc dayOfWeek_numbered = convertDayOfWeekPeriodString(_dayOfWeek, CONVERT_DAYOFWEEK_NAME_TO_NUMBER);
521         _dayOfWeek = convertDayOfWeekPeriodString(_dayOfWeek, CONVERT_DAYOFWEEK_NUMBER_TO_NAME);
522         
523         getPeriods(dayOfWeek_numbered, -1, -1);
524         _setDayOfWeek(_dayOfWeek);
525         changed();
526     }
527     
528     /**
529      * @return Returns the hour.
530      */

531     public abstract String JavaDoc getHour();
532
533     protected abstract void _setHour(String JavaDoc _hour);
534     
535     
536     /**
537      * @param hour The hour to set.
538      */

539     public void setHour(String JavaDoc _hour)
540         throws TimePatternFormatException
541     {
542         getPeriods(_hour, -1, -1);
543         _setHour(_hour);
544         changed();
545     }
546     
547     
548     /**
549      * @return Returns the minute.
550      */

551     public abstract String JavaDoc getMinute();
552     
553     protected abstract void _setMinute(String JavaDoc _minute);
554     
555     
556     /**
557      * @param minute The minute to set.
558      */

559     public void setMinute(String JavaDoc _minute)
560         throws TimePatternFormatException
561     {
562         getPeriods(_minute, -1, -1);
563         _setMinute(_minute);
564         changed();
565     }
566     
567     protected void changed()
568     {
569         cachedPeriods = null;
570         thisString = null;
571     }
572
573     /**
574      * @jdo.field persistence-modifier="none"
575      */

576     protected transient String JavaDoc thisString = null;
577     /**
578      * @see java.lang.Object#toString()
579      */

580     public String JavaDoc toString() {
581         if (thisString == null) {
582             StringBuffer JavaDoc sb = new StringBuffer JavaDoc(this.getClass().getName());
583             sb.append("{[");
584             sb.append(getYear());
585             sb.append("],[");
586             sb.append(getMonth());
587             sb.append("],[");
588             sb.append(getDay());
589             sb.append("],[");
590             sb.append(getDayOfWeek());
591             sb.append("],[");
592             sb.append(getHour());
593             sb.append("],[");
594             sb.append(getMinute());
595             sb.append("]}");
596             thisString = sb.toString();
597         }
598         return thisString;
599     }
600 }
Popular Tags