001: /*
002: * Copyright (C) 2007 Rob Manning
003: * manningr@users.sourceforge.net
004: *
005: * This library is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU Lesser General Public
007: * License as published by the Free Software Foundation; either
008: * version 2.1 of the License, or (at your option) any later version.
009: *
010: * This library is distributed in the hope that it will be useful,
011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * Lesser General Public License for more details.
014: *
015: * You should have received a copy of the GNU Lesser General Public
016: * License along with this library; if not, write to the Free Software
017: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
018: */
019: package net.sourceforge.squirrel_sql.plugins.oracle.types;
020:
021: import java.lang.reflect.Method;
022: import java.sql.Connection;
023: import java.sql.PreparedStatement;
024: import java.sql.ResultSet;
025: import java.sql.SQLException;
026:
027: import net.sourceforge.squirrel_sql.fw.datasetviewer.cellcomponent.BaseDataTypeComponent;
028: import net.sourceforge.squirrel_sql.fw.datasetviewer.cellcomponent.IDataTypeComponent;
029: import net.sourceforge.squirrel_sql.fw.sql.ISQLDatabaseMetaData;
030: import net.sourceforge.squirrel_sql.fw.util.StringManager;
031: import net.sourceforge.squirrel_sql.fw.util.StringManagerFactory;
032: import net.sourceforge.squirrel_sql.fw.util.log.ILogger;
033: import net.sourceforge.squirrel_sql.fw.util.log.LoggerController;
034:
035: /**
036: * A custom DatatType implementation of IDataTypeComponent that can handle
037: * Oracle's SYS.XMLTYPE (DataType value of 2007). This requires that the XDK
038: * (XML Developer Kit) be downloaded from Oracle and the jars from this kit
039: * included along with the driver in "Extra ClassPath".
040: *
041: * @author manningr
042: */
043: public class OracleXmlTypeDataTypeComponent extends
044: BaseDataTypeComponent implements IDataTypeComponent {
045:
046: /**
047: * The fully-qualified name of Oracle's utility class that will convert
048: * betweem XMLType and String for us.
049: */
050: private static final String XML_TYPE_CLASSNAME = "oracle.xdb.XMLType";
051:
052: /**
053: * When we lookup the class def for oracle.xdb.XMLType, save a copy of it
054: * so that we don't have to look it up again.
055: */
056: private static Class<?> XML_TYPE_CLASS = null;
057:
058: /** Logger for this class. */
059: private static ILogger s_log = LoggerController
060: .createLogger(OracleXmlTypeDataTypeComponent.class);
061:
062: /**
063: * Internationalized strings for this class.
064: */
065: private static final StringManager s_stringMgr = StringManagerFactory
066: .getStringManager(OracleXmlTypeDataTypeComponent.class);
067:
068: /**
069: * I18n messages
070: */
071: static interface i18n {
072: //i18n[OracleXmlTypeDataTypeComponent.cellErrorMsg=<Error: see log file>]
073: String CELL_ERROR_MSG = s_stringMgr
074: .getString("OracleXmlTypeDataTypeComponent.cellErrorMsg");
075: }
076:
077: /* IDataTypeComponent interface methods
078:
079: /**
080: * @see net.sourceforge.squirrel_sql.fw.datasetviewer.cellcomponent.IDataTypeComponent#canDoFileIO()
081: */
082: public boolean canDoFileIO() {
083: return true;
084: }
085:
086: /**
087: * @see net.sourceforge.squirrel_sql.fw.datasetviewer.cellcomponent.IDataTypeComponent#getClassName()
088: */
089: public String getClassName() {
090: return "java.lang.String";
091: }
092:
093: /**
094: * @see net.sourceforge.squirrel_sql.fw.datasetviewer.cellcomponent.IDataTypeComponent#getDefaultValue(java.lang.String)
095: */
096: public Object getDefaultValue(String dbDefaultValue) {
097: // At the moment, no default value
098: if (s_log.isInfoEnabled()) {
099: s_log.info("getDefaultValue: not yet implemented");
100: }
101: return null;
102: }
103:
104: /**
105: * @see net.sourceforge.squirrel_sql.fw.datasetviewer.cellcomponent.IDataTypeComponent#getWhereClauseValue(java.lang.Object, net.sourceforge.squirrel_sql.fw.sql.ISQLDatabaseMetaData)
106: */
107: public String getWhereClauseValue(Object value,
108: ISQLDatabaseMetaData md) {
109: /*
110: * For Oracle 10g we could say something like :
111: *
112: * "where XMLSERIALIZE(CONTENT " + _colDef.getLabel() +") like '<value>'"
113: *
114: * This doesn't appear to work on Oracle 9i at the moment, so we will
115: * avoid using this column in any where clause if the value is non-null.
116: *
117: * TODO: Find a way to do this for both versions or split this behavior
118: * so that it works on 10g and is disabled on 9i.
119: */
120: if (value == null || value.toString() == null) {
121: return _colDef.getLabel() + " IS NULL";
122: } else {
123: return "";
124: }
125: }
126:
127: /**
128: * This Data Type can be edited in a table cell as long as there are no
129: * issues using the XDK to display the data. If we should encounter
130: * Exceptions using XDK, then we should prevent the user from editing the
131: * cell (our error message is not meant to be valid XML data; further, we
132: * don't want to let the user whack their data with our tool accidentally)
133: *
134: * @see net.sourceforge.squirrel_sql.fw.datasetviewer.cellcomponent.IDataTypeComponent#isEditableInCell(java.lang.Object)
135: */
136: public boolean isEditableInCell(Object originalValue) {
137: return !i18n.CELL_ERROR_MSG.equals(originalValue);
138: }
139:
140: /**
141: * This Data Type can be edited in a popup as long as there are no
142: * issues using the XDK to display the data. If we should encounter
143: * Exceptions using XDK, then we should prevent the user from editing the
144: * cell (our error message is not meant to be valid XML data; further, we
145: * don't want to let the user whack their data with our tool accidentally)
146: *
147: * @see net.sourceforge.squirrel_sql.fw.datasetviewer.cellcomponent.IDataTypeComponent#isEditableInPopup(java.lang.Object)
148: */
149: public boolean isEditableInPopup(Object originalValue) {
150: return !i18n.CELL_ERROR_MSG.equals(originalValue);
151: }
152:
153: /**
154: * @see net.sourceforge.squirrel_sql.fw.datasetviewer.cellcomponent.IDataTypeComponent#needToReRead(java.lang.Object)
155: */
156: public boolean needToReRead(Object originalValue) {
157: return false;
158: }
159:
160: /**
161: * This class relies on reflection to get a handle to Oracle's XMLType which
162: * is made available separately from the JDBC driver, so we cannot just
163: * assume the user will always have, nor can we depend on it to compile
164: * SQuirreL code. So we remove this dependency here by using reflection
165: * which doesn't require this library in order to just compile the code.
166: *
167: * @see net.sourceforge.squirrel_sql.fw.datasetviewer.cellcomponent.IDataTypeComponent#readResultSet(java.sql.ResultSet, int, boolean)
168: */
169: public Object readResultSet(ResultSet rs, int idx,
170: boolean limitDataRead) throws SQLException {
171: Object result = null;
172: try {
173: Object o = rs.getObject(idx);
174: if (o == null) {
175: return NULL_VALUE_PATTERN;
176: } else if ("oracle.sql.OPAQUE".equals(o.getClass()
177: .getName())) {
178: XML_TYPE_CLASS = Class.forName(XML_TYPE_CLASSNAME);
179: Method createXmlMethod = XML_TYPE_CLASS.getMethod(
180: "createXml", o.getClass());
181:
182: // Below is equivalent to the following:
183: // xmlType = XMLType.createXML(o);
184: Object xmlTypeObj = createXmlMethod.invoke(null, o);
185: Method getStringValMethod = XML_TYPE_CLASS.getMethod(
186: "getStringVal", (Class[]) null);
187:
188: // Below is equivalent to the following:
189: // stringValueResult = xmlType.getStringVal();
190: Object stringValueResult = getStringValMethod.invoke(
191: xmlTypeObj, (Object[]) null);
192: result = stringValueResult;
193:
194: } else if (XML_TYPE_CLASSNAME
195: .equals(o.getClass().getName())) {
196: XML_TYPE_CLASS = o.getClass();
197: Method getStringValMethod = XML_TYPE_CLASS.getMethod(
198: "getStringVal", (Class[]) null);
199:
200: // Below is equivalent to the following:
201: // stringValueResult = xmlType.getStringVal();
202: Object stringValueResult = getStringValMethod.invoke(o,
203: (Object[]) null);
204: result = stringValueResult;
205: } else {
206: result = o;
207: }
208: } catch (ClassNotFoundException e) {
209: s_log.error("Perhaps the XDK which contains the class "
210: + XML_TYPE_CLASSNAME + " is not in the CLASSPATH?",
211: e);
212: } catch (Exception e) {
213: s_log.error(
214: "Unexpected exception while attempting to read "
215: + "SYS.XMLType column", e);
216: }
217: if (result == null) {
218: result = i18n.CELL_ERROR_MSG;
219: }
220: return result;
221: }
222:
223: /**
224: * @see net.sourceforge.squirrel_sql.fw.datasetviewer.cellcomponent.IDataTypeComponent#setPreparedStatementValue(java.sql.PreparedStatement, java.lang.Object, int)
225: */
226: public void setPreparedStatementValue(PreparedStatement pstmt,
227: Object value, int position) throws SQLException {
228: if (value == null) {
229: // Throws an exception claiming that 2007 isn't a valid type - go
230: // figure.
231: //pstmt.setNull(position, _colDef.getSqlType());
232:
233: // Both of these throw an exception claiming that it got a clob
234: // and expected a number (inconsistent data types):
235: //
236: //pstmt.setClob(position, null);
237: //pstmt.setNull(position, java.sql.Types.CLOB);
238: //
239:
240: // This seems to work for both Oracle 9i and 10g
241: pstmt.setObject(position, null);
242: } else {
243: try {
244: Class<?>[] args = new Class[] { Connection.class,
245: String.class };
246:
247: Method createXmlMethod = XML_TYPE_CLASS.getMethod(
248: "createXML", args);
249: Object xmlTypeObj = createXmlMethod.invoke(null, pstmt
250: .getConnection(), value.toString());
251:
252: // now bind the string..
253: pstmt.setObject(position, xmlTypeObj);
254: } catch (Exception e) {
255: s_log.error(
256: "setPreparedStatementValue: Unexpected exception - "
257: + e.getMessage(), e);
258: }
259:
260: }
261: }
262:
263: /**
264: * @see net.sourceforge.squirrel_sql.fw.datasetviewer.cellcomponent.IDataTypeComponent#useBinaryEditingPanel()
265: */
266: public boolean useBinaryEditingPanel() {
267: return false;
268: }
269:
270: /**
271: * @see net.sourceforge.squirrel_sql.fw.datasetviewer.cellcomponent.IDataTypeComponent#areEqual(java.lang.Object, java.lang.Object)
272: */
273: public boolean areEqual(Object obj1, Object obj2) {
274: return ((String) obj1).equals(obj2);
275: }
276:
277: }
|