From: James Ahlborn Date: Sun, 17 Jan 2021 23:32:53 +0000 (+0000) Subject: isolate java.sql usage so that the module is not required for core jackcess usage X-Git-Tag: jackcess-4.0.0~3^2~5 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=23f397f625ca3b0412005641d1ae2c00a6863e53;p=jackcess.git isolate java.sql usage so that the module is not required for core jackcess usage git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/branches/modules@1350 f203690c-595d-4dc9-a70b-905162fa7fd2 --- diff --git a/src/main/java/com/healthmarketscience/jackcess/Column.java b/src/main/java/com/healthmarketscience/jackcess/Column.java index 333f977..d05fb2a 100644 --- a/src/main/java/com/healthmarketscience/jackcess/Column.java +++ b/src/main/java/com/healthmarketscience/jackcess/Column.java @@ -17,7 +17,6 @@ limitations under the License. package com.healthmarketscience.jackcess; import java.io.IOException; -import java.sql.SQLException; import java.util.Map; import com.healthmarketscience.jackcess.complex.ComplexColumnInfo; @@ -34,7 +33,7 @@ import com.healthmarketscience.jackcess.util.ColumnValidator; * @author James Ahlborn * @usage _general_class_ */ -public interface Column +public interface Column { /** * Meaningless placeholder object for inserting values in an autonumber @@ -43,14 +42,14 @@ public interface Column * @usage _general_field_ */ public static final Object AUTO_NUMBER = ""; - + /** * Meaningless placeholder object for updating rows which indicates that a * given column should keep its existing value. * @usage _general_field_ */ public static final Object KEEP_VALUE = ""; - + /** * @usage _general_method_ */ @@ -89,7 +88,7 @@ public interface Column /** * @usage _general_method_ */ - public int getSQLType() throws SQLException; + public int getSQLType() throws IOException; /** * @usage _general_method_ @@ -149,7 +148,7 @@ public interface Column * @usage _general_method_ */ public PropertyMap getProperties() throws IOException; - + /** * Returns the column which tracks the version history for an "append only" * column. @@ -162,7 +161,7 @@ public interface Column * @usage _intermediate_method_ */ public ColumnValidator getColumnValidator(); - + /** * Sets a new ColumnValidator. If {@code null}, resets to the value * returned from the Database's ColumnValidatorFactory (if the factory @@ -173,12 +172,12 @@ public interface Column * @usage _intermediate_method_ */ public void setColumnValidator(ColumnValidator newValidator); - + public Object setRowValue(Object[] rowArray, Object value); - + public Object setRowValue(Map rowMap, Object value); - + public Object getRowValue(Object[] rowArray); - + public Object getRowValue(Map rowMap); } diff --git a/src/main/java/com/healthmarketscience/jackcess/ColumnBuilder.java b/src/main/java/com/healthmarketscience/jackcess/ColumnBuilder.java index 48fa1cf..e063448 100644 --- a/src/main/java/com/healthmarketscience/jackcess/ColumnBuilder.java +++ b/src/main/java/com/healthmarketscience/jackcess/ColumnBuilder.java @@ -17,7 +17,6 @@ limitations under the License. package com.healthmarketscience.jackcess; import java.io.IOException; -import java.sql.SQLException; import java.util.HashMap; import java.util.Map; @@ -93,7 +92,7 @@ public class ColumnBuilder { /** * Sets the type for the new column based on the given SQL type. */ - public ColumnBuilder setSQLType(int type) throws SQLException { + public ColumnBuilder setSQLType(int type) throws IOException { return setSQLType(type, 0, null); } @@ -102,7 +101,7 @@ public class ColumnBuilder { * data length (in type specific units). */ public ColumnBuilder setSQLType(int type, int lengthInUnits) - throws SQLException + throws IOException { return setSQLType(type, lengthInUnits, null); } @@ -113,7 +112,7 @@ public class ColumnBuilder { */ public ColumnBuilder setSQLType(int type, int lengthInUnits, Database.FileFormat fileFormat) - throws SQLException + throws IOException { return setType(DataType.fromSQLType(type, lengthInUnits, fileFormat)); } diff --git a/src/main/java/com/healthmarketscience/jackcess/DataType.java b/src/main/java/com/healthmarketscience/jackcess/DataType.java index 6070dbd..d191dc1 100644 --- a/src/main/java/com/healthmarketscience/jackcess/DataType.java +++ b/src/main/java/com/healthmarketscience/jackcess/DataType.java @@ -19,7 +19,6 @@ package com.healthmarketscience.jackcess; import java.io.IOException; import java.math.BigDecimal; import java.math.BigInteger; -import java.sql.SQLException; import java.sql.Types; import java.util.Date; import java.util.HashMap; @@ -28,6 +27,7 @@ import java.time.LocalDateTime; import com.healthmarketscience.jackcess.impl.DatabaseImpl; import com.healthmarketscience.jackcess.impl.JetFormat; +import com.healthmarketscience.jackcess.impl.SqlHelper; /** * Supported access data types. @@ -356,11 +356,11 @@ public enum DataType { return _maxSize; } - public int getSQLType() throws SQLException { + public int getSQLType() throws IOException { if (_sqlType != null) { return _sqlType; } - throw new SQLException("Unsupported data type: " + toString()); + throw new JackcessException("Unsupported data type: " + toString()); } public int getMinScale() { @@ -472,24 +472,24 @@ public enum DataType { } public static DataType fromSQLType(int sqlType) - throws SQLException + throws IOException { return fromSQLType(sqlType, 0, null); } public static DataType fromSQLType(int sqlType, int lengthInUnits) - throws SQLException + throws IOException { return fromSQLType(sqlType, lengthInUnits, null); } public static DataType fromSQLType(int sqlType, int lengthInUnits, Database.FileFormat fileFormat) - throws SQLException + throws IOException { DataType[] rtnArr = SQL_TYPES.get(sqlType); if(rtnArr == null) { - throw new SQLException("Unsupported SQL type: " + sqlType); + throw new JackcessException("Unsupported SQL type: " + sqlType); } JetFormat format = ((fileFormat != null) ? @@ -533,8 +533,7 @@ public enum DataType { DataType altType) { try { - java.lang.reflect.Field sqlTypeField = Types.class.getField(typeName); - Integer value = (Integer)sqlTypeField.get(null); + Integer value = SqlHelper.INSTANCE.getNewSqlType(typeName); SQL_TYPES.put(value, new DataType[]{type}); if(altType != null) { ALT_SQL_TYPES.put(value, altType); diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/ColumnImpl.java b/src/main/java/com/healthmarketscience/jackcess/impl/ColumnImpl.java index 07c798b..de4d8e7 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/ColumnImpl.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/ColumnImpl.java @@ -29,9 +29,6 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.CharBuffer; import java.nio.charset.Charset; -import java.sql.Blob; -import java.sql.Clob; -import java.sql.SQLException; import java.time.DateTimeException; import java.time.Duration; import java.time.Instant; @@ -441,7 +438,7 @@ public class ColumnImpl implements Column, Comparable, DateTimeConte } @Override - public int getSQLType() throws SQLException { + public int getSQLType() throws IOException { return _type.getSQLType(); } @@ -1836,14 +1833,8 @@ public class ColumnImpl implements Column, Comparable, DateTimeConte return null; } else if(value instanceof CharSequence) { return (CharSequence)value; - } else if(value instanceof Clob) { - try { - Clob c = (Clob)value; - // note, start pos is 1-based - return c.getSubString(1L, (int)c.length()); - } catch(SQLException e) { - throw (IOException)(new IOException(e.getMessage())).initCause(e); - } + } else if(SqlHelper.INSTANCE.isClob(value)) { + return SqlHelper.INSTANCE.getClobString(value); } else if(value instanceof Reader) { char[] buf = new char[8 * 1024]; StringBuilder sout = new StringBuilder(); @@ -1869,18 +1860,10 @@ public class ColumnImpl implements Column, Comparable, DateTimeConte return null; } else if(value instanceof byte[]) { return (byte[])value; - } else if(value instanceof OleUtil.OleBlobImpl) { - return ((OleUtil.OleBlobImpl)value).getBytes(); - } else if(value instanceof Blob) { - try { - Blob b = (Blob)value; - // note, start pos is 1-based - return b.getBytes(1L, (int)b.length()); - } catch(SQLException e) { - throw (IOException)(new IOException(e.getMessage())).initCause(e); - } - } else if(value instanceof RawData) { - return ((RawData)value).getBytes(); + } else if(value instanceof InMemoryBlob) { + return ((InMemoryBlob)value).getBytes(); + } else if(SqlHelper.INSTANCE.isBlob(value)) { + return SqlHelper.INSTANCE.getBlobBytes(value); } ByteArrayOutputStream bout = new ByteArrayOutputStream(); @@ -2302,7 +2285,7 @@ public class ColumnImpl implements Column, Comparable, DateTimeConte /** * Wrapper for raw column data which can be re-written. */ - private static class RawData implements Serializable + private static final class RawData implements Serializable, InMemoryBlob { private static final long serialVersionUID = 0L; @@ -2312,7 +2295,8 @@ public class ColumnImpl implements Column, Comparable, DateTimeConte _bytes = bytes; } - private byte[] getBytes() { + @Override + public byte[] getBytes() { return _bytes; } @@ -2788,4 +2772,9 @@ public class ColumnImpl implements Column, Comparable, DateTimeConte return LocalDateTime.ofInstant(inst, db.getZoneId()); } } + + /** internal interface for types which hold bytes in memory */ + static interface InMemoryBlob { + public byte[] getBytes() throws IOException; + } } diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/OleUtil.java b/src/main/java/com/healthmarketscience/jackcess/impl/OleUtil.java index a67a54d..7708b11 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/OleUtil.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/OleUtil.java @@ -463,7 +463,7 @@ public class OleUtil } - static final class OleBlobImpl implements OleBlob + static final class OleBlobImpl implements OleBlob, ColumnImpl.InMemoryBlob { private byte[] _bytes; private ContentImpl _content; @@ -502,6 +502,7 @@ public class OleUtil return _bytes.length; } + @Override public byte[] getBytes() throws IOException { if(_bytes == null) { throw new IOException("blob is closed"); diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/SqlHelper.java b/src/main/java/com/healthmarketscience/jackcess/impl/SqlHelper.java new file mode 100644 index 0000000..91fc4b6 --- /dev/null +++ b/src/main/java/com/healthmarketscience/jackcess/impl/SqlHelper.java @@ -0,0 +1,71 @@ +/* +Copyright (c) 2021 James Ahlborn + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package com.healthmarketscience.jackcess.impl; + +import java.io.IOException; + +/** + * Helper class to isolate the java.sql module interactions from the core of + * jackcess (in java 9+ environments). If the module is enabled (indicating + * that the application is already using sql constructs), then jackcess will + * seamlessly interact with sql types. If the module is not enabled + * (indicating that the application is not using any sql constructs), then + * jackcess will not require the module in order to function otherwise + * normally. + * + * This base class is the "fallback" class if the java.sql module is not + * available. + * + * @author James Ahlborn + */ +public class SqlHelper { + + public static final SqlHelper INSTANCE = loadInstance(); + + public SqlHelper() {} + + public boolean isBlob(Object value) { + return false; + } + + public byte[] getBlobBytes(Object value) throws IOException { + throw new UnsupportedOperationException(); + } + + public boolean isClob(Object value) { + return false; + } + + public CharSequence getClobString(Object value) throws IOException { + throw new UnsupportedOperationException(); + } + + public Integer getNewSqlType(String typeName) throws Exception { + throw new UnsupportedOperationException(); + } + + private static final SqlHelper loadInstance() { + // attempt to load the implementation of this class which works with + // java.sql classes. if that fails, use this fallback instance instead. + try { + return (SqlHelper) + Class.forName("com.healthmarketscience.jackcess.impl.SqlHelperImpl") + .newInstance(); + } catch(Throwable ignored) {} + return new SqlHelper(); + } +} diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/SqlHelperImpl.java b/src/main/java/com/healthmarketscience/jackcess/impl/SqlHelperImpl.java new file mode 100644 index 0000000..142fb1f --- /dev/null +++ b/src/main/java/com/healthmarketscience/jackcess/impl/SqlHelperImpl.java @@ -0,0 +1,73 @@ +/* +Copyright (c) 2021 James Ahlborn + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package com.healthmarketscience.jackcess.impl; + +import java.io.IOException; +import java.sql.Blob; +import java.sql.Clob; +import java.sql.SQLException; +import java.sql.Types; + +/** + * Implementation of SqlHelperImpl which works with the java.sql modules + * classes. This class is used if the java.sql module is enabled in the + * application. + * + * @author James Ahlborn + */ +public class SqlHelperImpl extends SqlHelper { + + public SqlHelperImpl() {} + + @Override + public boolean isBlob(Object value) { + return (value instanceof Blob); + } + + @Override + public byte[] getBlobBytes(Object value) throws IOException { + try { + Blob b = (Blob)value; + // note, start pos is 1-based + return b.getBytes(1L, (int)b.length()); + } catch(SQLException e) { + throw (IOException)(new IOException(e.getMessage())).initCause(e); + } + } + + @Override + public boolean isClob(Object value) { + return (value instanceof Clob); + } + + @Override + public CharSequence getClobString(Object value) throws IOException { + try { + Clob c = (Clob)value; + // note, start pos is 1-based + return c.getSubString(1L, (int)c.length()); + } catch(SQLException e) { + throw (IOException)(new IOException(e.getMessage())).initCause(e); + } + } + + @Override + public Integer getNewSqlType(String typeName) throws Exception { + java.lang.reflect.Field sqlTypeField = Types.class.getField(typeName); + return (Integer)sqlTypeField.get(null); + } +} diff --git a/src/main/java/com/healthmarketscience/jackcess/util/ImportUtil.java b/src/main/java/com/healthmarketscience/jackcess/util/ImportUtil.java index 2fd6455..b68e448 100644 --- a/src/main/java/com/healthmarketscience/jackcess/util/ImportUtil.java +++ b/src/main/java/com/healthmarketscience/jackcess/util/ImportUtil.java @@ -16,6 +16,13 @@ limitations under the License. package com.healthmarketscience.jackcess.util; +import com.healthmarketscience.jackcess.ColumnBuilder; +import com.healthmarketscience.jackcess.DataType; +import com.healthmarketscience.jackcess.Database; +import com.healthmarketscience.jackcess.Table; +import com.healthmarketscience.jackcess.TableBuilder; +import com.healthmarketscience.jackcess.impl.ByteUtil; +import com.healthmarketscience.jackcess.impl.DatabaseImpl; import java.io.BufferedReader; import java.io.EOFException; import java.io.File; @@ -29,14 +36,6 @@ import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; -import com.healthmarketscience.jackcess.ColumnBuilder; -import com.healthmarketscience.jackcess.DataType; -import com.healthmarketscience.jackcess.Database; -import com.healthmarketscience.jackcess.Table; -import com.healthmarketscience.jackcess.TableBuilder; -import com.healthmarketscience.jackcess.impl.ByteUtil; -import com.healthmarketscience.jackcess.impl.DatabaseImpl; - /** * Utility class for importing tables to an Access database from other * sources. See the {@link Builder} for convenient configuration of the @@ -64,7 +63,7 @@ public class ImportUtil * @return a List of Columns */ public static List toColumns(ResultSetMetaData md) - throws SQLException + throws SQLException, IOException { List columns = new ArrayList(); for (int i = 1; i <= md.getColumnCount(); i++) { diff --git a/src/test/java/com/healthmarketscience/jackcess/impl/JetFormatTest.java b/src/test/java/com/healthmarketscience/jackcess/impl/JetFormatTest.java index 9257e5e..19d95f0 100644 --- a/src/test/java/com/healthmarketscience/jackcess/impl/JetFormatTest.java +++ b/src/test/java/com/healthmarketscience/jackcess/impl/JetFormatTest.java @@ -6,7 +6,6 @@ import java.io.InputStream; import java.nio.charset.Charset; import java.nio.channels.FileChannel; import java.nio.channels.NonWritableChannelException; -import java.sql.SQLException; import java.util.ArrayList; import java.util.EnumSet; import java.util.List; @@ -14,6 +13,7 @@ import java.util.Set; import com.healthmarketscience.jackcess.DataType; import com.healthmarketscience.jackcess.Database; +import com.healthmarketscience.jackcess.JackcessException; import static com.healthmarketscience.jackcess.Database.*; import com.healthmarketscience.jackcess.DatabaseBuilder; import com.healthmarketscience.jackcess.PropertyMap; @@ -292,7 +292,7 @@ public class JetFormatTest extends TestCase { Integer sqlType = null; try { sqlType = dt.getSQLType(); - } catch(SQLException ignored) {} + } catch(JackcessException ignored) {} if(sqlType != null) { assertEquals(dt, DataType.fromSQLType(sqlType));