uk.ac.starlink.gbin.DefaultGbinTableProfile Maven / Gradle / Ivy
package uk.ac.starlink.gbin;
import java.lang.reflect.Array;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Default implementation of GbinTableProfile.
*
* @author Mark Taylor
* @since 3 Sep 2015
*/
public class DefaultGbinTableProfile implements GbinTableProfile {
private final Map,Representation>> reprMap_;
private static final Map,Class>> primMap_ =
Collections.unmodifiableMap( createPrimitiveMap() );
/**
* Constructs a profile with default configuration.
*/
public DefaultGbinTableProfile() {
this( new Class>[] { String.class, },
new Class>[] { Class.class, },
new Class>[] {} );
}
/**
* Constructs a profile with explicit configuration.
* Any classes not referred to in the supplied lists,
* apart from primitive classes which are always used,
* will be treated as elements in the object hierarchy
* and descended into recursively.
*
* @param simpleClasses list of scalar Object subclasses that
* are suitable contents of a column as they stand
* @param stringClasses list of scalar Object subclasses that
* can be used as string contents of a column
* by invoking their toString
method
* @param ignoreClasses classes which should be ignored completely
* when converting to a table
*/
public DefaultGbinTableProfile( Class>[] simpleClasses,
Class>[] stringClasses,
Class>[] ignoreClasses ) {
reprMap_ = new LinkedHashMap,Representation>>();
for ( Class> primClazz : primMap_.keySet() ) {
Class> wrapperClazz = primMap_.get( primClazz );
Class> arrayClazz = Array.newInstance( primClazz, 0 ).getClass();
Representation> wrapperRepr =
createSimpleColumnRepresentation( wrapperClazz );
reprMap_.put( primClazz, wrapperRepr );
reprMap_.put( wrapperClazz, wrapperRepr );
reprMap_.put( arrayClazz,
createSimpleColumnRepresentation( arrayClazz ) );
}
for ( Class> clazz : simpleClasses ) {
reprMap_.put( clazz, createSimpleColumnRepresentation( clazz ) );
}
for ( Class> clazz : stringClasses ) {
reprMap_.put( clazz, createStringColumnRepresentation( clazz ) );
}
for ( Class> clazz : ignoreClasses ) {
reprMap_.put( clazz, null );
}
}
public boolean isReadMeta() {
return true;
}
public boolean isTestMagic() {
return true;
}
public boolean isHierarchicalNames() {
return false;
}
public String getNameSeparator() {
return "_";
}
public boolean isSortedMethods() {
return true;
}
public String[] getIgnoreMethodNames() {
return new String[] {
"getClass",
"getField",
"getStringValue",
"getGTDescription",
"getParamMaxValues",
"getParamMinValues",
"getParamOutOfRangeValues",
"getFieldNames",
};
}
/**
* This implementation returns the classnames
* gaia.cu1.tools.dm.GaiaRoot
and
* gaia.cu1.tools.dmimpl.GaiaRootImpl
.
* This behaviour addresses an issue that shows up in versions of
* GaiaTools 19.4.*, >=20.1.0 and >=21.0.0,
* where invoking the GaiaRoot getSolutionId
* method throws an exception.
*
* The getSolutionId
method is present in all
* GaiaRoot
subtypes, but in some cases no
* solutionId
field is defined.
* In that circumstance older GaiaTools versions simply returned
* a dummy value Long.MIN_VALUE, but more recent versions throw
* an exception, which caused the GbinStarTable construction to fail.
*
*
Avoiding attempts to turn the getSolutionId
method
* into a GbinStarTable column can be done by checking whether
* the class declaring the method is GaiaRoot/GaiaRootImpl,
* as provided here. This fix was suggested by Alexander Hutton (CU1).
*/
public String[] getIgnoreMethodDeclaringClasses() {
return new String[] {
"gaia.cu1.tools.dm.GaiaRoot",
"gaia.cu1.tools.dmimpl.GaiaRootImpl",
};
}
public Representation> createRepresentation( Class> clazz ) {
/* If we have a specific representation for this type, use it. */
if ( reprMap_.containsKey( clazz ) ) {
return reprMap_.get( clazz );
}
/* Arrays need treating specially. */
else if ( clazz.isArray() ) {
Class> scalarClass = getScalarClass( clazz );
if ( reprMap_.containsKey( scalarClass ) ) {
Representation> scalarRepr = reprMap_.get( scalarClass );
/* If the scalar type is a structured object
* (either appears in the map of known types with
* isColumn=false, or implicitly structured by being absent
* from that map),
* there's not much we can do with it, since you
* can't map that structure to a sequence of columns
* (you don't know how many there would be).
* So we have to discard it. */
if ( scalarRepr == null || ! scalarRepr.isColumn() ) {
return null;
}
/* If the scalar type is a column type, assume that it's
* OK for an array (or array of arrays, or ...) to be
* a column too. Note however that this code is not
* quite right at the moment; it will work for identity
* representations, but ones that convert input objects
* to a different representation (e.g. toString) will
* go wrong here. */
else {
return createSimpleColumnRepresentation( clazz );
}
}
else {
return null;
}
}
/* Non-array types with no special instructions will be treated
* as structured objects. */
else {
return createIdentityRepresentation( clazz, false );
}
}
/**
* Returns a scalar, or scalar-like, class based on a class which
* may be an array, or array of arrays, or ... of some class.
*
* @param clazz class to test
* @return a class obtained by stripping "[]"s from the given type
* which either has a known representation or doesn't
* have any more "[]"s to strip
*/
private Class> getScalarClass( Class> clazz ) {
if ( reprMap_.containsKey( clazz ) ) {
return clazz;
}
else if ( clazz.isArray() ) {
return getScalarClass( clazz.getComponentType() );
}
else {
return clazz;
}
}
/**
* Returns a representation that uses the input values unchanged.
*
* @param clazz input type
* @param isColumn whether this represents a column or not
* @return representation of column or structured object
*/
public static Representation
createIdentityRepresentation( final Class clazz,
final boolean isColumn ) {
return new Representation() {
public Class getContentClass() {
return clazz;
}
public T representValue( Object value ) {
return clazz.isInstance( value )
? clazz.cast( value )
: null;
}
public boolean isColumn() {
return isColumn;
}
};
}
/**
* Returns a column-like representation that uses the
* input values unchanged.
*
* @param clazz input type
* @return representation of column
*/
private static Representation
createSimpleColumnRepresentation( Class clazz ) {
return createIdentityRepresentation( clazz, true );
}
/**
* Returns a representation that maps its input values to strings
* using their toString method, and uses the result like a column.
*
* @param clazz input type
* @return representation of column
*/
public static Representation
createStringColumnRepresentation( Class> clazz ) {
return new Representation() {
public Class getContentClass() {
return String.class;
}
public String representValue( Object value ) {
return value == null ? null : value.toString();
}
public boolean isColumn() {
return true;
}
};
}
/**
* Constructs a map of all existing primtive classes to their
* corresponding wrapper classes.
*
* @return primitive->wrapper class map
*/
private static Map,Class>> createPrimitiveMap() {
Map,Class>> map = new LinkedHashMap,Class>>();
map.put( boolean.class, Boolean.class );
map.put( char.class, Character.class );
map.put( byte.class, Byte.class );
map.put( short.class, Short.class );
map.put( int.class, Integer.class );
map.put( long.class, Long.class );
map.put( float.class, Float.class );
map.put( double.class, Double.class );
return map;
}
}