001: /*
002: * $Id: DateType.java,v 1.7 2005/08/24 00:37:25 ahimanikya Exp $
003: * =======================================================================
004: * Copyright (c) 2002-2005 Axion Development Team. All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions
008: * are met:
009: *
010: * 1. Redistributions of source code must retain the above
011: * copyright notice, this list of conditions and the following
012: * disclaimer.
013: *
014: * 2. Redistributions in binary form must reproduce the above copyright
015: * notice, this list of conditions and the following disclaimer in
016: * the documentation and/or other materials provided with the
017: * distribution.
018: *
019: * 3. The names "Tigris", "Axion", nor the names of its contributors may
020: * not be used to endorse or promote products derived from this
021: * software without specific prior written permission.
022: *
023: * 4. Products derived from this software may not be called "Axion", nor
024: * may "Tigris" or "Axion" appear in their names without specific prior
025: * written permission.
026: *
027: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
028: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
029: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
030: * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
031: * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
032: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
033: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
034: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
035: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
036: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
037: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
038: * =======================================================================
039: */
040:
041: package org.axiondb.types;
042:
043: import java.io.DataInput;
044: import java.io.DataOutput;
045: import java.io.IOException;
046: import java.math.BigDecimal;
047: import java.sql.Date;
048: import java.sql.Time;
049: import java.sql.Timestamp;
050: import java.text.DateFormat;
051: import java.text.ParsePosition;
052: import java.text.SimpleDateFormat;
053: import java.util.TimeZone;
054:
055: import org.axiondb.AxionException;
056: import org.axiondb.DataType;
057:
058: /**
059: * Implements a date type which can generate instances of java.sql.Date and other JDBC
060: * date-related types.
061: *
062: * @author Jonathan Giron
063: * @author Ahimanikya Satapathy
064: * @version $Revision: 1.7 $
065: */
066: public class DateType extends TimestampType {
067: private static final Object LOCK_DATE_PARSING = new Object();
068: private static final Object LOCK_DATE_FORMATTING = new Object();
069:
070: // DateFormat objects are not thread safe. Do not share across threads w/o synch block.
071: private static final DateFormat[] DATE_PARSING_FORMATS = new DateFormat[] {
072: new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", LOCALE),
073: new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", LOCALE),
074: new SimpleDateFormat("yyyy-MM-dd", LOCALE),
075: new SimpleDateFormat("MM-dd-yyyy", LOCALE),
076: new SimpleDateFormat("HH:mm:ss", LOCALE),
077: DateFormat.getTimeInstance(DateFormat.SHORT, LOCALE) };
078:
079: private static final DateFormat ISO_DATE_FORMAT = new SimpleDateFormat(
080: "yyyy-MM-dd");
081:
082: static {
083: for (int i = 0; i < DATE_PARSING_FORMATS.length; i++) {
084: DATE_PARSING_FORMATS[i].setLenient(false);
085: }
086: }
087:
088: /* Increment to use in computing a successor value. */
089: // One day = 1 day x 24 hr/day x 60 min/hr x 60 sec/min x 1000 ms/sec
090: static final long INCREMENT_DAY = 1 * 24 * 60 * 60 * 1000;
091:
092: static final long serialVersionUID = 7318179645848578342L;
093:
094: public static long normalizeToUTCZeroHour(long rawTimeMillis) {
095: return (rawTimeMillis % INCREMENT_DAY == 0) ? rawTimeMillis
096: : (rawTimeMillis / INCREMENT_DAY) * INCREMENT_DAY;
097: }
098:
099: public boolean accepts(Object value) {
100: if (null == value) {
101: return true;
102: } else if (value instanceof Number) {
103: return true;
104: } else if (value instanceof String) {
105: return true;
106: } else if (value instanceof java.util.Date) {
107: return true;
108: } else if (value instanceof Date) {
109: return true;
110: } else if (value instanceof Time) {
111: return true;
112: } else {
113: return false;
114: }
115: }
116:
117: private Date convertToDate(Object value) throws AxionException {
118: if (null == value) {
119: return null;
120: } else if (value instanceof Number) {
121: return new Date(((Number) value).longValue());
122: } else if (value instanceof Timestamp) {
123: return new Date(((Timestamp) value).getTime());
124: } else if (value instanceof java.sql.Date) {
125: return new Date(((java.sql.Date) value).getTime());
126: } else if (value instanceof java.sql.Time) {
127: return new Date(((java.sql.Time) value).getTime());
128: } else if (value instanceof java.util.Date) {
129: return new Date(((java.util.Date) value).getTime());
130: } else if (value instanceof String) {
131: java.util.Date dVal = null;
132: int i = 0;
133: while (dVal == null && i < DATE_PARSING_FORMATS.length) {
134: synchronized (LOCK_DATE_PARSING) {
135: dVal = DATE_PARSING_FORMATS[i].parse(
136: (String) value, new ParsePosition(0));
137: }
138: i++;
139: }
140:
141: if (dVal == null) {
142: throw new AxionException(22007);
143: }
144: return new Date(dVal.getTime());
145: } else {
146: throw new AxionException(22007);
147: }
148: }
149:
150: /**
151: * Returns a <tt>java.sql.Date</tt> converted from the given <i>value </i>, or
152: * throws {@link IllegalArgumentException}if the given <i>value </i> isn't
153: * {@link #accepts acceptable}.
154: */
155: public Object convert(Object value) throws AxionException {
156: try {
157: if (value instanceof Date) {
158: return value;
159: }
160: return convertToDate(value);
161:
162: } catch (AxionException e) {
163: throw new AxionException("Can't convert "
164: + value.getClass().getName() + " " + value + ".");
165: }
166: }
167:
168: /**
169: * @see org.axiondb.DataType#getColumnDisplaySize()
170: */
171: public int getColumnDisplaySize() {
172: return 11;
173: }
174:
175: public int getPrecision() {
176: return 11;
177: }
178:
179: /**
180: * @see org.axiondb.DataType#getJdbcType()
181: */
182: public int getJdbcType() {
183: return java.sql.Types.DATE;
184: }
185:
186: /**
187: * @see org.axiondb.DataTypeFactory#makeNewInstance()
188: */
189: public DataType makeNewInstance() {
190: return new DateType();
191: }
192:
193: /**
194: * @see org.axiondb.DataType#successor(java.lang.Object)
195: */
196: public Object successor(Object value)
197: throws IllegalArgumentException {
198: try {
199: return new Date(toDate(value).getTime() + INCREMENT_DAY);
200: } catch (AxionException e) {
201: throw new IllegalArgumentException(e.getMessage());
202: }
203: }
204:
205: /**
206: * @see org.axiondb.DataType#supportsSuccessor()
207: */
208: public boolean supportsSuccessor() {
209: return true;
210: }
211:
212: /**
213: * @see org.axiondb.DataType#toBigDecimal(java.lang.Object)
214: */
215: public BigDecimal toBigDecimal(Object value) throws AxionException {
216: Date dval;
217: try {
218: dval = (Date) convert(value);
219: } catch (IllegalArgumentException e) {
220: throw new AxionException("Can't convert \"" + value
221: + "\" to BigDecimal.", e);
222: }
223:
224: return (dval != null) ? BigDecimal
225: .valueOf(normalizeToUTCZeroHour(dval.getTime())) : null;
226: }
227:
228: /**
229: * @see org.axiondb.DataType#toDate(java.lang.Object)
230: */
231: public Date toDate(Object value) throws AxionException {
232: return new Date(normalizeToUTCZeroHour(convertToDate(value)
233: .getTime()));
234: }
235:
236: /**
237: * @see java.lang.Object#toString()
238: */
239: public String toString() {
240: return "date";
241: }
242:
243: /**
244: * @see org.axiondb.DataType#toString(java.lang.Object)
245: */
246: public String toString(Object value) throws AxionException {
247: Date dval = convertToDate(value);
248: if (null == dval) {
249: return null;
250: }
251:
252: String ret = null;
253:
254: synchronized (LOCK_DATE_FORMATTING) {
255: ret = ISO_DATE_FORMAT.format(dval);
256: }
257: return ret;
258: }
259:
260: /**
261: * @see org.axiondb.DataType#toTime(java.lang.Object)
262: */
263: public Time toTime(Object value) throws AxionException {
264: return new Time(convertToDate(value).getTime());
265: }
266:
267: /**
268: * @see org.axiondb.DataType#toTimestamp(java.lang.Object)
269: */
270: public Timestamp toTimestamp(Object value) throws AxionException {
271: return new Timestamp(
272: normalizeToUTCZeroHour(convertToDate(value).getTime()));
273: }
274:
275: /**
276: * Overrides parent implementation to read only milliseconds (as a long) from the
277: * input stream, ignoring any nanoseconds written by TimestampType.write(). We read
278: * TimeType data assuming that they are in the same form as that of TimestampType data
279: * in order to preserve backwards compatibility, as java.sql.Time was originally
280: * mapped to {@link org.axiondb.types.TimestampType}.
281: *
282: * @param value Date object (typically a <code>java.sql.Date</code> or other
283: * convertible form) to be unpersisted
284: * @param out DataOutput to supply serialized data
285: * @throws IOException if error occurs during read
286: * @see org.axiondb.DataType#read
287: */
288: public Object read(DataInput in) throws IOException {
289: Date result = null;
290: int nanos = in.readInt();
291: if (Integer.MIN_VALUE != nanos) {
292: long millis = in.readLong();
293: result = new Date(millis);
294: }
295: return result;
296: }
297:
298: /**
299: * Overrides parent implementation to always write time (in milliseconds) as a long,
300: * writing a placeholder zero for the nanosecond field usually written by
301: * TimestampType.write().
302: *
303: * @param value Date object (typically a <code>java.sql.Date</code> or other
304: * convertible form) to be persisted
305: * @param out DataOutput to receive serialized data
306: * @throws IOException if error occurs during write
307: * @see org.axiondb.DataType#write
308: */
309: public void write(Object value, DataOutput out) throws IOException {
310: if (null == value) {
311: out.writeInt(Integer.MIN_VALUE);
312: } else {
313: try {
314: Date val = (Date) convert(value);
315: out.writeInt(0);
316: out.writeLong(val.getTime());
317: } catch (AxionException e) {
318: throw new IOException(e.getMessage());
319: }
320: }
321: }
322:
323: public static void setTimeZone(String id) {
324: TimeZone tZone = TimeZone.getTimeZone(id);
325: if (tZone != null) {
326: synchronized (LOCK_DATE_PARSING) {
327: for (int i = 0; i < DATE_PARSING_FORMATS.length; i++) {
328: DATE_PARSING_FORMATS[i].setTimeZone(tZone);
329: }
330: }
331:
332: synchronized (LOCK_DATE_FORMATTING) {
333: ISO_DATE_FORMAT.setTimeZone(tZone);
334: }
335: }
336: }
337: }
|