001: /*
002: * Copyright 2002 (C) TJDO.
003: * All rights reserved.
004: *
005: * This software is distributed under the terms of the TJDO License version 1.0.
006: * See the terms of the TJDO License in the documentation provided with this software.
007: *
008: * $Id: SqlTimestampMapping.java,v 1.6 2003/10/10 22:22:15 pierreg0 Exp $
009: */
010:
011: package com.triactive.jdo.store;
012:
013: import com.triactive.jdo.PersistenceManager;
014: import java.sql.PreparedStatement;
015: import java.sql.ResultSet;
016: import java.sql.SQLException;
017: import java.sql.Timestamp;
018: import java.sql.Types;
019: import java.util.Calendar;
020: import java.util.GregorianCalendar;
021: import java.util.TimeZone;
022: import javax.jdo.JDODataStoreException;
023:
024: public class SqlTimestampMapping extends ColumnMapping {
025: /**
026: * Maximum length of a timestamp string, based on JDBC timestamp escape
027: * format: "YYYY-MM-DD HH:MM:SS.FFFFFFFFF"
028: */
029: private static final int TIMESTAMP_STRING_LENGTH = 29;
030:
031: private static final TimeZone timeZoneUTC = TimeZone
032: .getTimeZone("UTC");
033:
034: /** Used to zero-fill the fractional seconds to nine digits. */
035: private static final String zeroes = "000000000";
036:
037: public SqlTimestampMapping(DatabaseAdapter dba, Class type) {
038: super (dba, type);
039:
040: initTypeInfo();
041: }
042:
043: public SqlTimestampMapping(Column col) {
044: super (col);
045:
046: col.checkPrimitive();
047:
048: initTypeInfo();
049: }
050:
051: public SqlTimestampMapping(ClassBaseTable table,
052: int relativeFieldNumber) {
053: this (table.newColumn(relativeFieldNumber));
054: }
055:
056: protected TypeInfo getTypeInfo() {
057: return dba
058: .getTypeInfo(new int[] { Types.TIMESTAMP, Types.CHAR });
059: }
060:
061: protected void initTypeInfo() {
062: super .initTypeInfo();
063:
064: if (col != null && typeInfo.dataType == Types.CHAR) {
065: col.setFixedLength(TIMESTAMP_STRING_LENGTH);
066: col.checkString();
067: }
068: }
069:
070: public void setObject(PersistenceManager pm, PreparedStatement ps,
071: int param, Object value) {
072: try {
073: Calendar cal = new GregorianCalendar(timeZoneUTC);
074:
075: if (value == null)
076: ps.setNull(param, typeInfo.dataType);
077: else if (typeInfo.dataType == Types.TIMESTAMP)
078: ps.setTimestamp(param, (Timestamp) value, cal);
079: else
080: ps.setString(param, timestampToString(
081: (Timestamp) value, cal));
082: } catch (SQLException e) {
083: throw dba.newDataStoreException(
084: "Can't set Timestamp parameter: value = " + value,
085: e);
086: }
087: }
088:
089: protected Timestamp getTimestamp(ResultSet rs, int param) {
090: Timestamp value;
091:
092: try {
093: Calendar cal = new GregorianCalendar(timeZoneUTC);
094:
095: if (typeInfo.dataType == Types.TIMESTAMP)
096: value = rs.getTimestamp(param, cal);
097: else {
098: String s = rs.getString(param);
099:
100: value = s == null ? null : stringToTimestamp(s, cal);
101: }
102: } catch (SQLException e) {
103: throw dba.newDataStoreException(
104: "Can't get Timestamp result: param = " + param, e);
105: }
106:
107: return value;
108: }
109:
110: public Object getObject(PersistenceManager pm, ResultSet rs,
111: int param) {
112: Timestamp value = getTimestamp(rs, param);
113:
114: if (value == null)
115: return null;
116: else
117: return value;
118: }
119:
120: public SQLExpression newSQLLiteral(QueryStatement qs, Object value) {
121: return new SqlTimestampLiteral(qs, this , (Timestamp) value);
122: }
123:
124: public SQLExpression newSQLExpression(QueryStatement qs,
125: QueryStatement.QueryColumn qsc, String fieldName) {
126: return new SqlTimestampExpression(qs, qsc);
127: }
128:
129: /**
130: * Converts a string in JDBC timestamp escape format to a
131: * <tt>java.sql.Timestamp</tt> object using the "UTC" time zone.
132: *
133: * @param s Timestamp string in format
134: * <tt>yyyy-mm-dd hh:mm:ss.fffffffff</tt>.
135: *
136: * @return Corresponding <tt>java.sql.Timestamp</tt> value.
137: *
138: * @exception java.lang.IllegalArgumentException
139: * If the given argument does not have the format
140: * <tt>yyyy-mm-dd hh:mm:ss.fffffffff</tt>.
141: *
142: * @see java.sql.Timestamp
143: */
144:
145: public static Timestamp stringToTimestamp(String s, Calendar cal) {
146: String formatError = "Bad timestamp format: \"" + s
147: + "\", must be yyyy-mm-dd hh:mm:ss.fffffffff";
148:
149: /* Split the string into date and time components. */
150: s = s.trim();
151: int dividingSpace = s.indexOf(' ');
152:
153: if (dividingSpace <= 0)
154: throw new java.lang.IllegalArgumentException(formatError);
155:
156: String date_s = s.substring(0, dividingSpace);
157: String time_s = s.substring(dividingSpace + 1);
158:
159: /* Parse the date. */
160: int firstDash = date_s.indexOf('-');
161: int secondDash = date_s.indexOf('-', firstDash + 1);
162: int firstColon = time_s.indexOf(':');
163: int secondColon = time_s.indexOf(':', firstColon + 1);
164: int period = time_s.indexOf('.', secondColon + 1);
165:
166: /* Convert the date. */
167: if (firstDash <= 0 || secondDash <= 0
168: || secondDash >= date_s.length() - 1)
169: throw new java.lang.IllegalArgumentException(formatError);
170:
171: int year = Integer.parseInt(date_s.substring(0, firstDash));
172: int month = Integer.parseInt(date_s.substring(firstDash + 1,
173: secondDash));
174: int day = Integer.parseInt(date_s.substring(secondDash + 1));
175:
176: /* Convert the time; default missing nanos. */
177: if (firstColon <= 0 || secondColon <= 0
178: || secondColon >= time_s.length() - 1)
179: throw new java.lang.IllegalArgumentException(formatError);
180:
181: int hour = Integer.parseInt(time_s.substring(0, firstColon));
182: int minute = Integer.parseInt(time_s.substring(firstColon + 1,
183: secondColon));
184: int second;
185: int nanos = 0;
186:
187: if (period < 0)
188: second = Integer
189: .parseInt(time_s.substring(secondColon + 1));
190: else if (period == 0 || period >= time_s.length() - 1)
191: throw new java.lang.IllegalArgumentException(formatError);
192: else {
193: second = Integer.parseInt(time_s.substring(secondColon + 1,
194: period));
195: String nanos_s = time_s.substring(period + 1);
196:
197: if (nanos_s.length() > zeroes.length())
198: throw new java.lang.IllegalArgumentException(
199: formatError);
200:
201: nanos_s += zeroes.substring(0, zeroes.length()
202: - nanos_s.length());
203: nanos = Integer.parseInt(nanos_s);
204: }
205:
206: cal.set(Calendar.ERA, GregorianCalendar.AD);
207: cal.set(Calendar.YEAR, year);
208: cal.set(Calendar.MONTH, month - 1); // Months are zero based in Calendar
209: cal.set(Calendar.DATE, day);
210: cal.set(Calendar.HOUR_OF_DAY, hour);
211: cal.set(Calendar.MINUTE, minute);
212: cal.set(Calendar.SECOND, second);
213:
214: Timestamp ts = new Timestamp(cal.getTime().getTime());
215: ts.setNanos(nanos);
216:
217: return ts;
218: }
219:
220: /**
221: * Formats a timestamp in JDBC timestamp escape format using the "UTC" time
222: * zone.
223: *
224: * @param ts The timestamp to be formatted.
225: *
226: * @return A String in <tt>yyyy-mm-dd hh:mm:ss.fffffffff</tt> format.
227: *
228: * @see java.sql.Timestamp
229: */
230:
231: public static String timestampToString(Timestamp ts, Calendar cal) {
232: cal.setTime(ts);
233:
234: int year = cal.get(Calendar.YEAR);
235: int month = cal.get(Calendar.MONTH) + 1; // Months are zero based in Calendar
236: int day = cal.get(Calendar.DATE);
237: int hour = cal.get(Calendar.HOUR_OF_DAY);
238: int minute = cal.get(Calendar.MINUTE);
239: int second = cal.get(Calendar.SECOND);
240:
241: String yearString = Integer.toString(year);
242: String monthString = month < 10 ? "0" + month : Integer
243: .toString(month);
244: String dayString = day < 10 ? "0" + day : Integer.toString(day);
245: String hourString = hour < 10 ? "0" + hour : Integer
246: .toString(hour);
247: String minuteString = minute < 10 ? "0" + minute : Integer
248: .toString(minute);
249: String secondString = second < 10 ? "0" + second : Integer
250: .toString(second);
251: String nanosString = Integer.toString(ts.getNanos());
252:
253: if (ts.getNanos() != 0) {
254: /* Add leading zeroes. */
255: nanosString = zeroes.substring(0, zeroes.length()
256: - nanosString.length())
257: + nanosString;
258:
259: /* Truncate trailing zeroes. */
260: int truncIndex = nanosString.length() - 1;
261:
262: while (nanosString.charAt(truncIndex) == '0')
263: --truncIndex;
264:
265: nanosString = nanosString.substring(0, truncIndex + 1);
266: }
267:
268: return (yearString + "-" + monthString + "-" + dayString + " "
269: + hourString + ":" + minuteString + ":" + secondString
270: + "." + nanosString);
271: }
272: }
|