All Downloads are FREE. Search and download functionalities are using the official Maven repository.

uk.ac.starlink.votable.Decoder Maven / Gradle / Ivy

package uk.ac.starlink.votable;

import java.io.DataInput;
import java.io.IOException;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Logger;
import uk.ac.starlink.util.IOUtils;

/**
 * Decoder object associated with a Field.
 * Instances of this class know about the size and shape of fields
 * as well as their numeric type, and can decode various data sources
 * into the objects they represent.  To construct a decoder use the
 * static {@link #makeDecoder} method.
 *
 * 

The various decode methods turn some kind of representation of * the given object into a standard representation. The standard * representation is in accordance with the recommendations made in * the the {@link uk.ac.starlink.table} package. * * @author Mark Taylor (Starlink) */ abstract class Decoder { static Logger logger = Logger.getLogger( "uk.ac.starlink.votable" ); static final long[] SCALAR_SIZE = new long[ 0 ]; protected String blankString; protected boolean isVariable; protected int sliceSize; protected long[] arraysize; private final Class clazz; /** * Sets the null (bad) value to be used by the decoder from a string. * * @param txt a string representation of the bad value * @throws IllegalArgumentException if the string cannot be parsed */ abstract void setNullValue( String txt ); /** * Returns an object array based on the given text (space-separated * values for numeric types, normal string values for characters). * * @param txt a string encoding one or many values * @return an object containing the decoded values */ abstract public Object decodeString( String txt ); /** * Returns an object array read from the next bit of a given input * stream as raw bytes. The VOTable BINARY/BINARY2 format is used. * * @param strm a DataInput object from which bytes are to be read */ abstract public Object decodeStream( DataInput strm ) throws IOException; /** * Skips over the bytes in a stream corresponding to a single cell. * The effect is the same as calling {@link #decodeStream}, but no * data is returned. * * @param strm a DataInput object from which bytes are to be read */ abstract public void skipStream( DataInput strm ) throws IOException; /** * Indicates whether an element of a given array matches the Null value * used by this decoder. * * @param array the array in which the element to check is * @param index the index into array at which the element to * check is * @return true iff the index'th element of array * matches the Null value for this decoder */ abstract public boolean isNull( Object array, int index ); /** * Does required setup for a decoder given its shape. * * @param clazz the class to which all objects returned by the * decode* methods will belong * @param arraysize the dimensions of objects with this type - * the last element of the array may be negative to * indicate unknown slowest-varying dimension */ protected Decoder( Class clazz, long[] arraysize ) { this.clazz = clazz; this.arraysize = arraysize; int ndim = arraysize.length; if ( ndim == 0 ) { isVariable = false; sliceSize = 1; } else if ( arraysize[ ndim - 1 ] < 0 ) { isVariable = true; long ss = 1; for ( int i = 0; i < ndim - 1; i++ ) { ss *= arraysize[ i ]; } sliceSize = (int) ss; } else { isVariable = false; long ss = 1; for ( int i = 0; i < ndim; i++ ) { ss *= arraysize[ i ]; } sliceSize = (int) ss; } } /** * Returns the class for objects returned by this decoder. * Objects returned by the decode* methods of this decoder * will be instances of the class returned by this method, or null. * * @param returned object class */ public Class getContentClass() { return clazz; } /** * Returns the number of cells to use for this Decoder given a * certain number of available tokens. This is just the arraysize * if it is fixed, or some large-enough multiple of the slice size * if it is variable. * * @param ntok the number of available tokens * @return the number of cells to fill */ int numCells( int ntok ) { if ( isVariable ) { return ( ( ntok + sliceSize - 1 ) / sliceSize ) * sliceSize; } else { return sliceSize; } } /** * Work out how many items are to be read from the supplied input stream. * This may be a fixed number for this Decoder, or it may require * reading a count from the stream. */ int getNumItems( DataInput strm ) throws IOException { return isVariable ? strm.readInt() : sliceSize; } /** * Gets the shape of items returned by this decoder. By default this * is the same as the arraysize, but decoders may * change the shape from that defined by the arraysize attribute * of the FIELD element. In particular, the char and * unicodeChar decoders package an array of characters as * a String. * * @return the shape of objects returned by this decoder. * The last element might be negative to indicate variable size */ public long[] getDecodedShape() { return arraysize; } /** * Gets the 'element size' of items returned by this decoder. * This has the same meaning as * {@link uk.ac.starlink.table.ValueInfo#getElementSize}; * the Decoder implementation returns -1, but character-type decoders * override this. * * @return notional size of each element an array of values decoded * by this object, or -1 if unknown */ public int getElementSize() { return -1; } /** * Skips over a given number of bytes in a stream without reading them. * * @param strm stream * @return byte count */ static void skipBytes( DataInput strm, long num ) throws IOException { IOUtils.skipBytes( strm, num ); } /** * Create a decoder given its datatype, shape and blank (bad) value. * The shape is specified by the arraysize parameter, * which gives array dimensions. The last element of this array * may be negative to indicate an unknown last (slowest varying) * dimension. * *

All the decoders named in the VOTable 1.0 document are supported: *

    *
  • boolean *
  • bit *
  • unsignedByte *
  • short *
  • int *
  • long *
  • char *
  • unicodeChar *
  • float *
  • double *
  • floatComplex *
  • doubleComplex *
* * @param datatype the datatype name, that is the value of the * VOTable "datatype" attribute * @param arraysize shape of the array * @param blank a string giving the bad value * @return a Decoder object capable of decoding values according to * its name and shape */ public static Decoder makeDecoder( String datatype, long[] arraysize, String blank ) { /* Work out if we have an effectively scalar quantity (either an * actual scalar or an array with one element. */ boolean isScalar; int ndim = arraysize.length; if (ndim == 0) { isScalar = true; } else if ( arraysize[ ndim - 1 ] > 0 ) { int nel = 1; for ( int i = 0; i < ndim; i++ ) { nel *= arraysize[ i ]; } isScalar = nel == 1; } else { isScalar = false; } /* Construct a decoder for the arraysize and datatype. */ Decoder dec; if ( datatype.equals( "boolean" ) ) { dec = isScalar ? BooleanDecoder.createScalarBooleanDecoder() : new BooleanDecoder( arraysize ); } else if ( datatype.equals( "bit" ) ) { dec = isScalar ? BitDecoder.createScalarBitDecoder() : new BitDecoder( arraysize ); } else if ( datatype.equals( "unsignedByte" ) ) { dec = isScalar ? new NumericDecoder.ScalarUnsignedByteDecoder() : new NumericDecoder.UnsignedByteDecoder( arraysize); } else if ( datatype.equals( "short" ) ) { dec = isScalar ? new NumericDecoder.ScalarShortDecoder() : new NumericDecoder.ShortDecoder( arraysize ); } else if ( datatype.equals( "int" ) ) { dec = isScalar ? new NumericDecoder.ScalarIntDecoder() : new NumericDecoder.IntDecoder( arraysize ); } else if ( datatype.equals( "long" ) ) { dec = isScalar ? new NumericDecoder.ScalarLongDecoder() : new NumericDecoder.LongDecoder( arraysize ); } else if ( datatype.equals( "char" ) ) { dec = CharDecoders.makeCharDecoder( arraysize ); } else if ( datatype.equals( "unicodeChar" ) ) { dec = CharDecoders.makeUnicodeCharDecoder( arraysize ); } else if ( datatype.equals( "float" ) ) { dec = isScalar ? new NumericDecoder.ScalarFloatDecoder() : new NumericDecoder.FloatDecoder( arraysize ); } else if ( datatype.equals( "double" ) ) { dec = isScalar ? new NumericDecoder.ScalarDoubleDecoder() : new NumericDecoder.DoubleDecoder( arraysize ); } else if ( datatype.equals( "floatComplex" ) ) { long[] arraysize2 = new long[ arraysize.length + 1 ]; arraysize2[ 0 ] = 2L; System.arraycopy( arraysize, 0, arraysize2, 1, arraysize.length ); dec = new NumericDecoder.FloatDecoder( arraysize2 ); } else if ( datatype.equals( "doubleComplex" ) ) { long[] arraysize2 = new long[ arraysize.length + 1 ]; arraysize2[ 0 ] = 2L; System.arraycopy( arraysize, 0, arraysize2, 1, arraysize.length ); dec = new NumericDecoder.DoubleDecoder( arraysize2 ); } else { logger.warning( "Unknown data type " + datatype + " - treat as string" + ", but may cause problems" ); dec = new UnknownDecoder(); } /* Set the blank value. */ if ( blank != null && blank.trim().length() > 0 ) { try { dec.setNullValue( blank ); } catch ( IllegalArgumentException e ) { logger.warning( "Bad null value " + blank ); } } /* Return the new Decoder object. */ return dec; } private static class UnknownDecoder extends Decoder { public UnknownDecoder() { super( String.class, new long[ 0 ] ); } public Object decodeString( String txt ) { return txt; } public Object decodeStream( DataInput strm ) { throw new UnsupportedOperationException( "Can't do STREAM decode of unknown data type " + this ); } public void skipStream( DataInput strm ) { decodeStream( strm ); } void setNullValue( String txt ) {} public boolean isNull( Object array, int index ) { return false; } } static int[] longsToInts( long[] larray ) { int[] iarray = new int[ larray.length ]; for ( int i = 0; i < larray.length; i++ ) { if ( larray[ i ] < Integer.MIN_VALUE || larray[ i ] > Integer.MAX_VALUE ) { throw new IndexOutOfBoundsException( "Long value " + larray[ i ] + " out of integer range" ); } else { iarray[ i ] = (int) larray[ i ]; } } return iarray; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy