001: package org.apache.ojb.broker.metadata;
002:
003: /* Copyright 2002-2005 The Apache Software Foundation
004: *
005: * Licensed under the Apache License, Version 2.0 (the "License");
006: * you may not use this file except in compliance with the License.
007: * 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:
018: import java.lang.reflect.Method;
019: import java.sql.Date;
020: import java.sql.Time;
021: import java.sql.Timestamp;
022: import java.sql.Types;
023: import java.util.Arrays;
024: import java.io.Serializable;
025:
026: import org.apache.commons.lang.ArrayUtils;
027: import org.apache.commons.lang.ObjectUtils;
028: import org.apache.commons.lang.SerializationUtils;
029: import org.apache.commons.lang.builder.ToStringBuilder;
030: import org.apache.ojb.broker.OJBRuntimeException;
031:
032: /**
033: * Encapsulates all {@link FieldType} as inner classes.
034: *
035: * @version $Id: FieldTypeClasses.java,v 1.1.2.3 2005/12/21 22:26:10 tomdz Exp $
036: */
037: class FieldTypeClasses {
038: private FieldTypeClasses() {
039: }
040:
041: /**
042: * Returns a {@link FieldType} instance for the given sql type
043: * (see {@link java.sql.Types}) as specified in JDBC 3.0 specification
044: * (see JDBC 3.0 specification <em>Appendix B, Data Type Conversion Tables</em>).
045: *
046: * @param jdbcType Specify the type to look for.
047: * @return A new specific {@link FieldType} instance.
048: */
049: static FieldType newFieldType(JdbcType jdbcType) {
050: FieldType result = null;
051: switch (jdbcType.getType()) {
052: case Types.ARRAY:
053: result = new ArrayFieldType();
054: break;
055: case Types.BIGINT:
056: result = new LongFieldType();
057: break;
058: case Types.BINARY:
059: result = new ByteArrayFieldType();
060: break;
061: case Types.BIT:
062: result = new BooleanFieldType();
063: break;
064: case Types.BLOB:
065: result = new BlobFieldType();
066: break;
067: case Types.CHAR:
068: result = new StringFieldType();
069: break;
070: case Types.CLOB:
071: result = new ClobFieldType();
072: break;
073: case Types.DATE:
074: result = new DateFieldType();
075: break;
076: case Types.DECIMAL:
077: result = new BigDecimalFieldType();
078: break;
079: // Not needed, user have to use the underlying sql datatype in OJB mapping files
080: // case Types.DISTINCT:
081: // result = new DistinctFieldType();
082: // break;
083: case Types.DOUBLE:
084: result = new DoubleFieldType();
085: break;
086: case Types.FLOAT:
087: result = new FloatFieldType();
088: break;
089: case Types.INTEGER:
090: result = new IntegerFieldType();
091: break;
092: case Types.JAVA_OBJECT:
093: result = new JavaObjectFieldType();
094: break;
095: case Types.LONGVARBINARY:
096: result = new ByteArrayFieldType();
097: break;
098: case Types.LONGVARCHAR:
099: result = new StringFieldType();
100: break;
101: case Types.NUMERIC:
102: result = new BigDecimalFieldType();
103: break;
104: case Types.REAL:
105: result = new FloatFieldType();
106: break;
107: case Types.REF:
108: result = new RefFieldType();
109: break;
110: case Types.SMALLINT:
111: result = new ShortFieldType();
112: break;
113: case Types.STRUCT:
114: result = new StructFieldType();
115: break;
116: case Types.TIME:
117: result = new TimeFieldType();
118: break;
119: case Types.TIMESTAMP:
120: result = new TimestampFieldType();
121: break;
122: case Types.TINYINT:
123: result = new ByteFieldType();
124: break;
125: case Types.VARBINARY:
126: result = new ByteArrayFieldType();
127: break;
128: case Types.VARCHAR:
129: result = new StringFieldType();
130: break;
131: case Types.OTHER:
132: result = new JavaObjectFieldType();
133: break;
134: //
135: // case Types.NULL:
136: // result = new NullFieldType();
137: // break;
138:
139: //#ifdef JDBC30
140: case Types.BOOLEAN:
141: result = new BooleanFieldType();
142: break;
143: case Types.DATALINK:
144: result = new URLFieldType();
145: break;
146: //#endif
147: default:
148: throw new OJBRuntimeException(
149: "Unkown or not supported field type specified, specified jdbc type was '"
150: + jdbcType
151: + "', as string: "
152: + JdbcTypesHelper
153: .getSqlTypeAsString(jdbcType
154: .getType()));
155: }
156: // make sure that the sql type was set
157: result.setSqlType(jdbcType);
158: return result;
159: }
160:
161: /**
162: * Base class for all fields.
163: */
164: abstract static class BaseFieldType implements FieldType {
165: int sqlType;
166:
167: public void setSqlType(JdbcType jdbcType) {
168: sqlType = jdbcType.getType();
169: }
170:
171: public int getSqlType() {
172: return sqlType;
173: }
174:
175: /**
176: * Helper method to copy an object if possible.
177: *
178: * @param toCopy The object to copy.
179: * @return The copy of the object or <em>null</em> clone is not supported.
180: */
181: Object copyIfCloneable(Object toCopy) {
182: Object result = null;
183: if (toCopy instanceof Cloneable) {
184: try {
185: Method m = toCopy.getClass().getMethod("clone",
186: ArrayUtils.EMPTY_CLASS_ARRAY);
187: /*
188: arminw:
189: By definition the overrided object.clone() method has to be public
190: so we don't need to make it accessible
191: */
192: //m.setAccessible(true);
193: result = m.invoke(toCopy, null);
194: } catch (Exception e) {
195: throw new OJBRuntimeException(
196: "Can't invoke method 'clone' on object: "
197: + toCopy, e);
198: }
199: }
200: return result;
201: }
202:
203: /**
204: * Helper method to copy an object if possible.
205: *
206: * @param toCopy The object to copy.
207: * @return The copy of the object or <em>null</em> if serialization is not supported.
208: */
209: Object copyIfSerializeable(Object toCopy) {
210: Object result = null;
211: if (toCopy instanceof Serializable) {
212: result = SerializationUtils
213: .clone((Serializable) toCopy);
214: }
215: return result;
216: }
217:
218: public String toString() {
219: return new ToStringBuilder(this )
220: .append("sqlType", sqlType)
221: .append("sqlTypeAsString",
222: JdbcTypesHelper.getSqlTypeAsString(sqlType))
223: .append("isMutable", isMutable()).toString();
224: }
225: }
226:
227: /**
228: * Base class for all <em>immutable</em> types, like Number fields, Strings, ...
229: */
230: abstract static class ImmutableFieldType extends BaseFieldType {
231: public boolean isMutable() {
232: return false;
233: }
234:
235: public Object copy(Object source) {
236: return source;
237: }
238:
239: public boolean equals(Object firstValue, Object secondValue) {
240: return ObjectUtils.equals(firstValue, secondValue);
241: }
242: }
243:
244: /**
245: * Base class for all <em>mutable</em> fields.
246: */
247: abstract static class MutableFieldType extends BaseFieldType {
248: public boolean isMutable() {
249: return true;
250: }
251:
252: public boolean equals(Object firstValue, Object secondValue) {
253: return ObjectUtils.equals(firstValue, secondValue);
254: }
255: }
256:
257: /**
258: * Clob fields are logical pointer to DB, so for OJB it's immutable
259: * @see BlobFieldType
260: */
261: public static class ClobFieldType extends ImmutableFieldType {
262: }
263:
264: /**
265: * Blob fields are logical pointer to DB, so for OJB it's immutable.
266: * Snip of JDBC specification:
267: * "An application does not deal directly with the LOCATOR(blob) and
268: * LOCATOR(clob) types that are defined in SQL. By default, a JDBC
269: * driver should implement the Blob and Clob interfaces using the
270: * appropriate locator type. Also by default, Blob and Clob objects
271: * remain valid only during the transaction in which they are created."
272: */
273: public static class BlobFieldType extends ImmutableFieldType {
274: }
275:
276: /**
277: * Array fields are logical pointer to DB, so for OJB it's immutable.
278: * Snip of JDBC specification:
279: * "The Array object returned to an application by the ResultSet.getArray and
280: * CallableStatement.getArray methods is a logical pointer to the SQL ARRAY
281: * value in the database; it does not contain the contents of the SQL ARRAY value."
282: */
283: public static class ArrayFieldType extends ImmutableFieldType {
284: }
285:
286: /**
287: * Ref fields are logical pointer to DB, so for OJB it's immutable.
288: * Snip of JDBC specification:
289: * "An SQL REF value is a pointer; therefore, a Ref object, which is the mapping of a
290: * REF value, is likewise a pointer and does not contain the data of the structured type
291: * instance to which it refers."
292: */
293: public static class RefFieldType extends ImmutableFieldType {
294: }
295:
296: /**
297: * When using SQL UDT's it's possible that the jdbc-driver returns
298: * full materialized java objects defined by the user.
299: */
300: public static class StructFieldType extends MutableFieldType {
301: // TODO: does this make sense?? or Struct instances always Locator objects?
302: public Object copy(Object fieldValue) {
303: if (fieldValue == null)
304: return null;
305:
306: Object copy = copyIfCloneable(fieldValue);
307: if (copy == null) {
308: copy = copyIfSerializeable(fieldValue);
309: }
310: return copy == null ? fieldValue : copy;
311: }
312: }
313:
314: /**
315: * If a user-defined object is used, we can check if object is
316: * {@link Cloneable} or {@link Serializable} to copy the object.
317: * If not possible return the specified object instance.
318: */
319: public static class JavaObjectFieldType extends MutableFieldType {
320: public Object copy(Object fieldValue) {
321: if (fieldValue == null)
322: return null;
323:
324: Object copy = copyIfCloneable(fieldValue);
325: if (copy == null) {
326: copy = copyIfSerializeable(fieldValue);
327: }
328: return copy == null ? fieldValue : copy;
329: }
330: }
331:
332: public static class ByteArrayFieldType extends MutableFieldType {
333: public Object copy(Object fieldValue) {
334: byte[] result = null;
335: if (fieldValue != null) {
336: byte[] source = (byte[]) fieldValue;
337: int length = source.length;
338: result = new byte[length];
339: System.arraycopy(fieldValue, 0, result, 0, length);
340: }
341: return result;
342: }
343:
344: public boolean equals(Object firstValue, Object secondValue) {
345: return Arrays.equals((byte[]) firstValue,
346: (byte[]) secondValue);
347: }
348: }
349:
350: public static class DateFieldType extends MutableFieldType {
351: public Object copy(Object fieldValue) {
352: Date source = (Date) fieldValue;
353: return source != null ? new Date(source.getTime()) : null;
354: }
355: }
356:
357: public static class TimeFieldType extends MutableFieldType {
358: public Object copy(Object fieldValue) {
359: Time source = (Time) fieldValue;
360: return source != null ? new Time(source.getTime()) : null;
361: }
362: }
363:
364: public static class TimestampFieldType extends MutableFieldType {
365: public Object copy(Object fieldValue) {
366: Timestamp result = null;
367: if (fieldValue != null) {
368: Timestamp source = (Timestamp) fieldValue;
369: result = (Timestamp) source.clone();
370: }
371: return result;
372: }
373: }
374:
375: public static class StringFieldType extends ImmutableFieldType {
376: }
377:
378: public static class BigDecimalFieldType extends ImmutableFieldType {
379: }
380:
381: public static class BooleanFieldType extends ImmutableFieldType {
382: }
383:
384: public static class ByteFieldType extends ImmutableFieldType {
385: }
386:
387: public static class ShortFieldType extends ImmutableFieldType {
388:
389: }
390:
391: public static class IntegerFieldType extends ImmutableFieldType {
392:
393: }
394:
395: public static class LongFieldType extends ImmutableFieldType {
396:
397: }
398:
399: public static class FloatFieldType extends ImmutableFieldType {
400:
401: }
402:
403: public static class DoubleFieldType extends ImmutableFieldType {
404:
405: }
406:
407: public static class URLFieldType extends ImmutableFieldType {
408:
409: }
410: }
|