package org.h2.value;
import static org.h2.util.DateTimeUtils.NANOS_PER_DAY;
import static org.h2.util.DateTimeUtils.NANOS_PER_HOUR;
import static org.h2.util.DateTimeUtils.NANOS_PER_MINUTE;
import static org.h2.util.DateTimeUtils.NANOS_PER_SECOND;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.h2.api.Interval;
import org.h2.api.IntervalQualifier;
import org.h2.message.DbException;
import org.h2.util.DateTimeUtils;
import org.h2.util.IntervalUtils;
public class ValueInterval extends Value {
public static final int DEFAULT_PRECISION = 2;
public static final int MAXIMUM_PRECISION = 18;
static final int DEFAULT_SCALE = 6;
public static final int MAXIMUM_SCALE = 9;
private final int valueType;
private TypeInfo type;
private final boolean negative;
private final long leading;
private final long remaining;
public static ValueInterval from(IntervalQualifier qualifier, boolean negative, long leading, long remaining) {
negative = IntervalUtils.validateInterval(qualifier, negative, leading, remaining);
return (ValueInterval) Value
.cache(new ValueInterval(qualifier.ordinal() + INTERVAL_YEAR, negative, leading, remaining));
}
public static int getDisplaySize(int type, int precision, int scale) {
switch (type) {
case INTERVAL_YEAR:
case INTERVAL_HOUR:
return 17 + precision;
case INTERVAL_MONTH:
return 18 + precision;
case INTERVAL_DAY:
return 16 + precision;
case INTERVAL_MINUTE:
return 19 + precision;
case INTERVAL_SECOND:
return scale > 0 ? 20 + precision + scale : 19 + precision;
case INTERVAL_YEAR_TO_MONTH:
return 29 + precision;
case INTERVAL_DAY_TO_HOUR:
return 27 + precision;
case INTERVAL_DAY_TO_MINUTE:
return 32 + precision;
case INTERVAL_DAY_TO_SECOND:
return scale > 0 ? 36 + precision + scale : 35 + precision;
case INTERVAL_HOUR_TO_MINUTE:
return 30 + precision;
case INTERVAL_HOUR_TO_SECOND:
return scale > 0 ? 34 + precision + scale : 33 + precision;
case INTERVAL_MINUTE_TO_SECOND:
return scale > 0 ? 33 + precision + scale : 32 + precision;
default:
throw DbException.getUnsupportedException(Integer.toString(type));
}
}
private ValueInterval(int type, boolean negative, long leading, long remaining) {
this.valueType = type;
this.negative = negative;
this.leading = leading;
this.remaining = remaining;
}
@Override
public StringBuilder getSQL(StringBuilder builder) {
return IntervalUtils.appendInterval(builder, getQualifier(), negative, leading, remaining);
}
@Override
public TypeInfo getType() {
TypeInfo type = this.type;
if (type == null) {
long l = leading;
int precision = 0;
while (l > 0) {
precision++;
l /= 10;
}
if (precision == 0) {
precision = 1;
}
this.type = type = new TypeInfo(valueType, precision, 0,
getDisplaySize(valueType, MAXIMUM_PRECISION, MAXIMUM_SCALE), null);
}
return type;
}
@Override
public int getValueType() {
return valueType;
}
@Override
public int getMemory() {
return 48;
}
@Override
public Value convertScale(boolean onlyToSmallerScale, int targetScale) {
if (targetScale >= MAXIMUM_SCALE) {
return this;
}
if (targetScale < 0) {
throw DbException.getInvalidValueException("scale", targetScale);
}
IntervalQualifier qualifier = getQualifier();
if (!qualifier.hasSeconds()) {
return this;
}
long r = DateTimeUtils.convertScale(remaining, targetScale);
if (r == remaining) {
return this;
}
long l = leading;
switch (valueType) {
case INTERVAL_SECOND:
if (r >= NANOS_PER_SECOND) {
l++;
r -= NANOS_PER_SECOND;
}
break;
case INTERVAL_DAY_TO_SECOND:
if (r >= NANOS_PER_DAY) {
l++;
r -= NANOS_PER_DAY;
}
break;
case INTERVAL_HOUR_TO_SECOND:
if (r >= NANOS_PER_HOUR) {
l++;
r -= NANOS_PER_HOUR;
}
break;
case INTERVAL_MINUTE_TO_SECOND:
if (r >= NANOS_PER_MINUTE) {
l++;
r -= NANOS_PER_MINUTE;
}
break;
}
return from(qualifier, negative, l, r);
}
@Override
public String getString() {
return IntervalUtils.appendInterval(new StringBuilder(), getQualifier(), negative, leading, remaining)
.toString();
}
@Override
public Object getObject() {
return new Interval(getQualifier(), negative, leading, remaining);
}
public IntervalQualifier getQualifier() {
return IntervalQualifier.valueOf(valueType - INTERVAL_YEAR);
}
public boolean isNegative() {
return negative;
}
public long getLeading() {
return leading;
}
public long getRemaining() {
return remaining;
}
@Override
public void set(PreparedStatement prep, int parameterIndex) throws SQLException {
prep.setString(parameterIndex, getString());
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + valueType;
result = prime * result + (negative ? 1231 : 1237);
result = prime * result + (int) (leading ^ leading >>> 32);
result = prime * result + (int) (remaining ^ remaining >>> 32);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof ValueInterval)) {
return false;
}
ValueInterval other = (ValueInterval) obj;
return valueType == other.valueType && negative == other.negative && leading == other.leading
&& remaining == other.remaining;
}
@Override
public int compareTypeSafe(Value v, CompareMode mode) {
ValueInterval other = (ValueInterval) v;
if (negative != other.negative) {
return negative ? -1 : 1;
}
int cmp = Long.compare(leading, other.leading);
if (cmp == 0) {
cmp = Long.compare(remaining, other.remaining);
}
return negative ? -cmp : cmp;
}
@Override
public int getSignum() {
return negative ? -1 : leading == 0L && remaining == 0L ? 0 : 1;
}
@Override
public Value add(Value v) {
return IntervalUtils.intervalFromAbsolute(getQualifier(),
IntervalUtils.intervalToAbsolute(this).add(IntervalUtils.intervalToAbsolute((ValueInterval) v)));
}
@Override
public Value subtract(Value v) {
return IntervalUtils.intervalFromAbsolute(getQualifier(),
IntervalUtils.intervalToAbsolute(this).subtract(IntervalUtils.intervalToAbsolute((ValueInterval) v)));
}
@Override
public Value negate() {
if (leading == 0L && remaining == 0L) {
return this;
}
return Value.cache(new ValueInterval(valueType, !negative, leading, remaining));
}
}