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:
019: import java.sql.PreparedStatement;
020: import java.sql.SQLException;
021: import java.sql.ResultSet;
022: import java.util.HashMap;
023:
024: import javax.jdo.JDOFatalDataStoreException; //todo: appears only in throws-clause
025:
026: import com.versant.core.common.BindingSupportImpl;
027:
028: /**
029: * This is a base class for converters that convert a type to/from byte[] and
030: * use a nested converter to do the actual JDBC work with the byte[].
031: * @keep-all
032: */
033: public abstract class TypeAsBytesConverterBase implements JdbcConverter {
034:
035: /**
036: * Subclasses must extend this and provide their class to the superclass
037: * constructor.
038: */
039: public static abstract class Factory extends
040: NoArgJdbcConverterFactory {
041:
042: private HashMap instanceMap = new HashMap(17);
043:
044: /**
045: * Create a converter for col using props as parameters. Return null if
046: * no converter is required.
047: */
048: public JdbcConverter createJdbcConverter(JdbcColumn col,
049: Object args, JdbcTypeRegistry jdbcTypeRegistry) {
050: JdbcConverterFactory f = jdbcTypeRegistry
051: .getJdbcConverterFactory(col.jdbcType);
052: JdbcConverter c = f.createJdbcConverter(col, args,
053: jdbcTypeRegistry);
054: if (c.getValueType() != byte[].class) {
055: throw BindingSupportImpl.getInstance().illegalArgument(
056: "Invalid JDBC type: "
057: + JdbcTypes.toString(col.jdbcType));
058: }
059: JdbcConverter ans = (JdbcConverter) instanceMap.get(c);
060: if (ans == null) {
061: instanceMap.put(c, ans = createConverter(c));
062: }
063: return ans;
064: }
065:
066: /**
067: * Create an instance of our converter.
068: */
069: protected abstract JdbcConverter createConverter(
070: JdbcConverter nested);
071:
072: }
073:
074: protected final JdbcConverter nested;
075:
076: public TypeAsBytesConverterBase(JdbcConverter nested) {
077: this .nested = nested;
078: }
079:
080: /**
081: * Convert a byte[] into an instance of our value class.
082: */
083: protected abstract Object fromByteArray(byte[] buf);
084:
085: /**
086: * Convert an instance of our value class into a byte[].
087: */
088: protected abstract byte[] toByteArray(Object value);
089:
090: /**
091: * Get the type of our expected value objects (e.g. java.util.Locale
092: * for a converter for Locale's).
093: */
094: public abstract Class getValueType();
095:
096: /**
097: * Is this converter for an Oracle style LOB column? Oracle LOBs require
098: * a hard coded a value into the insert/update statement instead of using
099: * a replaceable parameter and then select the value (if not null) and
100: * modify it.
101: */
102: public boolean isOracleStyleLOB() {
103: return nested.isOracleStyleLOB();
104: }
105:
106: /**
107: * This is only called if isOracleStyleLOB returns true. Get the String
108: * to be embedded in an SQL insert/update statement when the value for
109: * this column is not null (e.g. "empty_clob()");
110: */
111: public String getOracleStyleLOBNotNullString() {
112: return nested.getOracleStyleLOBNotNullString();
113: }
114:
115: /**
116: * Get the value of col from rs at position index.
117: * @exception SQLException on SQL errors
118: * @exception JDOFatalDataStoreException if the ResultSet value is invalid
119: */
120: public Object get(ResultSet rs, int index, JdbcColumn col)
121: throws SQLException, JDOFatalDataStoreException {
122: byte[] buf = (byte[]) nested.get(rs, index, col);
123: if (buf == null)
124: return null;
125: return fromByteArray(buf);
126: }
127:
128: /**
129: * Set parameter index on ps to value (for col).
130: * @exception SQLException on SQL errors
131: * @exception JDOFatalDataStoreException if value is invalid
132: */
133: public void set(PreparedStatement ps, int index, JdbcColumn col,
134: Object value) throws SQLException,
135: JDOFatalDataStoreException {
136: nested.set(ps, index, col, toByteArray(value));
137: }
138:
139: /**
140: * Set parameter index on ps to value (for col). This special form is used
141: * when the value to be set is available as an int to avoid creating
142: * an wrapper instance.
143: * @exception SQLException on SQL errors
144: * @exception JDOFatalDataStoreException if value is invalid
145: */
146: public void set(PreparedStatement ps, int index, JdbcColumn col,
147: int value) throws SQLException, JDOFatalDataStoreException {
148: throw BindingSupportImpl.getInstance().fatalDatastore(
149: "set(..int) called");
150: }
151:
152: /**
153: * This method is called for converters that return true for
154: * isOracleStyleLOB. The value at index in rs will contain the LOB to
155: * be updated.
156: * @exception SQLException on SQL errors
157: * @exception JDOFatalDataStoreException if value is invalid
158: */
159: public void set(ResultSet rs, int index, JdbcColumn col,
160: Object value) throws SQLException,
161: JDOFatalDataStoreException {
162: nested.set(rs, index, col, toByteArray(value));
163: }
164:
165: }
|