KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sf > saxon > functions > FormatDate


1 package net.sf.saxon.functions;
2 import net.sf.saxon.expr.ExpressionTool;
3 import net.sf.saxon.expr.StaticContext;
4 import net.sf.saxon.expr.XPathContext;
5 import net.sf.saxon.instruct.NumberInstruction;
6 import net.sf.saxon.number.Numberer;
7 import net.sf.saxon.om.Item;
8 import net.sf.saxon.om.FastStringBuffer;
9 import net.sf.saxon.trans.DynamicError;
10 import net.sf.saxon.trans.StaticError;
11 import net.sf.saxon.trans.XPathException;
12 import net.sf.saxon.type.Type;
13 import net.sf.saxon.value.*;
14
15 import javax.xml.transform.TransformerException JavaDoc;
16 import java.util.Calendar JavaDoc;
17 import java.util.Locale JavaDoc;
18 import java.util.regex.Matcher JavaDoc;
19 import java.util.regex.Pattern JavaDoc;
20
21 /**
22  * Implement the format-date() function in XPath 2.0
23  */

24
25 public class FormatDate extends SystemFunction implements XSLTFunction {
26
27     public void checkArguments(StaticContext env) throws XPathException {
28         int numArgs = argument.length;
29         if (numArgs != 2 && numArgs != 5) {
30             throw new StaticError("Function " + getDisplayName(env.getNamePool()) +
31                     " must have either two or five arguments",
32                     ExpressionTool.getLocator(this));
33         }
34         super.checkArguments(env);
35     }
36
37     /**
38      * Evaluate in a general context
39      */

40
41     public Item evaluateItem(XPathContext context) throws XPathException {
42         CalendarValue value = (CalendarValue)argument[0].evaluateItem(context);
43         if (value==null) {
44             return null;
45         }
46         //DateTimeValue value = (DateTimeValue)item.convert(Type.DATE_TIME, context);
47
String JavaDoc format = argument[1].evaluateItem(context).getStringValue();
48
49         String JavaDoc language;
50
51         if (argument.length > 2) {
52             AtomicValue languageVal = (AtomicValue)argument[2].evaluateItem(context);
53             //StringValue calendarVal = (StringValue)argument[3].evaluateItem(context);
54
//StringValue countryVal = (StringValue)argument[4].evaluateItem(context);
55
if (languageVal==null) {
56                 language = Locale.getDefault().getLanguage();
57             } else {
58                 language = languageVal.getStringValue();
59                 if (language.length() >= 2) {
60                     language = language.substring(0, 2);
61                 } else {
62                     language = Locale.getDefault().getLanguage();
63                 }
64             }
65         } else {
66             language = Locale.getDefault().getLanguage();
67         }
68
69         return new StringValue(formatDate(value, format, language, context));
70     }
71
72     /**
73      * This method analyzes the formatting picture and delegates the work of formatting
74      * individual parts of the date.
75      */

76
77     private static CharSequence JavaDoc formatDate(CalendarValue value, String JavaDoc format, String JavaDoc language, XPathContext context)
78     throws XPathException {
79
80         Numberer numberer = NumberInstruction.makeNumberer(language, context);
81         FastStringBuffer sb = new FastStringBuffer(32);
82         int i = 0;
83         while (true) {
84             while (i < format.length() && format.charAt(i) != '[') {
85                 sb.append(format.charAt(i));
86                 if (format.charAt(i) == ']') {
87                     i++;
88                     if (i == format.length() || format.charAt(i) != ']') {
89                         DynamicError e = new DynamicError("Closing ']' in date picture must be written as ']]'");
90                         e.setXPathContext(context);
91                         throw e;
92                     }
93                 }
94                 i++;
95             }
96             if (i == format.length()) {
97                 break;
98             }
99             // look for '[['
100
i++;
101             if (format.charAt(i) == '[') {
102                 sb.append('[');
103                 i++;
104             } else {
105                 int close = format.indexOf("]", i);
106                 if (close == -1) {
107                     DynamicError e = new DynamicError("Date format contains a '[' with no matching ']'");
108                     e.setXPathContext(context);
109                     throw e;
110                 }
111                 String JavaDoc componentFormat = format.substring(i, close);
112                 sb.append(formatComponent(value, componentFormat.trim(), numberer, context));
113                 i = close+1;
114             }
115         }
116         return sb;
117     }
118
119     private static Pattern JavaDoc componentPattern =
120             Pattern.compile("([YMDdWwFHhmsfZzPCE])\\s*(.*)");
121
122     private static CharSequence JavaDoc formatComponent(CalendarValue value, String JavaDoc specifier, Numberer numberer, XPathContext context)
123     throws XPathException {
124         boolean ignoreDate = (value instanceof TimeValue);
125         boolean ignoreTime = (value instanceof DateValue);
126         DateTimeValue dtvalue;
127         if (ignoreDate) {
128             dtvalue = ((TimeValue)value).toDateTime();
129         } else if (ignoreTime) {
130             dtvalue = (DateTimeValue)value.convert(Type.DATE_TIME, context);
131         } else {
132             dtvalue = (DateTimeValue)value;
133         }
134         Calendar JavaDoc cal = dtvalue.getCalendar();
135         Matcher JavaDoc matcher = componentPattern.matcher(specifier);
136         if (!matcher.matches()) {
137             try {
138                 context.getController().getErrorListener().warning(
139                         new DynamicError("Unrecognized date/time component [" + specifier + "] (ignored)"));
140             } catch (TransformerException JavaDoc e) {
141                 throw DynamicError.makeDynamicError(e);
142             }
143             return "";
144         }
145         String JavaDoc component = matcher.group(1);
146         String JavaDoc format = matcher.group(2);
147         if (format==null) {
148             format = "";
149         }
150         boolean defaultFormat = false;
151         if ("".equals(format) || format.startsWith(",")) {
152             defaultFormat = true;
153             switch (component.charAt(0) ) {
154                 case 'F':
155                     format = "Nn" + format;
156                     break;
157                 case 'P':
158                     format = 'n' + format;
159                     break;
160                 case 'C':
161                 case 'E':
162                     format = 'N' + format;
163                     break;
164                 case 'm':
165                 case 's':
166                     format = "01" + format;
167                     break;
168                 default:
169                     format = '1' + format;
170             }
171         }
172
173         switch (component.charAt(0)) {
174             case 'Y': // year
175
if (ignoreDate) {
176                     return "";
177                 } else {
178                     return formatNumber(component, cal.get(Calendar.YEAR), format, defaultFormat, numberer, context);
179                 }
180             case 'M': // month
181
if (ignoreDate) {
182                     return "";
183                 } else {
184                     return formatNumber(component, cal.get(Calendar.MONTH)+1, format, defaultFormat, numberer, context);
185                 }
186             case 'D': // day in month
187
if (ignoreDate) {
188                     return "";
189                 } else {
190                     return formatNumber(component, cal.get(Calendar.DAY_OF_MONTH), format, defaultFormat, numberer, context);
191                 }
192             case 'd': // day in year
193
if (ignoreDate) {
194                     return "";
195                 } else {
196                     return formatNumber(component, cal.get(Calendar.DAY_OF_YEAR), format, defaultFormat, numberer, context);
197                 }
198             case 'W': // week of year
199
if (ignoreDate) {
200                     return "";
201                 } else {
202                     return formatNumber(component, cal.get(Calendar.WEEK_OF_YEAR), format, defaultFormat, numberer, context);
203                 }
204             case 'w': // week in month
205
if (ignoreDate) {
206                     return "";
207                 } else {
208                     return formatNumber(component, cal.get(Calendar.WEEK_OF_MONTH), format, defaultFormat, numberer, context);
209                 }
210             case 'H': // hour in day
211
if (ignoreTime) {
212                     return "";
213                 } else {
214                     return formatNumber(component, cal.get(Calendar.HOUR_OF_DAY), format, defaultFormat, numberer, context);
215                 }
216             case 'h': // hour in half-day (12 hour clock)
217
if (ignoreTime) {
218                     return "";
219                 } else {
220                     int hr = cal.get(Calendar.HOUR);
221                     if (hr==0) hr = 12;
222                     return formatNumber(component, hr, format, defaultFormat, numberer, context);
223                 }
224             case 'm': // minutes
225
if (ignoreTime) {
226                     return "";
227                 } else {
228                     return formatNumber(component, cal.get(Calendar.MINUTE), format, defaultFormat, numberer, context);
229                 }
230             case 's': // seconds
231
if (ignoreTime) {
232                     return "";
233                 } else {
234                     return formatNumber(component, cal.get(Calendar.SECOND), format, defaultFormat, numberer, context);
235                 }
236             case 'f': // fractional seconds
237
// ignore the format
238
if (ignoreTime) {
239                     return "";
240                 } else {
241                     int millis = cal.get(Calendar.MILLISECOND) % 1000;
242                     return ((1000 + millis)+"").substring(1);
243                 }
244             case 'Z': // timezone
245
case 'z': // timezone
246
FastStringBuffer sbz = new FastStringBuffer(8);
247                 DateTimeValue.appendTimezone(cal, sbz);
248                 return sbz.toString();
249                 // TODO: this isn't the correct format for timezone
250

251             case 'F': // day of week
252
if (ignoreDate) {
253                     return "";
254                 } else {
255                     return formatNumber(component, cal.get(Calendar.DAY_OF_WEEK), format, defaultFormat, numberer, context);
256                 }
257             case 'P': // am/pm marker
258
if (ignoreTime) {
259                     return "";
260                 } else {
261                     int hour = cal.get(Calendar.HOUR_OF_DAY);
262                     int minutes = cal.get(Calendar.MINUTE);
263                     return formatNumber(component, hour*60 + minutes, format, defaultFormat, numberer, context);
264                 }
265             case 'C': // calendar
266
return "Gregorian";
267             case 'E': // era
268
if (ignoreDate) {
269                     return "";
270                 } else {
271                     return "*AD*";
272                 }
273                 //return formatEra(cal.get(Calendar.ERA), format);
274
default:
275                 DynamicError e = new DynamicError("Unknown formatDate/time component specifier '" + format.charAt(0) + '\'');
276                 e.setXPathContext(context);
277                 throw e;
278         }
279     }
280
281     private static Pattern JavaDoc formatPattern =
282             Pattern.compile("([^ot,]*?)([ot]?)(,.*)?");
283
284     private static Pattern JavaDoc widthPattern =
285             Pattern.compile(",(\\*|[0-9]+)(\\-(\\*|[0-9]+))?");
286
287     private static CharSequence JavaDoc formatNumber(String JavaDoc component, int value,
288                                              String JavaDoc format, boolean defaultFormat, Numberer numberer, XPathContext context)
289     throws XPathException {
290         Matcher JavaDoc matcher = formatPattern.matcher(format);
291         if (!matcher.matches()) {
292             matcher = formatPattern.matcher("1");
293             matcher.matches();
294         }
295         String JavaDoc primary = matcher.group(1);
296         String JavaDoc modifier = matcher.group(2);
297         String JavaDoc letterValue = ("t".equals(modifier) ? "traditional" : null);
298         String JavaDoc ordinal = ("o".equals(modifier) ? numberer.getOrdinalSuffixForDateTime(component) : null);
299         String JavaDoc widths = matcher.group(3);
300         int min, max;
301
302         if (widths==null || "".equals(widths)) {
303             min = 1;
304             max = Integer.MAX_VALUE;
305         } else {
306             int[] range = getWidths(widths, context);
307             min = range[0];
308             max = range[1];
309             if (defaultFormat) {
310                 // if format was defaulted, the explicit widths override the implicit format
311
if (primary.endsWith("1") && min != primary.length()) {
312                     FastStringBuffer sb = new FastStringBuffer(min+1);
313                     for (int i=1; i<min; i++) {
314                         sb.append('0');
315                     }
316                     sb.append('1');
317                     primary = sb.toString();
318                 }
319             }
320         }
321
322         if ("P".equals(component)) {
323             // A.M./P.M. can only be formatted as a name
324
if (!("N".equals(primary) || "n".equals(primary) || "Nn".equals(primary))) {
325                 primary = "n";
326             }
327         }
328
329         if ("N".equals(primary) || "n".equals(primary) || "Nn".equals(primary)) {
330             String JavaDoc s = "";
331             if ("M".equals(component)) {
332                 s = numberer.monthName(value, min, max);
333             } else if ("F".equals(component)) {
334                 s = numberer.dayName(value, min, max);
335             } else if ("P".equals(component)) {
336                 s = numberer.halfDayName(value, min, max);
337             } else {
338                 primary = "1";
339             }
340             if ("N".equals(primary)) {
341                 return s.toUpperCase();
342             } else if ("n".equals(primary)) {
343                 return s.toLowerCase();
344             } else {
345                 return s;
346             }
347         }
348
349         String JavaDoc s = numberer.format(value, primary, 0, ",", letterValue, ordinal);
350
351         while (s.length() < min) {
352             s = ("00000000"+s).substring(s.length()+8-min);
353         }
354         if (s.length() > max) {
355             // the year is the only field we allow to be truncated
356
if (component.charAt(0) == 'Y') {
357                 s = s.substring(s.length() - max);
358             }
359         }
360         return s;
361     }
362
363     private static int[] getWidths(String JavaDoc widths, XPathContext context) throws XPathException {
364         try {
365             int min = -1;
366             int max = -1;
367
368             if (!"".equals(widths)) {
369                 Matcher JavaDoc widthMatcher = widthPattern.matcher(widths);
370                 if (widthMatcher.matches()) {
371                     String JavaDoc smin = widthMatcher.group(1);
372                     if (smin==null || "".equals(smin) || "*".equals(smin)) {
373                         min = 1;
374                     } else {
375                         min = Integer.parseInt(smin);
376                     }
377                     String JavaDoc smax = widthMatcher.group(3);
378                     if (smax==null || "".equals(smax) || "*".equals(smax)) {
379                         max = Integer.MAX_VALUE;
380                     } else {
381                         max = Integer.parseInt(smax);
382                     }
383                 } else {
384                     try {
385                         context.getController().getErrorListener().warning(
386                                 new DynamicError("Invalid width specifier '" + widths +
387                                 "' in date/time picture (ignored)"));
388                     } catch (TransformerException JavaDoc e) {
389                         throw DynamicError.makeDynamicError(e);
390                     }
391                     min = 1;
392                     max = 50;
393                 }
394             }
395
396             if (min>max && max!=-1) {
397                 DynamicError e = new DynamicError("Minimum width in date/time picture exceeds maximum width");
398                 e.setXPathContext(context);
399                 throw e;
400             }
401             int[] result = new int[2];
402             result[0] = min;
403             result[1] = max;
404             return result;
405         } catch (NumberFormatException JavaDoc err) {
406             DynamicError e = new DynamicError("Invalid integer used as width in date/time picture");
407             e.setXPathContext(context);
408             throw e;
409         }
410     }
411
412 }
413
414
415
416 //
417
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
418
// you may not use this file except in compliance with the License. You may obtain a copy of the
419
// License at http://www.mozilla.org/MPL/
420
//
421
// Software distributed under the License is distributed on an "AS IS" basis,
422
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
423
// See the License for the specific language governing rights and limitations under the License.
424
//
425
// The Original Code is: all this file.
426
//
427
// The Initial Developer of the Original Code is Michael H. Kay
428
//
429
// Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
430
//
431
// Contributor(s): none.
432
//
433

434
Popular Tags