/*
* JBoss, Home of Professional Open Source.
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership. Some portions may be licensed
* to Red Hat, Inc. under one or more contributor license agreements.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
package org.teiid.core.types;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.sql.Array;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Map;
import org.teiid.core.TeiidRuntimeException;
import org.teiid.core.util.ExternalizeUtil;
import org.teiid.core.util.HashCodeUtil;
/**
* Provides a serializable {@link Array} implementation with minimal JDBC functionality.
*/
public final class ArrayImpl implements Comparable<ArrayImpl>, Externalizable, Array {
private static final String INVALID = ""; //$NON-NLS-1$
private static final long serialVersionUID = 517794153664734815L;
/**
* a regrettable hack for pg compatibility since we want to avoid adding a vector type
*/
private boolean zeroBased;
private Object[] values;
@SuppressWarnings("serial")
public final static class NullException extends RuntimeException {};
private final static NullException ex = new NullException();
public ArrayImpl(Object... values) {
this.values = values;
}
public ArrayImpl() {
}
private void checkValues() throws SQLException {
if (values == null) {
throw new SQLException("Already freed or invalid"); //$NON-NLS-1$
}
}
@Override
public int compareTo(ArrayImpl o) {
return compareTo(o, false, null);
}
public int compareTo(ArrayImpl o, boolean noNulls, Comparator<Object> comparator) {
if (zeroBased != o.zeroBased) {
throw new TeiidRuntimeException("Incompatible types"); //$NON-NLS-1$
}
try {
checkValues();
o.checkValues();
} catch (SQLException e) {
throw new TeiidRuntimeException(e);
}
Object[] values2 = o.values;
return compare(noNulls, comparator, values, values2);
}
private static int compare(boolean noNulls, Comparator<Object> comparator,
Object[] values, Object[] values2) {
int len1 = values.length;
int len2 = values2.length;
int lim = Math.min(len1, len2);
for (int k = 0; k < lim; k++) {
Object object1 = values[k];
Object object2 = values2[k];
if (object1 == null) {
if (noNulls) {
throw ex;
}
if (object2 != null) {
return -1;
}
continue;
} else if (object2 == null) {
if (noNulls) {
throw ex;
}
return 1;
}
int comp = 0;
if (object1 instanceof Object[] && object2 instanceof Object[]) {
comp = compare(noNulls, comparator, (Object[])object1, (Object[])object2);
} else if (comparator != null) {
comp = comparator.compare(object1, object2);
} else {
comp = ((Comparable)object1).compareTo(object2);
}
if (comp != 0) {
return comp;
}
}
return len1 - len2;
}
@Override
public int hashCode() {
return HashCodeUtil.expHashCode(0, this.values);
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof ArrayImpl)) {
return false;
}
ArrayImpl other = (ArrayImpl)obj;
return zeroBased == other.zeroBased && compareTo(other) == 0;
}
public Object[] getValues() {
if (values == null) {
throw new TeiidRuntimeException("Already freed or invalid"); //$NON-NLS-1$
}
return values;
}
@Override
public String toString() {
return Arrays.toString(this.values);
}
public boolean isZeroBased() {
return zeroBased;
}
public void setZeroBased(boolean zeroBased) {
this.zeroBased = zeroBased;
}
@Override
public void free() throws SQLException {
this.values = null;
}
@Override
public Object getArray() throws SQLException {
checkValues();
return values;
}
@Override
public Object getArray(Map<String, Class<?>> map) throws SQLException {
throw new SQLFeatureNotSupportedException();
}
@Override
public Object getArray(long index, int count) throws SQLException {
if (index > Integer.MAX_VALUE) {
throw new ArrayIndexOutOfBoundsException(String.valueOf(index));
}
int offset = zeroBased?0:1;
int iIndex = (int)index - offset;
if (iIndex >= values.length || iIndex < 0) {
throw new ArrayIndexOutOfBoundsException(iIndex);
}
checkValues();
return Arrays.copyOfRange(values, iIndex, Math.min(iIndex + count, values.length));
}
@Override
public Object getArray(long index, int count, Map<String, Class<?>> map)
throws SQLException {
throw new SQLFeatureNotSupportedException();
}
@Override
public int getBaseType() throws SQLException {
checkValues();
return JDBCSQLTypeInfo.getSQLType(DataTypeManager.getDataTypeName(values.getClass().getComponentType()));
}
@Override
public String getBaseTypeName() throws SQLException {
checkValues();
return DataTypeManager.getDataTypeName(values.getClass().getComponentType());
}
@Override
public ResultSet getResultSet() throws SQLException {
throw new SQLFeatureNotSupportedException();
}
@Override
public ResultSet getResultSet(Map<String, Class<?>> map)
throws SQLException {
throw new SQLFeatureNotSupportedException();
}
@Override
public ResultSet getResultSet(long index, int count) throws SQLException {
throw new SQLFeatureNotSupportedException();
}
@Override
public ResultSet getResultSet(long index, int count,
Map<String, Class<?>> map) throws SQLException {
throw new SQLFeatureNotSupportedException();
}
@Override
public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException {
String componentType = in.readUTF();
if (INVALID.equalsIgnoreCase(componentType)) {
return;
}
this.values = ExternalizeUtil.readArray(in, DataTypeManager.getDataTypeClass(componentType));
zeroBased = in.readBoolean();
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
if (values == null) {
out.writeUTF(INVALID);
return;
}
out.writeUTF(DataTypeManager.getDataTypeName(this.values.getClass().getComponentType()));
ExternalizeUtil.writeArray(out, values);
out.writeBoolean(zeroBased);
}
}