/* Copyright (c) 2001-2018, The HSQL Development Group
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the HSQL Development Group nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.hsqldb.types;
import java.util.
BitSet;
import org.hsqldb.
OpTypes;
import org.hsqldb.
Session;
import org.hsqldb.
SessionInterface;
import org.hsqldb.
Tokens;
import org.hsqldb.error.
Error;
import org.hsqldb.error.
ErrorCode;
import org.hsqldb.lib.
ArrayUtil;
import org.hsqldb.lib.
StringConverter;
import org.hsqldb.map.
BitMap;
/**
*
* Operations allowed on BIT strings are CONCAT, SUBSTRING, POSITION,
* BIT_LENGTH and OCTET_LENGTH.<p>
*
* BIT values can be cast to BINARY and vice-versa. In casts, BIT values are
* converted to their counterpart BINARY values by treating each set of 8 bits
* or less as a single byte. The first bit of a BIT string is treated as the most
* significant bit of the resulting byte value. Binary values are converted by
* treating the bits in the sequence of bytes as sequence of bits in the BIT
* string<p>
*
* @author Fred Toussi (fredt@users dot sourceforge.net)
* @version 2.4.1
* @since 1.9.0
*/
public final class
BitType extends
BinaryType {
static final long
maxBitPrecision = 1024;
public
BitType(int
type, long
precision) {
super(
type,
precision);
}
public int
displaySize() {
return (int)
precision;
}
public int
getJDBCTypeCode() {
return
Types.
BIT;
}
public
Class getJDBCClass() {
return byte[].class;
}
public
String getJDBCClassName() {
return "[B";
}
public int
getSQLGenericTypeCode() {
return
typeCode;
}
public
String getNameString() {
return
typeCode ==
Types.
SQL_BIT ?
Tokens.
T_BIT
: "BIT VARYING";
}
public
String getDefinition() {
if (
precision == 0) {
return
getNameString();
}
StringBuffer sb = new
StringBuffer(16);
sb.
append(
getNameString());
sb.
append('(');
sb.
append(
precision);
sb.
append(')');
return
sb.
toString();
}
public boolean
isBitType() {
return true;
}
public long
getMaxPrecision() {
return
maxBitPrecision;
}
public boolean
requiresPrecision() {
return
typeCode ==
Types.
SQL_BIT_VARYING;
}
public
Type getAggregateType(
Type other) {
if (
other == null) {
return this;
}
if (
other ==
SQL_ALL_TYPES) {
return this;
}
if (
typeCode ==
other.
typeCode) {
return
precision >=
other.
precision ? this
:
other;
}
switch (
other.
typeCode) {
case
Types.
SQL_BIT :
return
precision >=
other.
precision ? this
:
getBitType(
typeCode,
other.
precision);
case
Types.
SQL_BIT_VARYING :
return
other.
precision >=
precision ?
other
:
getBitType(
other.
typeCode,
precision);
case
Types.
SQL_BINARY :
case
Types.
SQL_VARBINARY :
case
Types.
SQL_BLOB :
return
other;
default :
throw
Error.
error(
ErrorCode.
X_42562);
}
}
/**
* Returns type for concat
*/
public
Type getCombinedType(
Session session,
Type other, int
operation) {
if (
operation !=
OpTypes.
CONCAT) {
return
getAggregateType(
other);
}
Type newType;
long
newPrecision =
precision +
other.
precision;
switch (
other.
typeCode) {
case
Types.
SQL_ALL_TYPES :
return this;
case
Types.
SQL_BIT :
newType = this;
break;
case
Types.
SQL_BIT_VARYING :
newType =
other;
break;
case
Types.
SQL_BINARY :
case
Types.
SQL_VARBINARY :
case
Types.
SQL_BLOB :
return
other.
getCombinedType(
session, this,
operation);
default :
throw
Error.
error(
ErrorCode.
X_42562);
}
if (
newPrecision >
maxBitPrecision) {
if (
typeCode ==
Types.
SQL_BIT) {
// Standard disallows type length reduction
throw
Error.
error(
ErrorCode.
X_42570);
}
newPrecision =
maxBitPrecision;
}
return
getBitType(
newType.
typeCode,
newPrecision);
}
public int
compare(
Session session,
Object a,
Object b) {
int
i = super.compare(
session,
a,
b);
if (
i == 0 &&
a != null) {
if (((
BinaryData)
a).
bitLength(null)
== ((
BinaryData)
b).
bitLength(null)) {
return 0;
}
return ((
BinaryData)
a).
bitLength(null)
> ((
BinaryData)
b).
bitLength(null) ? 1
: -1;
}
return
i;
}
public
Object convertToTypeLimits(
SessionInterface session,
Object a) {
return
castOrConvertToType(null,
a, this, false);
}
public
Object castToType(
SessionInterface session,
Object a,
Type otherType) {
return
castOrConvertToType(
session,
a,
otherType, true);
}
public
Object convertToType(
SessionInterface session,
Object a,
Type otherType) {
return
castOrConvertToType(
session,
a,
otherType, false);
}
Object castOrConvertToType(
SessionInterface session,
Object a,
Type otherType, boolean
cast) {
BlobData b;
if (
a == null) {
return null;
}
switch (
otherType.
typeCode) {
case
Types.
SQL_VARCHAR :
case
Types.
SQL_CHAR : {
b =
session.
getScanner().
convertToBit((
String)
a);
otherType =
getBitType(
Types.
SQL_BIT_VARYING,
b.
length(
session));
break;
}
case
Types.
SQL_BIT :
case
Types.
SQL_BIT_VARYING :
case
Types.
SQL_BINARY :
case
Types.
SQL_VARBINARY :
case
Types.
SQL_BLOB :
b = (
BlobData)
a;
break;
case
Types.
SQL_BOOLEAN : {
if (
precision != 1) {
throw
Error.
error(
ErrorCode.
X_22501);
}
if (((
Boolean)
a).
booleanValue()) {
return
BinaryData.
singleBitOne;
} else {
return
BinaryData.
singleBitZero;
}
}
case
Types.
TINYINT :
case
Types.
SQL_SMALLINT :
case
Types.
SQL_INTEGER :
case
Types.
SQL_BIGINT :
case
Types.
SQL_REAL :
case
Types.
SQL_FLOAT :
case
Types.
SQL_DOUBLE :
case
Types.
SQL_NUMERIC :
case
Types.
SQL_DECIMAL : {
if (
precision != 1) {
throw
Error.
error(
ErrorCode.
X_22501);
}
if (((
NumberType)
otherType).
compareToZero(
a) == 0) {
return
BinaryData.
singleBitZero;
} else {
return
BinaryData.
singleBitOne;
}
}
default :
throw
Error.
error(
ErrorCode.
X_22501);
}
// no special 0 bit consideration
if (
b.
bitLength(
session) >
precision) {
if (!
cast) {
throw
Error.
error(
ErrorCode.
X_22001);
}
session.
addWarning(
Error.
error(
ErrorCode.
W_01004));
}
int
bytePrecision = (int) ((
precision + 7) / 8);
if (
otherType.
typeCode ==
Types.
SQL_BLOB) {
byte[]
bytes =
b.
getBytes(
session, 0,
bytePrecision);
b = new
BinaryData(
bytes,
precision);
}
switch (
typeCode) {
case
Types.
SQL_BIT : {
if (
b.
bitLength(
session) ==
precision) {
return
b;
}
if (
b.
length(
session) >
bytePrecision) {
byte[]
data =
b.
getBytes(
session, 0,
bytePrecision);
b = new
BinaryData(
data,
precision);
} else if (
b.
length(
session) <=
bytePrecision) {
byte[]
data = (byte[])
ArrayUtil.
resizeArray(
b.
getBytes(),
bytePrecision);
b = new
BinaryData(
data,
precision);
}
break;
}
case
Types.
SQL_BIT_VARYING : {
if (
b.
bitLength(
session) <=
precision) {
return
b;
}
if (
b.
length(
session) >
bytePrecision) {
byte[]
data =
b.
getBytes(
session, 0,
bytePrecision);
b = new
BinaryData(
data,
precision);
}
break;
}
default :
throw
Error.
error(
ErrorCode.
X_22501);
}
byte[]
data =
b.
getBytes();
for (int
i = (int)
precision;
i <
b.
length(
session) * 8;
i++) {
BitMap.
unset(
data,
i);
}
return
b;
}
public
Object convertToDefaultType(
SessionInterface session,
Object a) {
if (
a == null) {
return
a;
}
if (
a instanceof byte[]) {
BinaryData data = new
BinaryData((byte[])
a, ((byte[])
a).length);
return
convertToTypeLimits(
session,
data);
} else if (
a instanceof
BinaryData) {
return
convertToTypeLimits(
session,
a);
} else if (
a instanceof
String) {
return
convertToType(
session,
a,
Type.
SQL_VARCHAR);
} else if (
a instanceof
Boolean) {
return
convertToType(
session,
a,
Type.
SQL_BOOLEAN);
} else if (
a instanceof
Integer) {
return
convertToType(
session,
a,
Type.
SQL_INTEGER);
} else if (
a instanceof
Long) {
return
convertToType(
session,
a,
Type.
SQL_BIGINT);
} else if (
a instanceof
BitSet) {
BitSet bs = (
BitSet)
a;
byte[]
bytes =
new byte[((int)
precision +
Byte.
SIZE - 1) /
Byte.
SIZE];
if (
bs.
length() >
precision) {
throw
Error.
error(
ErrorCode.
X_22501);
}
for (int
i = 0;
i <
bs.
length();
i++) {
boolean
set =
bs.
get(
i);
if (
set) {
BitMap.
set(
bytes,
i);
}
}
return new
BinaryData(
bytes,
precision);
}
throw
Error.
error(
ErrorCode.
X_22501);
}
public
Object convertJavaToSQL(
SessionInterface session,
Object a) {
return
convertToDefaultType(
session,
a);
}
public
String convertToString(
Object a) {
if (
a == null) {
return null;
}
return
StringConverter.
byteArrayToBitString(
((
BinaryData)
a).
getBytes(),
(int) ((
BinaryData)
a).
bitLength(null));
}
public
String convertToSQLString(
Object a) {
if (
a == null) {
return
Tokens.
T_NULL;
}
return
StringConverter.
byteArrayToSQLBitString(
((
BinaryData)
a).
getBytes(),
(int) ((
BinaryData)
a).
bitLength(null));
}
public boolean
canConvertFrom(
Type otherType) {
return
otherType.
typeCode ==
Types.
SQL_ALL_TYPES
||
otherType.
isBinaryType()
|| (
precision == 1
&& (
otherType.
isIntegralType() ||
otherType
.
isBooleanType()) ||
otherType.
isCharacterType());
}
/** @todo - implement */
public long
position(
SessionInterface session,
BlobData data,
BlobData otherData,
Type otherType, long
offset) {
if (
data == null ||
otherData == null) {
return -1L;
}
long
otherLength =
data.
bitLength(
session);
if (
offset +
otherLength >
data.
bitLength(
session)) {
return -1;
}
throw
Error.
runtimeError(
ErrorCode.
U_S0500, "BitType");
}
public
BlobData substring(
SessionInterface session,
BlobData data,
long
offset, long
length, boolean
hasLength) {
long
end;
long
dataLength =
data.
bitLength(
session);
if (
hasLength) {
end =
offset +
length;
} else {
end =
dataLength >
offset ?
dataLength
:
offset;
}
if (
end <
offset) {
throw
Error.
error(
ErrorCode.
X_22011);
}
if (
offset >
end ||
end < 0) {
// return zero length data
offset = 0;
end = 0;
}
if (
offset < 0) {
offset = 0;
}
if (
end >
dataLength) {
end =
dataLength;
}
length =
end -
offset;
byte[]
dataBytes =
data.
getBytes();
byte[]
bytes = new byte[(int) (
length + 7) / 8];
for (int
i = (int)
offset;
i <
end;
i++) {
if (
BitMap.
isSet(
dataBytes,
i)) {
BitMap.
set(
bytes,
i - (int)
offset);
}
}
return new
BinaryData(
bytes,
length);
}
int
getRightTrimSize(
BinaryData data) {
int
i = (int)
data.
bitLength(null) - 1;
byte[]
bytes =
data.
getBytes();
for (;
i >= 0;
i--) {
if (
BitMap.
isSet(
bytes,
i)) {
break;
}
}
return
i + 1;
}
public
BlobData overlay(
Session session,
BlobData value,
BlobData overlay,
long
offset, long
length, boolean
hasLength) {
if (
value == null ||
overlay == null) {
return null;
}
if (!
hasLength) {
length =
overlay.
bitLength(
session);
}
switch (
typeCode) {
case
Types.
SQL_BIT :
case
Types.
SQL_BIT_VARYING : {
byte[]
data =
(byte[])
ArrayUtil.
duplicateArray(
value.
getBytes());
byte[]
overlaydata =
overlay.
getBytes();
for (int
i = 0,
pos = (int)
offset;
i <
length;
pos += 8,
i++) {
int
count = 8;
if (
length -
pos < 8) {
count = (int)
length -
pos;
}
BitMap.
overlay(
data,
pos,
overlaydata[
i],
count);
}
BinaryData binary = new
BinaryData(
data,
value.
bitLength(
session));
return
binary;
}
default :
throw
Error.
runtimeError(
ErrorCode.
U_S0500, "BitType");
}
}
public
Object concat(
Session session,
Object a,
Object b) {
if (
a == null ||
b == null) {
return null;
}
long
length = ((
BlobData)
a).
bitLength(
session)
+ ((
BlobData)
b).
bitLength(
session);
if (
length >
Integer.
MAX_VALUE) {
throw
Error.
error(
ErrorCode.
W_01000);
}
byte[]
aData = ((
BlobData)
a).
getBytes();
byte[]
bData = ((
BlobData)
b).
getBytes();
int
aLength = (int) ((
BlobData)
a).
bitLength(
session);
int
bLength = (int) ((
BlobData)
b).
bitLength(
session);
byte[]
bytes = new byte[(int) (
length + 7) / 8];
System.
arraycopy(
aData, 0,
bytes, 0,
aData.length);
for (int
i = 0;
i <
bLength;
i++) {
if (
BitMap.
isSet(
bData,
i)) {
BitMap.
set(
bytes,
aLength +
i);
}
}
return new
BinaryData(
bytes,
length);
}
public static
BinaryType getBitType(int
type, long
precision) {
switch (
type) {
case
Types.
SQL_BIT :
case
Types.
SQL_BIT_VARYING :
return new
BitType(
type,
precision);
default :
throw
Error.
runtimeError(
ErrorCode.
U_S0500, "BitType");
}
}
public static
BitSet getJavaBitSet(
BinaryData data) {
int
bits = (int)
data.
bitLength(null);
BitSet bs = new
BitSet(
bits);
byte[]
bytes =
data.
getBytes();
for (int
i = 0;
i <
bits;
i++) {
boolean
set =
BitMap.
isSet(
bytes,
i);
if (
set) {
bs.
set(
i);
}
}
return
bs;
}
}