/** * Copyright (C) 2009-2013 FoundationDB, LLC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.foundationdb.server.types.value; import com.foundationdb.server.collation.AkCollator; import com.foundationdb.server.types.TInstance; public final class Value implements ValueSource, ValueTarget { // Value interface public void underlying(TInstance underlying) { this.type = underlying; this.state = State.UNSET; } public void unset() { this.state = State.UNSET; } // ValueTarget interface @Override public boolean supportsCachedObjects() { return true; } @Override public final void putNull() { setRawValues(State.NULL, -1, null, null); } @Override public void putBool(boolean value) { setIVal(UnderlyingType.BOOL, value ? BOOL_TRUE : BOOL_FALSE); } @Override public final void putInt8(byte value) { setIVal(UnderlyingType.INT_8, value); } @Override public final void putInt16(short value) { setIVal(UnderlyingType.INT_16, value); } @Override public final void putUInt16(char value) { setIVal(UnderlyingType.UINT_16, value); } @Override public final void putInt32(int value) { setIVal(UnderlyingType.INT_32, value); } @Override public final void putInt64(long value) { setIVal(UnderlyingType.INT_64, value); } @Override public final void putFloat(float value) { setIVal(UnderlyingType.FLOAT, Float.floatToIntBits(value)); } @Override public final void putDouble(double value) { setIVal(UnderlyingType.DOUBLE, Double.doubleToLongBits(value)); } @Override public final void putBytes(byte[] value) { checkUnderlying(UnderlyingType.BYTES); setRawValues(State.VAL_ONLY, -1, value, null); } @Override public void putString(String value, AkCollator collator) { checkUnderlying(UnderlyingType.STRING); setRawValues(State.VAL_ONLY, -1, value, null); } @Override public final void putObject(Object object) { if (object == null) putNull(); else setRawValues(State.CACHE_ONLY, -1, null, object); } // ValueSource interface @Override public final boolean isNull() { if (state == State.UNSET) throw new IllegalStateException("state not set"); return state == State.NULL; } @Override public boolean getBoolean() { return getIVal(UnderlyingType.BOOL) == BOOL_TRUE; } @Override public boolean getBoolean(boolean defaultValue) { return isNull() ? defaultValue : getBoolean(); } @Override public final byte getInt8() { return (byte) getIVal(UnderlyingType.INT_8); } @Override public final short getInt16() { return (short) getIVal(UnderlyingType.INT_16); } @Override public final char getUInt16() { return (char) getIVal(UnderlyingType.UINT_16); } @Override public final int getInt32() { return (int) getIVal(UnderlyingType.INT_32); } @Override public final long getInt64() { return getIVal(UnderlyingType.INT_64); } @Override public final float getFloat() { int i = (int) getIVal(UnderlyingType.FLOAT); return Float.intBitsToFloat(i); } @Override public final double getDouble() { long l = getIVal(UnderlyingType.DOUBLE); return Double.longBitsToDouble(l); } @Override public final byte[] getBytes() { checkUnderlying(UnderlyingType.BYTES); checkRawState(); return (byte[]) rawObject; } @Override public String getString() { checkUnderlying(UnderlyingType.STRING); checkRawState(); return (String) rawObject; } @Override public final Object getObject() { ensureCached(); assert state != State.UNSET : State.UNSET; return oCache; } @Override public boolean hasAnyValue() { return state != State.UNSET; } @Override public boolean hasRawValue() { switch (state) { case UNSET: case CACHE_ONLY: return false; case NULL: case VAL_ONLY: case VAL_AND_CACHE: return true; default: throw new AssertionError(state); } } @Override public boolean hasCacheValue() { switch (state) { case UNSET: case VAL_ONLY: return false; case NULL: case CACHE_ONLY: case VAL_AND_CACHE: return true; default: throw new AssertionError(state); } } @Override public boolean canGetRawValue() { if (hasRawValue()) return true; ValueCacher cacher = type.typeClass().cacher(); return cacher != null && cacher.canConvertToValue(oCache); } // ValueSource + ValueTarget @Override public TInstance getType() { return type; } // Object interface @Override public String toString() { StringBuilder sb = new StringBuilder("Value(").append(type).append(" = "); switch (state) { case UNSET: sb.append("<empty>"); break; case NULL: sb.append("NULL"); break; case VAL_AND_CACHE: sb.append("cached <").append(oCache).append(">: "); case VAL_ONLY: switch (TInstance.underlyingType(type)) { case INT_8: case INT_16: case INT_32: case INT_64: case UINT_16: sb.append(iVal); break; case FLOAT: sb.append(getFloat()); break; case DOUBLE: sb.append(getDouble()); break; case BOOL: sb.append(getBoolean()); break; case STRING: sb.append('"').append(getString()).append('"'); break; case BYTES: sb.append("0x "); byte[] bVal = (byte[]) rawObject; for (int i = 0, max= bVal.length; i < max; ++i) { byte b = bVal[i]; int bInt = ((int)b) & 0xFF; if (i > 0 && (i % 2 == 0)) sb.append(' '); sb.append(Integer.toHexString(bInt).toUpperCase()); } break; } break; case CACHE_ONLY: sb.append("(cached) ").append(oCache); break; } sb.append(')'); return sb.toString(); } private long getIVal(UnderlyingType expectedType) { checkUnderlying(expectedType); checkRawState(); return iVal; } private void checkUnderlying(UnderlyingType expected) { UnderlyingType underlyingType = TInstance.underlyingType(type); if (underlyingType != expected) { String underlyingToString = (underlyingType == null) ? "unspecified" : underlyingType.name(); throw new IllegalStateException("required underlying " + expected + " but was " + underlyingToString); } } private void setIVal(UnderlyingType expectedType, long value) { checkUnderlying(expectedType); setRawValues(State.VAL_ONLY, value, null, null); } private void setRawValues(State state, long iVal, Object rawObject, Object oCache) { this.state = state; this.iVal = iVal; this.rawObject = rawObject; this.oCache = oCache; } public Value() { this(null); } public Value(TInstance type) { underlying(type); } public Value(TInstance type, byte[] val) { this(type); putBytes(val); } public Value(TInstance type, long val) { this(type); putInt64(val); } public Value(TInstance type, float val) { this(type); putFloat(val); } public Value(TInstance type, double val) { this(type); putDouble(val); } public Value(TInstance type, int val) { this(type); putInt32(val); } public Value(TInstance type, short val) { this(type); putInt16(val); } public Value(TInstance type, byte val) { this(type); putInt8(val); } public Value(TInstance type, String val) { this(type); putString(val, null); } public Value(TInstance type, boolean val) { this(type); putBool(val); } private void checkRawState() { switch (state) { case UNSET: case CACHE_ONLY: Object oCacheSave = oCache; type.typeClass().cacher().cacheToValue(oCacheSave, type, this); assert state == State.VAL_ONLY : state; oCache = oCacheSave; state = State.VAL_AND_CACHE; break; case NULL: throw new NullValueException(); case VAL_ONLY: case VAL_AND_CACHE: break; default: throw new AssertionError(state); } } private void ensureCached() { switch (state) { case UNSET: throw new IllegalStateException("no value set"); case VAL_ONLY: oCache = type.typeClass().cacher().valueToCache(this, type); state = State.VAL_AND_CACHE; break; case NULL: case CACHE_ONLY: case VAL_AND_CACHE: break; default: throw new AssertionError(state); } } private TInstance type; private State state; private long iVal; private Object rawObject; private Object oCache; private static final long BOOL_TRUE = 1L; private static final long BOOL_FALSE = 0L; private enum State { UNSET, NULL, VAL_ONLY, CACHE_ONLY, VAL_AND_CACHE } }