001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.cocoon.forms.datatype.convertor;
018:
019: import org.xml.sax.ContentHandler;
020: import org.xml.sax.SAXException;
021: import org.apache.cocoon.forms.FormsConstants;
022: import org.apache.cocoon.xml.AttributesImpl;
023:
024: import java.util.Locale;
025: import java.util.Date;
026: import java.text.DateFormat;
027: import java.text.ParseException;
028: import java.text.SimpleDateFormat;
029:
030: /**
031: * A Convertor for {@link java.util.Date Date} objects backed by the
032: * {@link java.text.SimpleDateFormat SimpleDateFormat} class.
033: *
034: * <p>It can be configured to use one of three <strong>variants</strong>: date,
035: * time or datetime and one of four <strong>styles</strong>: long, full, medium or short.
036: *
037: * <p>Alternatively, a <strong>formatting pattern</strong> can be used. This can either be a locale-dependent
038: * or locale-independent formatting pattern. When looking up a formatting pattern, a mechansim
039: * similar to resource bundle lookup is used. Suppose the locale is nl-BE, then first a formatting
040: * pattern for nl-BE will be sought, then one for nl, and if that is not
041: * found, finally the locale-independent formatting pattern will be used.
042: *
043: * <p>Date parsing can be configured to be lenient or not by specifying a <code>lenient</code>
044: * boolean attribute. By default, parsing is lenient.
045: *
046: * @version $Id: FormattingDateConvertor.java 497095 2007-01-17 17:42:59Z bruno $
047: */
048: public class FormattingDateConvertor implements Convertor {
049: /** See {@link #setStyle}. */
050: private int style;
051: private int timeStyle = -1;
052: /** See {@link #setVariant}. */
053: private String variant;
054: /** Locale-specific formatting patterns. */
055: private LocaleMap localizedPatterns;
056: /** Non-locale specific formatting pattern. */
057: private String nonLocalizedPattern;
058: /** Should date parsing be lenient or not? */
059: private boolean lenient;
060:
061: public static final String DATE = "date";
062: public static final String TIME = "time";
063: public static final String DATE_TIME = "datetime";
064:
065: public FormattingDateConvertor() {
066: this .style = DateFormat.SHORT;
067: this .variant = DATE;
068: this .localizedPatterns = new LocaleMap();
069: this .lenient = true;
070: }
071:
072: public ConversionResult convertFromString(String value,
073: Locale locale, Convertor.FormatCache formatCache) {
074: SimpleDateFormat dateFormat = getDateFormat(locale, formatCache);
075: try {
076: return new ConversionResult(dateFormat.parse(value));
077: } catch (ParseException e) {
078: return ConversionResult.create("date." + this .variant);
079: }
080: }
081:
082: public String convertToString(Object value, Locale locale,
083: Convertor.FormatCache formatCache) {
084: SimpleDateFormat dateFormat = getDateFormat(locale, formatCache);
085: return dateFormat.format((Date) value);
086: }
087:
088: private final SimpleDateFormat getDateFormat(Locale locale,
089: Convertor.FormatCache formatCache) {
090: SimpleDateFormat dateFormat = null;
091: if (formatCache != null)
092: dateFormat = (SimpleDateFormat) formatCache.get();
093: if (dateFormat == null) {
094: dateFormat = getDateFormat(locale);
095: if (formatCache != null)
096: formatCache.store(dateFormat);
097: }
098: dateFormat.setLenient(lenient);
099: return dateFormat;
100: }
101:
102: protected SimpleDateFormat getDateFormat(Locale locale) {
103: SimpleDateFormat dateFormat = null;
104:
105: int timeStyle = this .timeStyle != -1 ? this .timeStyle : style;
106: if (this .variant.equals(DATE)) {
107: //dateFormat = I18nSupport.getInstance().getDateFormat(style, locale);
108: dateFormat = (SimpleDateFormat) DateFormat.getDateInstance(
109: style, locale);
110: } else if (this .variant.equals(TIME)) {
111: //dateFormat = I18nSupport.getInstance().getTimeFormat(style, locale);
112: dateFormat = (SimpleDateFormat) DateFormat.getTimeInstance(
113: timeStyle, locale);
114: } else if (this .variant.equals(DATE_TIME)) {
115: //dateFormat = I18nSupport.getInstance().getDateTimeFormat(style, style, locale);
116: dateFormat = (SimpleDateFormat) DateFormat
117: .getDateTimeInstance(style, timeStyle, locale);
118: }
119:
120: String pattern = (String) localizedPatterns.get(locale);
121:
122: if (pattern != null)
123: // Note: this was previously using applyLocalizedPattern() which allows to use
124: // a locale-specific pattern syntax, e.g. in french "j" (jour) for "d" and
125: // "a" (annee) for "y". But the localized pattern syntax is very little known and thus
126: // led to some weird pattern syntax error messages.
127: dateFormat.applyPattern(pattern);
128: else if (nonLocalizedPattern != null)
129: dateFormat.applyPattern(nonLocalizedPattern);
130:
131: dateFormat.setLenient(lenient);
132: return dateFormat;
133: }
134:
135: public Class getTypeClass() {
136: return Date.class;
137: }
138:
139: /**
140: *
141: * @param style one of the constants FULL, LONG, MEDIUM or SHORT defined in the {@link Date} class.
142: */
143: public void setStyle(int style) {
144: this .style = style;
145: }
146:
147: /**
148: * Sets the style for times, if not specified it defaults to the same style as for dates.
149: *
150: * @param style one of the constants FULL, LONG, MEDIUM or SHORT defined in the {@link Date} class.
151: */
152: public void setTimeStyle(int style) {
153: this .timeStyle = style;
154: }
155:
156: public void setVariant(String variant) {
157: if (DATE.equals(variant) || TIME.equals(variant)
158: || DATE_TIME.equals(variant)) {
159: this .variant = variant;
160: } else {
161: throw new IllegalArgumentException(
162: "Invalid value for variant parameter.");
163: }
164: }
165:
166: public void addFormattingPattern(Locale locale, String pattern) {
167: localizedPatterns.put(locale, pattern);
168: }
169:
170: public void setNonLocalizedPattern(String pattern) {
171: this .nonLocalizedPattern = pattern;
172: }
173:
174: public void setLenient(boolean lenient) {
175: this .lenient = lenient;
176: }
177:
178: private static final String CONVERTOR_EL = "convertor";
179:
180: public void generateSaxFragment(ContentHandler contentHandler,
181: Locale locale) throws SAXException {
182: String pattern = getDateFormat(locale).toPattern();
183:
184: if (pattern != null) {
185: AttributesImpl attrs = new AttributesImpl();
186: attrs.addCDATAAttribute("pattern", pattern);
187: attrs.addCDATAAttribute("variant", this.variant);
188: contentHandler.startElement(FormsConstants.INSTANCE_NS,
189: CONVERTOR_EL, FormsConstants.INSTANCE_PREFIX_COLON
190: + CONVERTOR_EL, attrs);
191: contentHandler.endElement(FormsConstants.INSTANCE_NS,
192: CONVERTOR_EL, FormsConstants.INSTANCE_PREFIX_COLON
193: + CONVERTOR_EL);
194: }
195: }
196: }
|