/*
* Copyright (C) 2012 Facebook, Inc.
*
* 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.facebook.data.types;
import com.google.common.base.Preconditions;
/**
* caches the conversion of a Datum to a long/double. The idea here is to save
* on cpu cost by caching the conversion to the number format with the highest
* bit-width
*/
public class NumberCachingDatum extends WrappedDatum {
private volatile Long longValue;
private volatile Double doubleValue;
private volatile boolean checkDone = false;
private volatile DatumType datumType;
public NumberCachingDatum(Datum delegate) {
super(delegate);
}
@Override
public long asLong() {
return getAsLong();
}
@Override
public boolean asBoolean() {
return getAsLong() != 0;
}
@Override
public byte asByte() {
return (byte) getAsLong();
}
@Override
public short asShort() {
return (short) getAsLong();
}
@Override
public int asInteger() {
return (int) getAsLong();
}
@Override
public float asFloat() {
return (float) getAsDouble();
}
@Override
public double asDouble() {
return getAsDouble();
}
@Override
public DatumType getType() {
checkType();
return datumType;
}
// does conversion to both long and double
private void checkType() {
DatumType type = super.getType();
switch (type) {
case NULL:
datumType = type;
break;
case BOOLEAN:
case BYTE:
case CHAR:
case SHORT:
case INTEGER:
case LONG:
case FLOAT:
case DOUBLE:
datumType = type;
break;
case STRING:
datumType = computeTypeOfString();
break;
case LIST:
case MAP:
case OTHER:
default:
datumType = type;
break;
}
}
private DatumType computeTypeOfString() {
Preconditions.checkState(
super.getType() == DatumType.STRING,
"only DatumType of STRING require special processing"
);
try {
getAsLong();
} catch (NumberFormatException e) {
// ignore
}
try {
getAsDouble();
} catch (NumberFormatException e) {
//ignore
}
if (longValue != null && doubleValue != null) {
// old school way to tell if a double you cast to a long can be represented as a long
@SuppressWarnings("FloatingPointEquality")
boolean isLongType = ((double) longValue) == doubleValue;
return isLongType ? DatumType.LONG : DatumType.DOUBLE;
} else if (longValue != null) {
return DatumType.LONG;
} else if (doubleValue != null) {
return DatumType.DOUBLE;
} else {
return super.getType();
}
}
private double getAsDouble() {
if (doubleValue == null) {
doubleValue = super.asDouble();
}
return doubleValue;
}
private long getAsLong() {
if (longValue == null) {
longValue = super.asLong();
}
return longValue;
}
}