uk.ac.starlink.feather.FeatherStarTable Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of stil Show documentation
Show all versions of stil Show documentation
Starlink Tables Infrastructure Library
package uk.ac.starlink.feather;
import java.io.File;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
import org.json.JSONException;
import org.json.JSONObject;
import uk.ac.bristol.star.feather.Decoder;
import uk.ac.bristol.star.feather.FeatherColumn;
import uk.ac.bristol.star.feather.FeatherTable;
import uk.ac.bristol.star.feather.FeatherType;
import uk.ac.bristol.star.feather.Reader;
import uk.ac.starlink.table.AbstractStarTable;
import uk.ac.starlink.table.ColumnInfo;
import uk.ac.starlink.table.DefaultValueInfo;
import uk.ac.starlink.table.DescribedValue;
import uk.ac.starlink.table.RowAccess;
import uk.ac.starlink.table.RowSequence;
import uk.ac.starlink.table.Tables;
import uk.ac.starlink.table.ValueInfo;
/**
* Adaptor from FeatherTable to StarTable.
*
* @author Mark Taylor
* @since 26 Feb 2020
*/
public class FeatherStarTable extends AbstractStarTable {
private final FeatherTable ftable_;
private final int ncol_;
private final long nrow_;
private final String name_;
private final FeatherColumn[] fcols_;
private final ColumnInfo[] colInfos_;
private final RowReader randomReader_;
/** JSON key used to store UCDs in column user metadata. */
public static final String UCD_KEY = "ucd";
/** JSON key used to store Utypes in column user metadata. */
public static final String UTYPE_KEY = "utype";
/** JSON key used to store units in column user metadata. */
public static final String UNIT_KEY = "unit";
/** JSON key used to store description text in column user metadata. */
public static final String DESCRIPTION_KEY = "description";
/** JSON key to store stringified array shape in column user metadata. */
public static final String SHAPE_KEY = "shape";
/** JSON key to store miscellaneous/broken metadata in column metadata. */
public static final String META_KEY = "meta";
/** Aux metadata key for column feather type value. */
public static final ValueInfo FTYPE_INFO =
new DefaultValueInfo( "feather_type", String.class,
"Data type code from Feather format input file" );
/**
* Constructs a FeatherStarTable from a FeatherTable.
*
* @param ftable feather table object
*/
public FeatherStarTable( FeatherTable ftable ) {
ftable_ = ftable;
ncol_ = ftable.getColumnCount();
nrow_ = ftable.getRowCount();
name_ = ftable.getDescription();
fcols_ = new FeatherColumn[ ncol_ ];
colInfos_ = new ColumnInfo[ ncol_ ];
for ( int icol = 0; icol < ncol_; icol++ ) {
fcols_[ icol ] = ftable.getColumn( icol );
colInfos_[ icol ] = createColumnInfo( fcols_[ icol ] );
}
randomReader_ = new RowReader();
}
/**
* Constructs a FeatherStarTable from a File.
*
* @param file file
*/
public FeatherStarTable( File file ) throws IOException {
this( FeatherTable.fromFile( file ) );
}
public int getColumnCount() {
return ncol_;
}
public long getRowCount() {
return nrow_;
}
public boolean isRandom() {
return true;
}
public String getName() {
return name_;
}
public ColumnInfo getColumnInfo( int icol ) {
return colInfos_[ icol ];
}
public Object getCell( long irow, int icol ) throws IOException {
return randomReader_.getCell( irow, icol );
}
public Object getRow( long irow, int icol ) throws IOException {
return randomReader_.getRow( irow );
}
public RowSequence getRowSequence() {
final RowReader rowReader = new RowReader();
return new RowSequence() {
long irow_ = -1;
boolean hasData_ = false;
public boolean next() {
if ( irow_ < nrow_ - 1 ) {
irow_++;
hasData_ = true;
}
else {
hasData_ = false;
}
return hasData_;
}
public Object getCell( int icol ) throws IOException {
if ( hasData_ ) {
return rowReader.getCell( irow_, icol );
}
else {
throw new IllegalStateException();
}
}
public Object[] getRow() throws IOException {
if ( hasData_ ) {
return rowReader.getRow( irow_ );
}
else {
throw new IllegalStateException();
}
}
public void close() {
}
};
}
public RowAccess getRowAccess() {
final RowReader rowReader = new RowReader();
return new RowAccess() {
long irow_ = -1;
public void setRowIndex( long irow ) {
irow_ = irow;
}
public Object getCell( int icol ) throws IOException {
return rowReader.getCell( irow_, icol );
}
public Object[] getRow() throws IOException {
return rowReader.getRow( irow_ );
}
public void close() {
}
};
}
/**
* Does nothing, but perhaps it ought to try to clear up resources
* related to the mapped buffers.
*/
@Override
public void close() throws IOException {
}
/**
* Adapts a FeatherColumn to a ColumnInfo.
*
* @param fcol feather column object
* @return column metadata
*/
private static ColumnInfo createColumnInfo( FeatherColumn fcol ) {
Decoder> decoder = fcol.getDecoder();
Class> clazz = decoder.getValueClass();
FeatherType ftype = decoder.getFeatherType();
ColumnInfo info = new ColumnInfo( fcol.getName(), clazz, null );
info.setNullable( fcol.getNullCount() > 0 );
Map metaMap = getColumnMetaMap( fcol.getUserMeta() );
for ( Map.Entry entry : metaMap.entrySet() ) {
String key = entry.getKey();
String value = entry.getValue();
if ( key.equals( UCD_KEY ) ) {
info.setUCD( value );
}
if ( key.equals( UTYPE_KEY ) ) {
info.setUtype( value );
}
if ( key.equals( UNIT_KEY ) ) {
info.setUnitString( value );
}
if ( key.equals( DESCRIPTION_KEY ) ) {
info.setDescription( value );
}
if ( key.equals( SHAPE_KEY ) ) {
info.setShape( DefaultValueInfo.unformatShape( value ) );
}
}
info.setAuxDatum( new DescribedValue( FTYPE_INFO, ftype.toString() ) );
if ( ftype == FeatherType.UINT8 && clazz.equals( Short.class ) ) {
info.setAuxDatum( new DescribedValue( Tables.UBYTE_FLAG_INFO,
Boolean.TRUE ) );
}
return info;
}
/**
* Turns a column user metadata string into a String->String map.
* The input string is assumed or hoped to be in JSON format.
* If it has been written by this package, it will contain well-known
* keys representing normal column metadata.
*
* @param userMeta user metadata text, hopefully JSON
* @return key-value map
*/
private static Map getColumnMetaMap( String userMeta ) {
Map map = new LinkedHashMap();
if ( userMeta != null && userMeta.trim().length() > 0 ) {
try {
JSONObject json = new JSONObject( userMeta );
for ( String key : json.keySet() ) {
if ( key.equals( UCD_KEY ) ||
key.equals( UTYPE_KEY ) ||
key.equals( UNIT_KEY ) ||
key.equals( DESCRIPTION_KEY ) ||
key.equals( SHAPE_KEY ) ) {
map.put( key, json.get( key ).toString() );
}
}
}
catch ( JSONException e ) {
map.put( META_KEY, userMeta );
}
}
return map;
}
/**
* Row/column access object that can acquire data values from table.
*/
private class RowReader {
final Reader>[] rdrs_ = new Reader>[ ncol_ ];
/**
* Returns a reader object for a given column.
*
* @param icol column index
* @return column reader
*/
Reader> getReader( int icol ) throws IOException {
Reader> rdr = rdrs_[ icol ];
if ( rdr != null ) {
return rdr;
}
else {
rdrs_[ icol ] = fcols_[ icol ].createReader();
return rdrs_[ icol ];
}
}
/**
* Returns a cell value.
*
* @param irow row index
* @param icol column index
* @return cell value
*/
Object getCell( long irow, int icol ) throws IOException {
return getReader( icol ).getObject( irow );
}
/**
* Returns an array of objects giving the cells in a row.
*
* @param irow row index
* @return cell value array
*/
Object[] getRow( long irow ) throws IOException {
Object[] row = new Object[ ncol_ ];
for ( int ic = 0; ic < ncol_; ic++ ) {
row[ ic ] = getReader( ic ).getObject( irow );
}
return row;
}
}
}