/**
*
*/
package org.yamcs.yarch;
import org.yamcs.parameter.ParameterValue;
/**
* Types supported by yarch. Currently TUPLE and LIST do now work well.
* ENUM is just like String, except that when it's stored on disk a two bytes integer value from a map is stored instead of the String.
* (maximum allowed version is 2^16 (which is anyway too big considering that the map is stored as serialised yaml file)
*
* PROTOBUF is a Google Protocol Buffer message
* @author nm
*
*/
public class DataType {
private static final long serialVersionUID = 201101181144L;
public enum _type {BYTE, SHORT, INT, DOUBLE, TIMESTAMP, STRING, BINARY, BOOLEAN, ENUM, PROTOBUF, PARAMETER_VALUE, TUPLE, LIST}
public final _type val;
public static final DataType BYTE = new DataType(_type.BYTE);
public static final DataType SHORT = new DataType(_type.SHORT);
public static final DataType INT = new DataType(_type.INT);
public static final DataType DOUBLE = new DataType(_type.DOUBLE);
public static final DataType STRING = new DataType(_type.STRING);
public static final DataType BINARY = new DataType(_type.BINARY);
public static final DataType BOOLEAN = new DataType(_type.BOOLEAN);
public static final DataType TIMESTAMP = new DataType(_type.TIMESTAMP);
public static final DataType ENUM = new DataType(_type.ENUM);
public static final DataType PARAMETER_VALUE = new DataType(_type.PARAMETER_VALUE);
protected DataType(_type t) {
this.val = t;
}
public static DataType tuple(TupleDefinition td) {
return new TupleDataType(td);
}
public static DataType list(TupleDefinition td) {
return new ListDataType(td);
}
public static DataType protobuf(String className) {
return new ProtobufDataType(className);
}
/**
* this is the inverse of {@link #name()}
* @param name
* @return the DataType corresponding to the name
* @throws IllegalArgumentException thrown in case the name is invalid
*/
public static DataType byName(String name) throws IllegalArgumentException {
if(name==null) {
throw new NullPointerException();
}
if("BYTE".equals(name)){
return BYTE;
}
if("SHORT".equals(name)){
return SHORT;
}
if("INT".equals(name)){
return INT;
}
if("DOUBLE".equals(name)){
return DOUBLE;
}
if("STRING".equals(name)){
return STRING;
}
if("BINARY".equals(name)){
return BINARY;
}
if("BOOLEAN".equals(name)){
return BOOLEAN;
}
if("TIMESTAMP".equals(name)){
return TIMESTAMP;
}
if("ENUM".equals(name)) {
return ENUM;
}
if("PARAMETER_VALUE".equals(name)){
return PARAMETER_VALUE;
}
if(name.toUpperCase().startsWith("PROTOBUF(")) {
return protobuf(name.substring(9, name.length()-1));
}
throw new IllegalArgumentException("invalid or unsupported DataType '"+name+"'");
}
/*returns Int, Short, etc suitable to use as getInt(), getShort() on the Object*/
public static String capitalized(String s) {
String t=s.toString();
return t.substring(0,1).toUpperCase()+t.substring(1).toLowerCase();
}
public String javaType() {
switch(val) {
case BOOLEAN:
case BYTE:
case DOUBLE:
case SHORT:
case STRING:
return capitalized(val.toString());
case BINARY:
return "byte[]";
case TIMESTAMP:
return "Long";
case ENUM:
return "String";
case INT:
return "Integer";
case PARAMETER_VALUE:
return "ParameterValue";
}
return null;
}
public String primitiveJavaType() {
switch(val) {
case BOOLEAN:
case BYTE:
case DOUBLE:
case SHORT:
case INT:
return val.toString().toLowerCase();
case TIMESTAMP:
return "long";
default:
throw new IllegalStateException("no primitive java type for "+val);
}
}
@Override
public String toString() {
return val.toString();
}
/**
* Returns type as string.
*
*
* @return for basic types returns the enum name
* for PROTOBUF returns PROTOBUF(className)
*/
public String name() {
return val.name();
}
public static DataType typeOf(Object v) {
if(v instanceof Boolean) {
return BOOLEAN;
} else if(v instanceof Byte) {
return BYTE;
} else if(v instanceof Short) {
return SHORT;
} else if(v instanceof Integer) {
return INT;
} else if(v instanceof Double) {
return DOUBLE;
} else if(v instanceof Long) {
return TIMESTAMP;
} else if(v instanceof String) {
return STRING;
} else if(v instanceof byte[]) {
return BINARY;
} else if(v instanceof ParameterValue) {
return PARAMETER_VALUE;
} else {
throw new IllegalArgumentException("invalid or unsupported object of type of "+v.getClass());
}
}
public static int compare(Object v1, Object v2) {
if(v1 instanceof Boolean) {
return ((Boolean)v1).compareTo((Boolean)v2);
} else if(v1 instanceof Byte) {
return ((Byte)v1).compareTo((Byte)v2);
} else if(v1 instanceof Short) {
return ((Short)v1).compareTo((Short)v2);
} else if(v1 instanceof Integer) {
return ((Integer)v1).compareTo((Integer)v2);
} else if(v1 instanceof Double) {
return ((Double)v1).compareTo((Double)v2);
} else if(v1 instanceof Long) {
return ((Long)v1).compareTo((Long)v2);
} else if(v1 instanceof String) {
return ((String)v1).compareTo((String)v2);
} else {
throw new IllegalArgumentException("cannot compare objects of type "+v1.getClass());
}
}
/**
* Performs casting of v from type1 to type2
*
* @param sourceType
* @param targetType
* @param v
* @return the casted object (can be v if no casting is performed)
* @throws IllegalArgumentException
*/
public static Object castAs(DataType sourceType, DataType targetType, Object v) throws IllegalArgumentException {
if(sourceType.equals(targetType)) {
return v;
}
if(v instanceof Number) {
Number n = (Number)v;
switch(targetType.val) {
case BYTE:
return n.byteValue();
case DOUBLE:
return n.doubleValue();
case SHORT:
return n.shortValue();
case INT:
return n.intValue();
case TIMESTAMP:
return n.longValue();
case STRING:
case ENUM:
return n.toString();
}
} else if(v instanceof String) {
String s = (String)v;
switch(targetType.val) {
case BYTE:
return Byte.decode(s);
case DOUBLE:
return Double.valueOf(s);
case SHORT:
return Short.decode(s);
case INT:
return Integer.decode(s);
case TIMESTAMP:
return Long.decode(s);
case ENUM:
return s;
}
}
throw new IllegalArgumentException("Cannot convert '"+v+"' from "+sourceType+" into "+targetType);
}
/**
* Performs casting:
* numbers to numbers
* numbers to string
* string to numbers
* @param targetType
* @param v
* @return the casted object (can be to v if no casting is performed)
* @throws IllegalArgumentException
*
*/
public static Object castAs(DataType targetType, Object v) throws IllegalArgumentException {
return castAs(typeOf(v), targetType, v);
}
public static boolean isNumber(DataType dt) {
switch(dt.val) {
case BYTE:
case DOUBLE:
case INT:
case SHORT:
case TIMESTAMP:
return true;
default:
return false;
}
}
public static boolean compatible(DataType dt1, DataType dt2) {
if(dt1==dt2) {
return true;
}
if(isNumber(dt1) && isNumber(dt2)){
return true;
}
if(dt1.val == _type.STRING || dt1.val == _type.ENUM) {
return dt2.val==_type.STRING || dt2.val==_type.ENUM;
}
return false;
}
}