KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > mmbase > datatypes > DateTimePattern


1 /*
2
3 This software is OSI Certified Open Source Software.
4 OSI Certified is a certification mark of the Open Source Initiative.
5
6 The license (Mozilla version 1.0) can be read at the MMBase site.
7 See http://www.MMBase.org/license
8
9 */

10
11 package org.mmbase.datatypes;
12
13 import java.text.*;
14 import java.util.*;
15 import org.mmbase.util.LocalizedString;
16 import org.mmbase.util.DateFormats;
17 import org.mmbase.util.logging.Logging;
18 import org.mmbase.util.logging.Logger;
19
20 /**
21  * This is a bit like SimpleDateFormat, because it accepts the same pattern String. It can also
22  * parse the String though (see {@link #getList}), which can be used to do something else
23  * for parsing or formatting (think: format an editor entry).
24  *
25  * This utility class is of course used in the implementation of {@link DateTimeDataType}.
26  *
27  * @author Michiel Meeuwissen
28  * @since MMBase-1.8
29  * @version $Id: DateTimePattern.java,v 1.13 2006/08/30 17:47:21 michiel Exp $
30  */

31
32 public class DateTimePattern implements Cloneable JavaDoc, java.io.Serializable JavaDoc {
33     private static final Logger log = Logging.getLoggerInstance(DateTimePattern.class);
34
35     public static final DateTimePattern DEFAULT = new DateTimePattern("yyyy-MM-dd HH:mm:ss");
36
37     private static final long serialVersionUID = 1L; // increase this if object serialization changes (which we shouldn't do!)
38

39     protected LocalizedString pattern;
40
41     public DateTimePattern(String JavaDoc pattern) {
42         this.pattern = new LocalizedString(pattern);
43     }
44
45     public void set(String JavaDoc pattern, Locale locale) {
46         this.pattern.set(pattern, locale);
47     }
48     public void set(String JavaDoc pattern) {
49         this.pattern.setKey(pattern);
50     }
51
52     /**
53      * Returns a DateFormat object associated with this object.
54      */

55     public DateFormat getDateFormat(Locale locale) {
56         if (locale == null) locale = LocalizedString.getDefault();
57         return DateFormats.getInstance(pattern.get(locale), null, locale);
58     }
59
60
61     /**
62      * Returns the original pattern, which can e.g. be used to instantiate a SimpleDateFormat (but this is also done for you in {@link #getDateFormat}.
63      */

64     public LocalizedString getPattern() {
65         return pattern;
66     }
67
68     private static final char DONTAPPEND = (char) -1;
69     private static List parse(String JavaDoc p) {
70         List parsed = new ArrayList();
71         StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
72         boolean inString = false;
73         boolean inQuote = false;
74
75         char nonStringChar = (char) -1;
76         for (int i = 0; i < p.length(); i++) {
77             char c = p.charAt(i);
78             if ((c >= 'a' && c <= 'z') ||
79                 (c >= 'A' && c <= 'Z')) { // reserved
80
if (inString) {
81                     if (! inQuote) {
82                         if (buf.length() > 0) {
83                             parsed.add(buf.toString());
84                             buf.setLength(0);
85                         }
86                         inString = false;
87                         nonStringChar = c;
88                     }
89                 } else {
90                     if (nonStringChar != c) {
91                         parsed.add(buf.toString());
92                         buf.setLength(0);
93                         nonStringChar = c;
94                     }
95                 }
96             } else {
97                 if (! inString) {
98                     if (i != 0) {
99                         parsed.add(buf.toString());
100                         buf.setLength(0);
101                     }
102                     buf.append("\'");
103                     inQuote = false;
104                     inString = true;
105                 }
106                 if (inString) {
107                     if (c == '\'') {
108                         if (inQuote && i > 0 && p.charAt(i -1) == '\'') {
109                             // enabling escape of '.
110
} else {
111                             c = DONTAPPEND;
112                         }
113                         inQuote = ! inQuote;
114
115                     }
116                 }
117
118             }
119             if (c != DONTAPPEND) {
120                 buf.append(c);
121             }
122         }
123         if (inQuote) {
124             throw new IllegalArgumentException JavaDoc("Unterminated quote");
125         }
126         if (buf.length() > 0) {
127             parsed.add(buf.toString());
128         }
129         return parsed;
130     }
131
132
133     /**
134      * Returns the pattern 'parsed'. This means that is is a List of Strings. If the string is
135      * introduced by a quote, then it is a literal string, otherwise it is a format-string,
136      * consisting only of a number of the same letters (e.g. yyy). So by checking the first
137      * character you can decide what to do with it. If for example you are making an editor, and
138      * the first char is an quote, you may decide to do either nothing, or to write it out (without
139      * the quote). If the first character is e.g. 'y' you can make an input box for the year (you
140      * could also attribute some meaning to the length of the string then).
141      *
142      *
143      */

144     public List getList(Locale locale) {
145         String JavaDoc p = pattern.get(locale);
146         return parse(p);
147     }
148
149     private static final Element HOUR_OF_DAY = new Element("hour", Calendar.HOUR_OF_DAY, 0, 23);
150     private static final Element HOUR = new Element("hourinampm", Calendar.HOUR, 0, 11);
151     private static final Element HOUR_OF_DAY_1 = new Element("hour", Calendar.HOUR_OF_DAY, 1, 24, 1);
152     private static final Element HOUR_1 = new Element("hour", Calendar.HOUR, 1, 12, 1);
153     private static final Element MINUTE = new Element("minute", Calendar.MINUTE, 0, 59);
154     private static final Element SECOND = new Element("second", Calendar.SECOND, 0, 59);
155     private static final Element DAY_OF_WEEK = new Element("dayofweek", Calendar.DAY_OF_WEEK, 1, 7) {
156             public String JavaDoc toString(int value, Locale locale, int length) {
157                 SimpleDateFormat format = new SimpleDateFormat("EEEEEEEEEEEEE".substring(0, length), locale);
158                 Calendar help = Calendar.getInstance();
159                 help.clear();
160                 help.set(field, value);
161                 return format.format(help.getTime());
162             }
163         };
164     private static final Element WEEK_OF_YEAR = new Element("weekinyear", Calendar.WEEK_OF_YEAR, 1, 53);
165     private static final Element DAY_OF_YEAR = new Element("dayinyear", Calendar.DAY_OF_YEAR, 1, 366);
166     private static final Element DAY_OF_WEEK_IN_MONTH = new Element("dayofweekinmonth", Calendar.DAY_OF_WEEK_IN_MONTH, 1, 5) {
167             public String JavaDoc toString(int value, Locale locale, int length) {
168                 if (length > 0) {
169                     ResourceBundle bundle = ResourceBundle.getBundle("org.mmbase.datatypes.resources.ordinals", locale);
170                     return bundle.getString("" + value);
171                 } else {
172                     return super.toString(value, locale, length);
173                 }
174             }
175         };
176
177     private static final Element AM_PM = new Element("am_pm", Calendar.AM_PM, 0, //AM
178
1 //PM
179
) {
180             public String JavaDoc toString(int value, Locale locale, int length) {
181                 SimpleDateFormat format = new SimpleDateFormat("aaaaaaaaaaaaaa".substring(0, length), locale);
182                 Calendar help = Calendar.getInstance();
183                 help.clear();
184                 help.set(field, value);
185                 return format.format(help.getTime());
186             }
187         };
188     private static final Element MILLISECOND = new Element("millisecond", Calendar.MILLISECOND, 0, 999);
189
190
191     /**
192      * Returns an {@link Element} structure assiocated with the characters of the format
193      * pattern. This utility function can be usefull when generating drop-downs based on the result
194      * of {@link #getList}.
195      * @param c The pattern character. 'y', 'M', 'd', 'H', 'K', 'h', 'k', 'm', 's', 'E', 'w', 'D', 'F', 'G', 'a', or 'S'. Also u is recognized (as in the ICU version of SimpleDateFormat), for years which can also be negative (targeted at GregorianCalendar with 2 era's)
196      * @param minDate If for example the parameter is 'y' then the 'getMin' property of the result
197      * Element will be the year of this date.
198      * @param maxDate If for example the parameter is 'y' then the 'getMax' property of the result
199      * Element will be the year of this date.
200      */

201     public static Element getElement(final char c, Calendar minDate, Calendar maxDate) {
202         switch(c) {
203         case 'G': {
204             int startEra = minDate.get(Calendar.ERA);
205             int endEra = maxDate.get(Calendar.ERA);
206             return new Element("era", Calendar.ERA, startEra, //BC
207
endEra //AD
208
) {
209                 public String JavaDoc toString(int value, Locale locale, int length) {
210                     SimpleDateFormat format = new SimpleDateFormat("GGGGGGGGGGGG".substring(0, length), locale);
211                     Calendar help = Calendar.getInstance();
212                     help.clear();
213                     help.set(field, value);
214                     return format.format(help.getTime());
215                 }
216             };
217         }
218         case 'y': {
219             int startEra = minDate.get(Calendar.ERA);
220             int endEra = maxDate.get(Calendar.ERA);
221             int startYear = minDate.get(Calendar.YEAR);
222             int endYear = maxDate.get(Calendar.YEAR);
223             if (startEra < endEra) { // you'll need an ERA indicator too, if this happens, and you want to be able to enter dates BC.
224
endYear = endYear > startYear ? endYear : startYear;
225                 startYear = minDate.getActualMinimum(Calendar.YEAR);
226             }
227             return new Element("year", Calendar.YEAR, startYear, endYear) {
228                 public int getNullValue() {
229                     return Integer.MAX_VALUE;
230                 }
231             };
232         }
233         case 'u': { // this is not a SimpleDateFormat character (it is a com.ibm.icu.text.SimpleDateFormat compatible though)
234
int startEra = minDate.get(Calendar.ERA);
235             int endEra = maxDate.get(Calendar.ERA);
236             int startYear = minDate.get(Calendar.YEAR);
237             if (minDate instanceof GregorianCalendar && startEra == GregorianCalendar.BC) startYear = -1 * (startYear - 1);
238             int endYear = maxDate.get(Calendar.YEAR);
239             if (maxDate instanceof GregorianCalendar && endEra == GregorianCalendar.BC) endYear = -1 * (endYear - 1);
240             return new Element("year", Calendar.YEAR, startYear, endYear) {
241                 public int getValue(Calendar cal) {
242                     int era = cal.get(Calendar.ERA);
243                     int year = cal.get(Calendar.YEAR);
244                     if (cal instanceof GregorianCalendar && era == GregorianCalendar.BC) year = -1 * (year - 1);
245                     return year;
246                 }
247                 public int getNullValue() {
248                     return Integer.MAX_VALUE;
249                 }
250             };
251         }
252         case 'M': {
253             int startYear = minDate.get(Calendar.YEAR);
254             int endYear = maxDate.get(Calendar.YEAR);
255             int min, max;
256             if (startYear == endYear) {
257                 min = minDate.get(Calendar.MONTH) + 1;
258                 max = maxDate.get(Calendar.MONTH) + 1;
259             } else {
260                 min = 1;
261                 max = 12;
262             }
263             return new Element("month", Calendar.MONTH, min, max, 1) {
264                     public String JavaDoc toString(int value, Locale locale, int length) {
265                         SimpleDateFormat format = new SimpleDateFormat("MMMMMMMMMMMMMMMMMM".substring(0, length), locale);
266                         Calendar help = Calendar.getInstance();
267                         help.clear();
268                         help.set(field, value);
269                         return format.format(help.getTime());
270                     }
271                 };
272         }
273         case 'd': {
274             int startYear = minDate.get(Calendar.YEAR);
275             int endYear = maxDate.get(Calendar.YEAR);
276             int min, max;
277             if (startYear == endYear) {
278                 int minMonth = minDate.get(Calendar.MONTH) + 1;
279                 int maxMonth = maxDate.get(Calendar.MONTH) + 1;
280                 if (minMonth == maxMonth) {
281                     min = minDate.get(Calendar.DAY_OF_MONTH);
282                     max = maxDate.get(Calendar.DAY_OF_MONTH);
283                 } else {
284                     min = 1;
285                     max = 31;
286                 }
287             } else {
288                 min = 1;
289                 max = 31;
290             }
291             return new Element("day", Calendar.DAY_OF_MONTH, min, max);
292         }
293             // ignore minDate, maxDate for these, never mind..
294
case 'H': return HOUR_OF_DAY;
295         case 'K': return HOUR;
296         case 'h': return HOUR_OF_DAY_1;
297         case 'k': return HOUR_1;
298         case 'm': return MINUTE;
299         case 's': return SECOND;
300         case 'E': return DAY_OF_WEEK;
301         case 'w': return WEEK_OF_YEAR;
302         case 'D': return DAY_OF_YEAR;
303         case 'F': return DAY_OF_WEEK_IN_MONTH;
304         case 'a': return AM_PM;
305         case 'S': return MILLISECOND;
306         default:
307             log.warn("Unknown pattern " + c);
308             return null;
309         }
310     }
311
312     /**
313      * A wrapper arround a field in a {@link java.util.Calendar} object. It provides a
314      * minimal and maximal value for the integer value, which can be requested by code which is
315      * producing a user interface to enter dates.
316      */

317     public static class Element {
318         private final String JavaDoc name;
319         final int field;
320         private final int min;
321         private final int max;
322         private final int offset;
323         Element(String JavaDoc n, int field, int min, int max) {
324             this(n, field, min, max, 0);
325         }
326         Element(String JavaDoc n, int field, int min, int max, int offset) {
327             name = n;
328             this.field = field;
329             this.min = min;
330             this.max = max;
331             this.offset = offset;
332         }
333         /**
334          * The name of the field in a Calendar object. Like e.g. 'day' or 'second'.
335          */

336         public final String JavaDoc getName() {
337             return name;
338         }
339         /**
340          * The associated constant in {@link java.util.Calendar}, e.g. {@link
341          * java.util.Calendar.DAY_OF_MONTH} or {@link java.util.Calendar#SECOND}
342          */

343         public final int getField() {
344             return field;
345         }
346         /**
347          * The minimal value this field of the Calendar object can take.
348          */

349         public final int getMin() {
350             return min;
351         }
352         /**
353          * The maximal value this field of the Calendar object can take.
354          */

355         public final int getMax() {
356             return max;
357         }
358         /**
359          * An offset to be used for presentation. E.g. months are represented by number from 0 to 11
360          * in Calendar objects but you typically want to present 1 to 12, so the offset is 1 then.
361          */

362         public final int getOffset() {
363             return offset;
364         }
365
366         /**
367          * Normally equivalent with <code>cal.getValue(getField())</code>
368          * @return The value for this element for a certain Calendar instance
369          */

370         public int getValue(Calendar cal) {
371             return cal.get(getField());
372         }
373         /**
374          * Converts a value for the Calendar field associated with this Element to a
375          * String. Typically used when creating optionlists.
376          * @param value the value to convert
377          * @param locale A locale can be used in some instances. E.g. to generate month names.
378          * @param length An indication of verboseness. Typically numeric results if a small number
379          * (perhaps filled to this length) or words if a big number (and it makes sense, e.g. for
380          * months, and weekdays).
381          */

382         public String JavaDoc toString(int value, Locale locale, int length) {
383             StringBuffer JavaDoc buf = new StringBuffer JavaDoc("" + value);
384             while(buf.length() < length) {
385                 buf.insert(0, "0");
386             }
387             return buf.toString();
388         }
389
390         public String JavaDoc toString() {
391             return getName() + " [" + min + ", " + max + "] + " + getOffset();
392         }
393         /**
394          * The int-value representing <code>null</code>. Some otherwise impossible value for the
395          * field. This can be use as a marker value in the option-list to set the calendar value to <code>null</code>.
396          */

397         public int getNullValue() {
398             return -1;
399         }
400     }
401
402
403     public Object JavaDoc clone() {
404         try {
405             DateTimePattern clone = (DateTimePattern) super.clone();
406             clone.pattern = (LocalizedString) pattern.clone();
407             return clone;
408         } catch (CloneNotSupportedException JavaDoc cns) {
409             // should not happen
410
throw new RuntimeException JavaDoc(cns);
411         }
412     }
413
414     public String JavaDoc toString() {
415         return pattern.toString();
416     }
417
418     public static void main(String JavaDoc argv[]) {
419         String JavaDoc input;
420         if (argv.length > 0) {
421             input = argv[0];
422         } else {
423             input = "yyyy-MM-dd";
424         }
425         DateTimePattern df = new DateTimePattern(input);
426         df.set("yyyy-MM-dd", Locale.FRANCE);
427
428         DateTimePattern df2 = (DateTimePattern) df.clone();
429         df2.set("HH:mm:ss");
430         df2.set("yyyy;MM;dd", Locale.FRANCE);
431
432         System.out.println("" + df.getList(Locale.FRANCE));
433         System.out.println("" + df2.getList(Locale.FRANCE));
434
435     }
436
437 }
438
Popular Tags