001: /*
002: * $Id: TimestampType.java,v 1.33 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.Timestamp;
048: import java.text.DateFormat;
049: import java.text.ParsePosition;
050: import java.text.SimpleDateFormat;
051: import java.util.Comparator;
052: import java.util.Date;
053: import java.util.Locale;
054: import java.util.TimeZone;
055:
056: import org.axiondb.AxionException;
057: import org.axiondb.DataType;
058:
059: /**
060: * A {@link DataType}representing a timestamp value.
061: *
062: * @version $Revision: 1.33 $ $Date: 2005/08/24 00:37:25 $
063: * @author Chuck Burdick
064: * @author Ritesh Adval
065: * @author Ahimanikya Satapathy
066: * @author Jonathan Giron
067: */
068: public class TimestampType extends BaseDataType implements Comparator {
069: // Irrespective of the JVM's Locale lets pick a Locale for use on any JVM
070: public static final Locale LOCALE = Locale.UK;
071: private static TimeZone TIMEZONE = TimeZone.getDefault();
072:
073: private static final Object LOCK_TIMESTAMP_PARSING = new Object();
074: private static final Object LOCK_TIMESTAMP_FORMATTING = new Object();
075:
076: // DateFormat objects are not thread safe. Do not share across threads w/o synch block.
077: private static final DateFormat[] TIMESTAMP_PARSING_FORMATS = new DateFormat[] {
078: new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", LOCALE),
079: new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", LOCALE),
080: new SimpleDateFormat("yyyy-MM-dd", LOCALE),
081: new SimpleDateFormat("MM-dd-yyyy", LOCALE),
082: new SimpleDateFormat("HH:mm:ss", LOCALE),
083: DateFormat.getTimeInstance(DateFormat.SHORT, LOCALE) };
084:
085: private static final SimpleDateFormat ISO_TIMESTAMP_FORMATTING_FORMAT = new SimpleDateFormat(
086: "yyyy-MM-dd HH:mm:ss.SSS", LOCALE);
087:
088: static {
089: for (int i = 0; i < TIMESTAMP_PARSING_FORMATS.length; i++) {
090: TIMESTAMP_PARSING_FORMATS[i].setLenient(false);
091: }
092: }
093:
094: public TimestampType() {
095: }
096:
097: public int getJdbcType() {
098: return java.sql.Types.TIMESTAMP;
099: }
100:
101: public String getPreferredValueClassName() {
102: return "java.lang.Long";
103: }
104:
105: public String toString() {
106: return "timestamp";
107: }
108:
109: public int getColumnDisplaySize() {
110: return 29;
111: }
112:
113: public int getPrecision() {
114: return 29;
115: }
116:
117: /**
118: * Returns <code>true</code> iff <i>value </i> is <code>null</code>, a
119: * <tt>Number</tt>, or a <tt>String</tt> that can be converted to a <tt>Long</tt>.
120: */
121: public boolean accepts(Object value) {
122: if (null == value) {
123: return true;
124: } else if (value instanceof Number) {
125: return true;
126: } else if (value instanceof java.util.Date) {
127: return true;
128: } else if (value instanceof String) {
129: return true;
130: } else {
131: return false;
132: }
133: }
134:
135: /**
136: * Returns an <tt>Byte</tt> converted from the given <i>value </i>, or throws
137: * {@link IllegalArgumentException}if the given <i>value </i> isn't
138: * {@link #accepts acceptable}.
139: */
140: public Object convert(Object value) throws AxionException {
141: if (null == value) {
142: return null;
143: } else if (value instanceof Number) {
144: return new Timestamp(((Number) value).longValue());
145: } else if (value instanceof Timestamp) {
146: return value;
147: } else if (value instanceof java.sql.Date) {
148: return new Timestamp(((java.sql.Date) value).getTime());
149: } else if (value instanceof java.sql.Time) {
150: return new Timestamp(((java.sql.Time) value).getTime());
151: } else if (value instanceof java.util.Date) {
152: return new Timestamp(((java.util.Date) value).getTime());
153: } else if (value instanceof String) {
154: java.util.Date dVal = null;
155: int i = 0;
156: while (dVal == null && i < TIMESTAMP_PARSING_FORMATS.length) {
157: synchronized (LOCK_TIMESTAMP_PARSING) {
158: dVal = TIMESTAMP_PARSING_FORMATS[i].parse(
159: (String) value, new ParsePosition(0));
160: }
161: i++;
162: }
163:
164: if (dVal == null) {
165: throw new AxionException(22007);
166: }
167: return new Timestamp(dVal.getTime());
168: } else {
169: throw new AxionException(22007);
170: }
171: }
172:
173: public BigDecimal toBigDecimal(Object value) throws AxionException {
174: Timestamp tval;
175: long time_ms, time_ns;
176: Long time;
177: try {
178: tval = (Timestamp) convert(value);
179: } catch (AxionException e) {
180: throw new AxionException("Can't convert \"" + value
181: + "\" to BigDecimal.", e);
182: }
183: // returns millisec since Jan 1 1970 00:00:00 GMT (integral sec only)
184: time_ms = tval.getTime();
185: // convert nanosec to millisec and add to time_ms (fractional sec)
186: time_ns = tval.getNanos() / 1000000;
187: time = new Long(time_ms + time_ns);
188: return super .toBigDecimal(time);
189: }
190:
191: private Timestamp convertToTimestamp(Object value)
192: throws AxionException {
193: try {
194: Timestamp tval = (Timestamp) convert(value);
195: return tval;
196: } catch (IllegalArgumentException e) {
197: throw new AxionException("Can't convert \"" + value
198: + "\" to Timestamp.", e);
199: }
200: }
201:
202: public java.sql.Date toDate(Object value) throws AxionException {
203: return new java.sql.Date(convertToTimestamp(value).getTime());
204: }
205:
206: public String toString(Object value) throws AxionException {
207: Timestamp val = convertToTimestamp(value);
208: if (val == null) {
209: return null;
210: }
211: String ret = null;
212: synchronized (LOCK_TIMESTAMP_FORMATTING) {
213: ret = ISO_TIMESTAMP_FORMATTING_FORMAT.format(val);
214: }
215: return ret;
216: }
217:
218: public java.sql.Timestamp toTimestamp(Object value)
219: throws AxionException {
220: return convertToTimestamp(value);
221: }
222:
223: public java.sql.Time toTime(Object value) throws AxionException {
224: return new java.sql.Time(convertToTimestamp(value).getTime());
225: }
226:
227: public boolean supportsSuccessor() {
228: return true;
229: }
230:
231: public Object successor(Object value)
232: throws IllegalArgumentException {
233: Timestamp v = (Timestamp) value;
234: Timestamp result = new Timestamp(v.getTime());
235: if (v.getNanos() == 999999999) {
236: result.setTime(result.getTime() + 1);
237: result.setNanos(0);
238: } else {
239: result.setNanos(v.getNanos() + 1);
240: }
241: return result;
242: }
243:
244: /**
245: * @see #write
246: */
247: public Object read(DataInput in) throws IOException {
248: Timestamp result = null;
249: int nanos = in.readInt();
250: if (Integer.MIN_VALUE != nanos) {
251: long time = in.readLong();
252: result = new Timestamp(time);
253: result.setNanos(nanos);
254: }
255: return result;
256: }
257:
258: /**
259: * Writes the given <i>value </i> to the given <code>DataOutput</code>.
260: * <code>Null</code> values are written as <code>Integer.MIN_VALUE</code>. All
261: * other values are written directly with an <code>int</code> representing
262: * nanoseconds first, and a <code>long</code> representing the time.
263: *
264: * @param value the value to write, which must be {@link #accepts acceptable}
265: */
266: public void write(Object value, DataOutput out) throws IOException {
267: if (null == value) {
268: out.writeInt(Integer.MIN_VALUE);
269: } else {
270: try {
271: Timestamp val = (Timestamp) convert(value);
272: out.writeInt(val.getNanos());
273: out.writeLong(val.getTime());
274: } catch (AxionException e) {
275: throw new IOException(e.getMessage());
276: }
277: }
278: }
279:
280: public DataType makeNewInstance() {
281: return new TimestampType();
282: }
283:
284: public int compare(Object left, Object right) {
285: long leftmillis = getMillis(left);
286: int leftnanos = getNanos(left);
287:
288: long rightmillis = getMillis(right);
289: int rightnanos = getNanos(right);
290:
291: if (leftmillis == rightmillis) {
292: if (leftnanos > rightnanos) {
293: return 1;
294: } else if (leftnanos == rightnanos) {
295: return 0;
296: } else {
297: return -1;
298: }
299: } else if (leftmillis > rightmillis) {
300: return 1;
301: } else {
302: return -1;
303: }
304: }
305:
306: private long getMillis(Object obj) {
307: return ((Date) obj).getTime();
308: }
309:
310: private int getNanos(Object obj) {
311: if (obj instanceof Timestamp) {
312: return ((Timestamp) obj).getNanos();
313: }
314: return 0;
315: }
316:
317: protected Comparator getComparator() {
318: return this ;
319: }
320:
321: public static void setTimeZone(String id) {
322: TimeZone tZone = TimeZone.getTimeZone(id);
323: if (tZone != null) {
324: synchronized (LOCK_TIMESTAMP_PARSING) {
325: TIMEZONE = tZone;
326: for (int i = 0; i < TIMESTAMP_PARSING_FORMATS.length; i++) {
327: TIMESTAMP_PARSING_FORMATS[i].setTimeZone(TIMEZONE);
328: }
329: }
330:
331: synchronized (LOCK_TIMESTAMP_FORMATTING) {
332: ISO_TIMESTAMP_FORMATTING_FORMAT.setTimeZone(TIMEZONE);
333: }
334: }
335: }
336:
337: public static TimeZone getTimeZone() {
338: return TIMEZONE;
339: }
340:
341: private static final long serialVersionUID = -4933113827044335868L;
342: }
|