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.wicket.datetime;
018:
019: import java.text.ParseException;
020: import java.util.Date;
021: import java.util.Locale;
022: import java.util.TimeZone;
023:
024: import org.apache.wicket.Component;
025: import org.apache.wicket.Session;
026: import org.apache.wicket.protocol.http.request.WebClientInfo;
027: import org.apache.wicket.request.ClientInfo;
028: import org.apache.wicket.util.convert.ConversionException;
029: import org.apache.wicket.util.convert.IConverter;
030: import org.apache.wicket.util.string.Strings;
031: import org.joda.time.DateMidnight;
032: import org.joda.time.DateTime;
033: import org.joda.time.DateTimeZone;
034: import org.joda.time.MutableDateTime;
035: import org.joda.time.format.DateTimeFormatter;
036:
037: /**
038: * Base class for Joda Time based date converters. It contains the logic to
039: * parse and format, optionally taking the time zone difference between clients
040: * and the server into account.
041: * <p>
042: * Converters of this class are best suited for per-component use.
043: * </p>
044: *
045: * @author eelcohillenius
046: */
047: public abstract class DateConverter implements IConverter {
048: /**
049: * Whether to apply the time zone difference when interpreting dates.
050: */
051: private final boolean applyTimeZoneDifference;
052:
053: /**
054: * Optional component to use for determining the locale.
055: */
056: private Component component = null;
057:
058: /**
059: * Construct.
060: * </p>
061: * When applyTimeZoneDifference is true, the current time is applied on the
062: * parsed date, and the date will be corrected for the time zone difference
063: * between the server and the client. For instance, if I'm in Seattle and
064: * the server I'm working on is in Amsterdam, the server is 9 hours ahead.
065: * So, if I'm inputting say 12/24 at a couple of hours before midnight, at
066: * the server it is already 12/25. If this boolean is true, it will be
067: * transformed to 12/25, while the client sees 12/24.
068: * </p>
069: *
070: * @param applyTimeZoneDifference
071: * whether to apply the difference in time zones between client
072: * and server
073: */
074: public DateConverter(boolean applyTimeZoneDifference) {
075: this .applyTimeZoneDifference = applyTimeZoneDifference;
076: }
077:
078: /**
079: * Gets the locale to use.
080: *
081: * @return the locale from either the component if that is set, or from the
082: * session
083: */
084: protected Locale getLocale() {
085: Component c = getComponent();
086: return (c != null) ? c.getLocale() : Session.get().getLocale();
087: }
088:
089: /**
090: * @see org.apache.wicket.util.convert.IConverter#convertToObject(java.lang.String,
091: * java.util.Locale)
092: */
093: public Object convertToObject(String value, Locale locale) {
094: if (Strings.isEmpty(value)) {
095: return null;
096: }
097:
098: DateTimeFormatter format = getFormat();
099: if (format == null) {
100: throw new IllegalStateException("format must be not null");
101: }
102:
103: if (applyTimeZoneDifference) {
104: TimeZone zone = getClientTimeZone();
105: // instantiate now/ current time
106: MutableDateTime dt = new MutableDateTime(new DateMidnight());
107: if (zone != null) {
108: // set time zone for client
109: format = format
110: .withZone(DateTimeZone.forTimeZone(zone));
111: dt.setZone(DateTimeZone.forTimeZone(zone));
112: }
113: try {
114: // parse date retaining the time of the submission
115: int result = format.parseInto(dt, value, 0);
116: if (result < 0) {
117: throw new ConversionException(new ParseException(
118: "unable to parse date " + value, ~result));
119: }
120: } catch (RuntimeException e) {
121: throw new ConversionException(e);
122: }
123: // apply the server time zone to the parsed value
124: dt.setZone(getTimeZone());
125: return dt.toDate();
126: } else {
127: try {
128: DateTime date = format.parseDateTime(value);
129: return date.toDate();
130: } catch (RuntimeException e) {
131: throw new ConversionException(e);
132: }
133: }
134: }
135:
136: /**
137: * @see org.apache.wicket.util.convert.IConverter#convertToString(java.lang.Object,
138: * java.util.Locale)
139: */
140: public String convertToString(Object value, Locale locale) {
141: DateTime dt = new DateTime(((Date) value).getTime(),
142: getTimeZone());
143: DateTimeFormatter format = getFormat();
144:
145: if (applyTimeZoneDifference) {
146: TimeZone zone = getClientTimeZone();
147: if (zone != null) {
148: // apply time zone to formatter
149: format = format
150: .withZone(DateTimeZone.forTimeZone(zone));
151: }
152: }
153: return format.print(dt);
154: }
155:
156: /**
157: * Gets whether to apply the time zone difference when interpreting dates.
158: *
159: * </p>
160: * When true, the current time is applied on the parsed date, and the date
161: * will be corrected for the time zone difference between the server and the
162: * client. For instance, if I'm in Seattle and the server I'm working on is
163: * in Amsterdam, the server is 9 hours ahead. So, if I'm inputting say 12/24
164: * at a couple of hours before midnight, at the server it is already 12/25.
165: * If this boolean is true, it will be transformed to 12/25, while the
166: * client sees 12/24.
167: * </p>
168: *
169: * @return whether to apply the difference in time zones between client and
170: * server
171: */
172: public final boolean getApplyTimeZoneDifference() {
173: return applyTimeZoneDifference;
174: }
175:
176: /**
177: * @return Gets the pattern that is used for printing and parsing
178: */
179: public abstract String getDatePattern();
180:
181: /**
182: * Gets the client's time zone.
183: *
184: * @return The client's time zone or null
185: */
186: protected TimeZone getClientTimeZone() {
187: ClientInfo info = Session.get().getClientInfo();
188: if (info instanceof WebClientInfo) {
189: return ((WebClientInfo) info).getProperties().getTimeZone();
190: }
191: return null;
192: }
193:
194: /**
195: * @return formatter The formatter for the current conversion
196: */
197: protected abstract DateTimeFormatter getFormat();
198:
199: /**
200: * Gets the server time zone. Override this method if you want to fix to a
201: * certain time zone, regardless of what actual time zone the server is in.
202: *
203: * @return The server time zone
204: */
205: protected DateTimeZone getTimeZone() {
206: return DateTimeZone.getDefault();
207: }
208:
209: /**
210: * @return optional component to use for determining the locale.
211: */
212: public final Component getComponent() {
213: return component;
214: }
215:
216: /**
217: * Sets component for getting the locale
218: *
219: * @param component
220: * optional component to use for determining the locale.
221: */
222: public final void setComponent(Component component) {
223: this.component = component;
224: }
225: }
|