/*
* Created on Dec 31, 2004 at the Interface Ecology Lab.
*/
package ecologylab.serialization.types.scalar;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.HashMap;
import ecologylab.generic.text.EfficientDecimalFormat;
import ecologylab.serialization.FieldDescriptor;
import ecologylab.serialization.ScalarUnmarshallingContext;
import ecologylab.serialization.TranslationContext;
import ecologylab.serialization.annotations.simpl_inherit;
import ecologylab.serialization.formatenums.Format;
import ecologylab.serialization.types.CrossLanguageTypeConstants;
import ecologylab.serialization.types.ScalarType;
/**
* Type system entry for double, a built-in primitive.
*
* @author andruid
*/
@simpl_inherit
public class DoubleType extends ScalarType<Double> implements CrossLanguageTypeConstants
{
public static final double DEFAULT_VALUE = 0;
public static final String DEFAULT_VALUE_STRING = "0.0";
/** The map of format Strings to their associated decimal formats. */
private static final HashMap<String, EfficientDecimalFormat> formatMap = new HashMap<String, EfficientDecimalFormat>();
/**
* This constructor should only be called once per session, through a static initializer,
* typically in TypeRegistry.
* <p>
* To get the instance of this type object for use in translations, call
* <code>TypeRegistry.get("double")</code>.
*
*/
public DoubleType()
{
this(double.class);
}
public DoubleType(Class<Double> thatClass)
{
super(thatClass, DOTNET_DOUBLE, OBJC_DOUBLE, null);
}
/**
* Convert the parameter to double.
*/
public double getValue(String valueString)
{
return Double.parseDouble(valueString);
}
/**
* Parse the String into the (primitive) type, and return a boxed instance.
*
* @param value
* String representation of the instance.
*/
@Override
public Double getInstance(String value, String[] formatStrings,
ScalarUnmarshallingContext scalarUnmarshallingContext)
{
return "null".equalsIgnoreCase(value) ? null : (value != null && value.contains("/")) ? rationalToDouble(value) : new Double(value);
}
public static double rationalToDouble(String rationalString)
{
String[] stringD = rationalString.split("/", 2);
double d0 = Double.parseDouble(stringD[0]);
double d1 = Double.parseDouble(stringD[1]);
return d0 / d1;
}
/**
* This is a primitive type, so we set it specially.
*
* @see ecologylab.serialization.types.ScalarType#setField(Object, Field, String)
*/
@Override
public boolean setField(Object object, Field field, String value)
{
boolean result = false;
try
{
field.setDouble(object, getValue(value));
result = true;
}
catch (Exception e)
{
setFieldError(field, value, e);
}
return result;
}
/**
* The string representation for a Field of this type
*/
@Override
public String toString(Field field, Object context)
{
String result = "COULDN'T CONVERT!";
try
{
result = Double.toString((Double) field.get(context));
}
catch (Exception e)
{
e.printStackTrace();
}
return result;
}
/**
* The default value for this type, as a String. This value is the one that translateToXML(...)
* wont bother emitting.
*
* @return "0"
*/
@Override
public String defaultValueString()
{
return DEFAULT_VALUE_STRING;
}
/**
* Since DoubleType is a floating point value, returns true.
*
* @return true
*/
@Override
public boolean isFloatingPoint()
{
return true;
}
/**
* True if the value in the Field object matches the default value for this type.
*
* @param field
* @return
*/
@Override
public boolean isDefaultValue(Field field, Object context) throws IllegalArgumentException,
IllegalAccessException
{
return (Double) field.get(context) == DEFAULT_VALUE;
}
/**
* Get the value from the Field, in the context. Append its value to the buffy.
*
* @param buffy
* @param field
* @param context
* @param formatStrings
* an array of 0 or 1 items containing a pattern String that specifies how the value will
* be emitted. Patterns are specified using {@link java.text.DecimalFormat}.
* @throws IllegalAccessException
* @throws IllegalArgumentException
* @see java.text.DecimalFormat
*/
@Override
public void appendValue(StringBuilder buffy, FieldDescriptor f2xo, Object context)
throws IllegalArgumentException, IllegalAccessException
{
buffy.append(getValueToAppend(f2xo, context));
}
/**
* Get the value from the Field, in the context. Append its value to the buffy.
*
* @param buffy
* @param context
* @param field
* @throws IllegalAccessException
* @throws IllegalArgumentException
* @throws IOException
*/
@Override
public void appendValue(Appendable buffy, FieldDescriptor fieldDescriptor, Object context,
TranslationContext serializationContext, Format format) throws IllegalArgumentException,
IllegalAccessException, IOException
{
buffy.append(getValueToAppend(fieldDescriptor, context));
}
public static String getValueToAppend(FieldDescriptor fieldDescriptor, Object context)
throws IllegalArgumentException, IllegalAccessException
{
if (fieldDescriptor.getField() == null || fieldDescriptor.getField().get(context) == null)
return "null";
double value = (Double) fieldDescriptor.getField().get(context);
String[] formatStrings = fieldDescriptor.getFormat();
StringBuilder res = new StringBuilder();
if (formatStrings != null)
{
EfficientDecimalFormat decFormat = getFormat(formatStrings[0]);
try
{
decFormat.format(value, res);
}
catch (IOException e)
{
e.printStackTrace();
}
}
else
{
res.append(Double.toString(value));
}
return res.toString();
}
private static EfficientDecimalFormat getFormat(String formatString)
{
EfficientDecimalFormat decFormat = formatMap.get(formatString);
if (decFormat == null)
{
synchronized (formatMap)
{
decFormat = formatMap.get(formatString);
if (decFormat == null)
{
decFormat = new EfficientDecimalFormat(formatString);
formatMap.put(formatString, decFormat);
}
}
}
return decFormat;
}
}