001: /*
002: * Copyright (c) 1998 - 2005 Versant Corporation
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * Versant Corporation - initial API and implementation
010: */
011: package com.versant.core.jdbc.sql.conv;
012:
013: import com.versant.core.jdbc.JdbcConverter;
014: import com.versant.core.jdbc.JdbcConverterFactory;
015: import com.versant.core.jdbc.JdbcTypeRegistry;
016: import com.versant.core.jdbc.metadata.JdbcColumn;
017: import com.versant.core.jdbc.metadata.JdbcTypes;
018: import com.versant.core.common.Utils;
019:
020: import java.sql.PreparedStatement;
021: import java.sql.SQLException;
022: import java.sql.ResultSet;
023: import java.lang.reflect.Constructor;
024: import java.lang.reflect.Method;
025:
026: import javax.jdo.JDOFatalDataStoreException; //todo: appears only in throws-clause
027:
028: import com.versant.core.common.BindingSupportImpl;
029: import com.versant.core.common.Utils;
030:
031: /**
032: * This converter can convert any type to/from byte[]. The type must have
033: * a constructor that accepts a byte[] and a method called toBytes that
034: * will provide a byte[].
035: */
036: public class TypeAsBytesConverter implements JdbcConverter {
037:
038: private Class type;
039: private Constructor constructor;
040: private Method toBytes;
041: private JdbcConverter bytesConverter;
042:
043: /**
044: * This creates converters for different types.
045: */
046: public static class Factory extends NoArgJdbcConverterFactory {
047:
048: /**
049: * Create a converter for col using props as parameters. Return null if
050: * no converter is required.
051: */
052: public JdbcConverter createJdbcConverter(JdbcColumn col,
053: Object args, JdbcTypeRegistry jdbcTypeRegistry) {
054: Class type = col.javaType;
055: JdbcConverterFactory f = jdbcTypeRegistry
056: .getJdbcConverterFactory(col.jdbcType);
057: JdbcConverter bytesConverter = f.createJdbcConverter(col,
058: args, jdbcTypeRegistry);
059: if (bytesConverter.getValueType() != byte[].class) {
060: throw BindingSupportImpl.getInstance().illegalArgument(
061: "Invalid JDBC type: "
062: + JdbcTypes.toString(col.jdbcType));
063: }
064: return new TypeAsBytesConverter(type, bytesConverter);
065: }
066:
067: }
068:
069: public TypeAsBytesConverter(Class type,
070: JdbcConverter stringConverter) {
071: this .type = type;
072: this .bytesConverter = stringConverter;
073: try {
074: constructor = type
075: .getConstructor(new Class[] { byte[].class });
076: } catch (NoSuchMethodException e) {
077: throw BindingSupportImpl.getInstance().runtime(
078: type + " does not have a "
079: + "constructor that accepts a byte[]", e);
080: }
081: try {
082: toBytes = type.getMethod("toBytes", null);
083: } catch (NoSuchMethodException e) {
084: throw BindingSupportImpl.getInstance().runtime(
085: type + " does not have a "
086: + "public toBytes() method", e);
087: }
088: if (toBytes.getReturnType() != byte[].class) {
089: throw BindingSupportImpl.getInstance().runtime(
090: type + ".toBytes() does not " + "return byte[]");
091: }
092: }
093:
094: /**
095: * Get the value of col from rs at position index.
096: */
097: public Object get(ResultSet rs, int index, JdbcColumn col)
098: throws SQLException, JDOFatalDataStoreException {
099: byte[] a = (byte[]) bytesConverter.get(rs, index, col);
100: if (a == null)
101: return null;
102: try {
103: return constructor.newInstance(new Object[] { a });
104: } catch (Throwable x) {
105: throw BindingSupportImpl.getInstance().fatalDatastore(
106: "Unable to create instance of " + type.getName()
107: + " from " + Utils.toString(a) + ": " + x,
108: x);
109: }
110: }
111:
112: private byte[] toBytes(Object value) {
113: byte[] a;
114: if (value == null) {
115: a = null;
116: } else {
117: try {
118: a = (byte[]) toBytes.invoke(value, null);
119: } catch (Throwable x) {
120: throw BindingSupportImpl.getInstance().fatalDatastore(
121: "Unable to convert instance of "
122: + type.getName() + " '"
123: + Utils.toString(value)
124: + "' to byte[]: " + x, x);
125: }
126: }
127: return a;
128: }
129:
130: /**
131: * Set parameter index on ps to value (for col).
132: */
133: public void set(PreparedStatement ps, int index, JdbcColumn col,
134: Object value) throws SQLException,
135: JDOFatalDataStoreException {
136: bytesConverter.set(ps, index, col, toBytes(value));
137: }
138:
139: /**
140: * Get the type of our expected value objects (e.g. java.util.Locale
141: * for a converter for Locale's).
142: */
143: public Class getValueType() {
144: return type;
145: }
146:
147: /**
148: * Is this converter for an Oracle style LOB column? Oracle LOBs require
149: * a hard coded a value into the insert/update statement instead of using
150: * a replaceable parameter and then select the value (if not null) and
151: * modify it.
152: */
153: public boolean isOracleStyleLOB() {
154: return bytesConverter.isOracleStyleLOB();
155: }
156:
157: /**
158: * This is only called if isOracleStyleLOB returns true. Get the String
159: * to be embedded in an SQL insert/update statement when the value for
160: * this column is not null (e.g. "empty_clob()");
161: */
162: public String getOracleStyleLOBNotNullString() {
163: return bytesConverter.getOracleStyleLOBNotNullString();
164: }
165:
166: /**
167: * Set parameter index on ps to value (for col). This special form is used
168: * when the value to be set is available as an int to avoid creating
169: * an wrapper instance.
170: *
171: * @throws java.sql.SQLException on SQL errors
172: * @throws javax.jdo.JDOFatalDataStoreException
173: * if value is invalid
174: */
175: public void set(PreparedStatement ps, int index, JdbcColumn col,
176: int value) throws SQLException, JDOFatalDataStoreException {
177: throw BindingSupportImpl.getInstance().fatalDatastore(
178: "set(..int) called");
179: }
180:
181: /**
182: * This method is only called for converters that return true for
183: * isOracleStyleLOB. The LOB to be updated is at index in rs.
184: *
185: * @throws java.sql.SQLException on SQL errors
186: * @throws javax.jdo.JDOFatalDataStoreException
187: * if value is invalid
188: * @see #isOracleStyleLOB
189: */
190: public void set(ResultSet rs, int index, JdbcColumn col,
191: Object value) throws SQLException,
192: JDOFatalDataStoreException {
193: bytesConverter.set(rs, index, col, toBytes(value));
194: }
195:
196: }
|