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

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

There is a newer version: 4.3
Show newest version
package uk.ac.starlink.votable;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
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.StarTable;
import uk.ac.starlink.table.Tables;
import uk.ac.starlink.table.URLValueInfo;
import uk.ac.starlink.table.ValueInfo;
import uk.ac.starlink.util.DOMUtils;
import uk.ac.starlink.votable.datalink.ServiceDescriptor;
import uk.ac.starlink.votable.datalink.ServiceDescriptorFactory;
import uk.ac.starlink.votable.datalink.ServiceDescriptorInfo;

/**
 * A {@link uk.ac.starlink.table.StarTable} implementation based on a VOTable.
 *
 * 

Some of the FIELD attributes defined by the VOTable format * correspond to standard information in the corresponding ColumnInfo * object, and some do not. Those that do are accessed using the * relevant ColumnInfo getter/setter methods directly, for instance *

 *     String ucd = table.getColumnInfo(0).getUCD();
 * 
* The ones that don't are stored in the ColumnInfo's auxiliary metadata * keyed using the various *_INFO public static variables defined * in this class. These are accessed using the * {@link uk.ac.starlink.table.ColumnInfo#getAuxDatum} method, for instance: *
 *     String id = (String) table.getColumnInfo(0)
 *                               .getAuxDatumValue(VOStarTable.ID_INFO);
 * 
* In the same way, if you set an auxiliary metadata item under one of * these keys, like this: *
 *     DescribedValue idVal = new DescribedValue(VOStarTable.ID_INFO, "COL0");
 *     table.getColumnInfo(0).setAuxDatum(idVal);
 * 
* then if the result is written to a VOTable the relevant attribute * will be attached to the corresponding FIELD element. * * @author Mark Taylor (Starlink) */ public class VOStarTable extends AbstractStarTable { private TableElement votable; private TabularData tdata; private ColumnInfo[] colinfos; private boolean doneParams; private static Logger logger_ = Logger.getLogger( "uk.ac.starlink.votable" ); /* Public column auxiliary metadata definitions. */ /** ValueInfo for VOTable ID attribute. */ public final static ValueInfo ID_INFO = new DefaultValueInfo( "VOTable ID", String.class, "VOTable ID attribute" ); /** ValueInfo for VOTable ucd attribute. */ public final static ValueInfo UCD_INFO = new DefaultValueInfo( "UCD", String.class, "Table UCD" ); /** ValueInfo for VOTable utype attribute. */ public final static ValueInfo UTYPE_INFO = new DefaultValueInfo( "utype", String.class, "Usage-specific type" + " (ties value to an external data model)" ); /** ValueInfo for VOTable width attribute. */ public final static ValueInfo WIDTH_INFO = new DefaultValueInfo( "VOTable width", Integer.class, "VOTable width attribute" ); /** ValueInfo for VOTable precision attribute. */ public final static ValueInfo PRECISION_INFO = new DefaultValueInfo( "VOTable precision", String.class, "VOTable precision attribute" ); /** ValueInfo for VOTable ref attribute. */ public final static ValueInfo REF_INFO = new DefaultValueInfo( "VOTable ref", String.class, "VOTable ref attribute" ); /** ValueInfo for VOTable type attribute. */ public final static ValueInfo TYPE_INFO = new DefaultValueInfo( "Type", String.class, "VOTable type attribute" ); /** ValueInfo for VOTable datatype attribute. */ public final static ValueInfo DATATYPE_INFO = new DefaultValueInfo( "Datatype", String.class, "VOTable data type name" ); /** ValueInfo for COOSYS system attribute. */ public final static ValueInfo COOSYS_SYSTEM_INFO = new DefaultValueInfo( "CoosysSystem", String.class, "Sky coordinate system name from COOSYS"); /** ValueInfo for COOSYS epoch attribute. */ public final static ValueInfo COOSYS_EPOCH_INFO = new DefaultValueInfo( "CoosysEpoch", String.class, "Sky epoch from COOSYS" ); /** ValueInfo for COOSYS equinox attribute. */ public final static ValueInfo COOSYS_EQUINOX_INFO = new DefaultValueInfo( "CoosysEquinox", String.class, "Sky equinox from COOSYS" ); /** ValueInfo for TIMESYS timeorigin attribute. */ public final static ValueInfo TIMESYS_TIMEORIGIN_INFO = new DefaultValueInfo( "TimesysTimeorigin", String.class, "Time origin from TIMESYS" ); /** ValueInfo for TIMESYS timescale attribute. */ public final static ValueInfo TIMESYS_TIMESCALE_INFO = new DefaultValueInfo( "TimesysTimescale", String.class, "Timescale from TIMESYS" ); /** ValueInfo for TIMESYS refposition attribute. */ public final static ValueInfo TIMESYS_REFPOSITION_INFO = new DefaultValueInfo( "TimesysRefposition", String.class, "Ref position from TIMESYS" ); private final static ValueInfo nullInfo = Tables.NULL_VALUE_INFO; private final static ValueInfo ubyteInfo = Tables.UBYTE_FLAG_INFO; private final static List auxDataInfos = Arrays.asList( new ValueInfo[] { DATATYPE_INFO, nullInfo, COOSYS_SYSTEM_INFO, COOSYS_EPOCH_INFO, COOSYS_EQUINOX_INFO, TIMESYS_TIMEORIGIN_INFO, TIMESYS_TIMESCALE_INFO, TIMESYS_REFPOSITION_INFO, ubyteInfo, WIDTH_INFO, PRECISION_INFO, ID_INFO, REF_INFO, TYPE_INFO, } ); /** * Construct a VOStarTable from a TABLE element. * The data itself is inferred or constructed from the state and * content of the element. * * @param votable Table VOElement */ public VOStarTable( TableElement votable ) throws IOException { this( votable, votable.getData() ); } /** * Construct a VOStarTable from a TABLE element forcing a particular * data implementation. * * @param votable Table VOElement, which supplies the table's metadata * @param tdata object supplying the table's data */ VOStarTable( TableElement votable, TabularData tdata ) { this.votable = votable; this.tdata = tdata; setName( calculateName( votable ) ); } public int getColumnCount() { return tdata.getColumnCount(); } public long getRowCount() { return votable.getNrows(); } public boolean isRandom() { return tdata.isRandom(); } public ColumnInfo getColumnInfo( int icol ) { /* Lazily construct the columninfo list. */ if ( colinfos == null ) { FieldElement[] fields = votable.getFields(); int ncol = fields.length; colinfos = new ColumnInfo[ ncol ]; for ( int i = 0; i < ncol; i++ ) { FieldElement field = fields[ i ]; colinfos[ i ] = new ColumnInfo( getValueInfo( field ) ); } } return colinfos[ icol ]; } public List getParameters() { /* Lazily construct parameter list. */ if ( ! doneParams ) { List params = new ArrayList(); /* DESCRIPTION child. */ String description = votable.getDescription(); if ( description != null && description.trim().length() > 0 ) { DefaultValueInfo descInfo = new DefaultValueInfo( "Description", String.class ); params.add( new DescribedValue( descInfo, description.trim() ) ); } /* UCD attribute. */ if ( votable.hasAttribute( "ucd" ) ) { DescribedValue dval = new DescribedValue( UCD_INFO, votable.getAttribute( "ucd" ) ); params.add( dval ); } /* Utype attribute. */ if ( votable.hasAttribute( "utype" ) ) { DescribedValue dval = new DescribedValue( UTYPE_INFO, votable.getAttribute( "utype" ) ); params.add( dval ); } /* Track back through ancestor elements to pick up parameter- * like elements in this TABLE element and any ancestor * RESOURCE elements. */ List pelList = new ArrayList(); for ( VOElement ancestor = votable; ancestor != null; ancestor = ancestor.getParent() ) { addParamElements( ancestor, pelList ); } /* Convert these elements into DescribedValue metadata objects. */ for ( VOElement el : pelList ) { String tag = el.getVOTagName(); if ( el instanceof ParamElement ) { ParamElement pel = (ParamElement) el; params.add( new DescribedValue( getValueInfo( pel ), pel.getObject() ) ); } else if ( el instanceof LinkElement ) { LinkElement lel = (LinkElement) el; params.add( getDescribedValue( lel ) ); } else if ( "INFO".equals( tag ) ) { String content = DOMUtils.getTextContent( el ); String descrip = ( content != null && content.trim().length() > 0 ) ? content : null; ValueInfo info = new DefaultValueInfo( el.getHandle(), String.class, descrip ); DescribedValue dval = new DescribedValue( info, el.getAttribute( "value" ) ); params.add( dval ); } else { assert false : el; } } /* Datalink-style Service Descriptors. */ ServiceDescriptorFactory sdFact = new ServiceDescriptorFactory(); ServiceDescriptor[] servDescrips = sdFact.readTableServiceDescriptors( votable ); int nsd = servDescrips.length; for ( int isd = 0; isd < nsd; isd++ ) { ServiceDescriptor sd = servDescrips[ isd ]; final String sdName; if ( sd.getName() != null ) { sdName = "Service_" + sd.getName(); } else { sdName = nsd == 1 ? "ServiceDescriptor" : "ServiceDescriptor" + ( isd + 1 ); } String sdDescrip = sd.getDescription() == null ? null : "Service Descriptor: " + sd.getDescription(); ValueInfo sdInfo = new ServiceDescriptorInfo( sdName, sdDescrip, this ); params.add( new DescribedValue( sdInfo, sd ) ); } /* Post-process parameter list. */ adjustParams( params ); /* Append this list to the superclass list. */ synchronized ( this ) { if ( ! doneParams ) { super.getParameters().addAll( params ); doneParams = true; } } } return super.getParameters(); } public List getColumnAuxDataInfos() { return auxDataInfos; } public RowSequence getRowSequence() throws IOException { return tdata.getRowSequence(); } public RowAccess getRowAccess() throws IOException { if ( isRandom() ) { return tdata.getRowAccess(); } else { throw new UnsupportedOperationException(); } } public Object[] getRow( long lrow ) throws IOException { if ( isRandom() ) { return tdata.getRow( lrow ); } else { throw new UnsupportedOperationException(); } } public Object getCell( long lrow, int icol ) throws IOException { if ( isRandom() ) { return tdata.getCell( lrow, icol ); } else { throw new UnsupportedOperationException(); } } public void close() throws IOException { tdata.close(); } /** * Perform post-processing on the list of table parameters that * has been acquired from the input VOTable document. * *

At present this strips out potentially huge numbers of * uk.ac.starlink.topcat.plot2.TopcatLayer* INFO elements * that were erroneously added by TOPCAT v4.5 when a table * was plotted, thus trying to undo the mess that bug added. * It is conceivable that this is behaviour could be * unwanted, but (except for specific debugging purposes) * very unlikely, so just do it rather than make it configurable. * * @param params mutable metadata item list to adjust in place */ private static void adjustParams( List params ) { int nTclayer = 0; String tclayerPrefix = "uk.ac.starlink.topcat.plot2.TopcatLayer"; for ( Iterator it = params.iterator(); it.hasNext(); ) { DescribedValue dval = it.next(); String dvname = dval.getInfo().getName(); if ( dvname != null && dvname.startsWith( tclayerPrefix ) ) { nTclayer++; it.remove(); } } if ( nTclayer > 0 ) { logger_.warning( "Discarded " + nTclayer + " " + tclayerPrefix + "*" + " INFOs (added by TOPCAT v4.5 bug)" ); } } /** * Works out a suitable name for a given table element. * * @param table element * @return label string */ private static String calculateName( TableElement table ) { /* If there is a name attribute, use that. */ if ( table.getName() != null ) { return table.getName(); } /* Otherwise, try to get a system ID (document base name). */ String sysid = ((VODocument) table.getOwnerDocument()).getSystemId(); /* Shorten the system ID to a reasonable length. */ if ( sysid != null ) { int sindex = sysid.lastIndexOf( '/' ); if ( sindex < 0 || sindex == sysid.length() - 1 ) { sindex = sysid.lastIndexOf( '\\' ); } if ( sindex > 0 && sindex < sysid.length() - 1 ) { sysid = sysid.substring( sindex + 1 ); } } /* Work out how many TABLE elements there are in the document. */ Document doc = table.getOwnerDocument(); boolean multiTable = (doc instanceof VODocument) ? ((VODocument) doc).getElementCount( "TABLE" ) > 1 : true; if ( multiTable ) { return ( sysid == null ? "" : sysid ) + "#" + ( table.getElementSequence() + 1 ); } else { return sysid == null ? "votable" : sysid; } } /** * Adds parameter-like children of a given element to a given list. * Recurses into GRUOP elements, but not into any other elements * such as TABLE or RESOURCE. * *

Element types added to the list are * PARAM (including referents of PARAMrefs), LINK and INFO. * * @param parent element whose children are to be considered * @param pelList list to which parameter-like elements will be added */ private static void addParamElements( VOElement parent, List pelList ) { VOElement[] children = parent.getChildren(); for ( int i = 0; i < children.length; i++ ) { VOElement child = children[ i ]; if ( child instanceof ParamElement ) { if ( ! pelList.contains( child ) ) { pelList.add( child ); } } else if ( child instanceof ParamRefElement ) { ParamElement pel = ((ParamRefElement) child).getParam(); if ( pel != null ) { if ( ! pelList.contains( pel ) ) { pelList.add( pel ); } } else { String msg = new StringBuffer() .append( "Ignoring PARAMref element with no referent " ) .append( '"' ) .append( child.getAttribute( "ref" ) ) .append( '"' ) .toString(); logger_.warning( msg ); } } else if ( child instanceof LinkElement ) { if ( ! pelList.contains( child ) ) { pelList.add( child ); } } else if ( "INFO".equals( child.getVOTagName() ) ) { if ( ! pelList.contains( child ) ) { pelList.add( child ); } } else if ( child instanceof GroupElement ) { addParamElements( child, pelList ); } } } /** * Returns a ValueInfo object suitable for holding the values in a * VOTable Field (or Param) object. The datatype, array shape and * other metadata in the returned object are taken from the * relevant bits of the supplied field. * * @param field the FieldElement object for which the ValueInfo is to be * constructed * @return a ValueInfo suitable for field */ public static ValueInfo getValueInfo( FieldElement field ) { /* Decoder. */ Decoder decoder = field.getDecoder(); /* Basic metadata. */ Class clazz = decoder.getContentClass(); String name = field.getHandle(); long[] shapel = decoder.getDecodedShape(); DefaultValueInfo info = new DefaultValueInfo( name, clazz ); info.setDescription( field.getDescription() ); info.setUnitString( field.getUnit() ); info.setUCD( field.getUcd() ); info.setUtype( field.getUtype() ); info.setXtype( field.getXtype() ); info.setShape( ( shapel == null || shapel.length == 0 ) ? null : Decoder.longsToInts( shapel ) ); info.setElementSize( decoder.getElementSize() ); /* Aux metadata. */ List auxdata = info.getAuxData(); if ( field.hasAttribute( "ID" ) ) { String id = field.getAttribute( "ID" ); auxdata.add( new DescribedValue( ID_INFO, id ) ); } if ( field.hasAttribute( "datatype" ) ) { String datatype = field.getAttribute( "datatype" ); auxdata.add( new DescribedValue( DATATYPE_INFO, datatype ) ); if ( "unsignedByte".equals( datatype ) ) { auxdata.add( new DescribedValue( ubyteInfo, Boolean.TRUE ) ); } } String blankstr = field.getNull(); if ( blankstr != null ) { Object blank = blankstr; try { if ( clazz == Byte.class ) { blank = Byte.valueOf( blankstr ); } else if ( clazz == Short.class ) { blank = Short.valueOf( blankstr ); } else if ( clazz == Integer.class ) { blank = Integer.valueOf( blankstr ); } else if ( clazz == Long.class ) { blank = Long.valueOf( blankstr ); } } catch ( NumberFormatException e ) { blank = blankstr; } auxdata.add( new DescribedValue( nullInfo, blank ) ); } if ( field.hasAttribute( "width" ) ) { String width = field.getAttribute( "width" ); try { int wv = Integer.parseInt( width ); auxdata.add( new DescribedValue( WIDTH_INFO, new Integer( wv ) ) ); } catch ( NumberFormatException e ) { } } if ( field.hasAttribute( "precision" ) ) { String precision = field.getAttribute( "precision" ); auxdata.add( new DescribedValue( PRECISION_INFO, precision ) ); } if ( field.hasAttribute( "type" ) ) { String type = field.getAttribute( "type" ); auxdata.add( new DescribedValue( TYPE_INFO, type ) ); } /* Coosys. */ VOElement coosys = field.getCoosys(); if ( coosys != null ) { if ( coosys.hasAttribute( "system" ) ) { String system = coosys.getAttribute( "system" ); auxdata.add( new DescribedValue( COOSYS_SYSTEM_INFO, system ) ); } if ( coosys.hasAttribute( "epoch" ) ) { String epoch = coosys.getAttribute( "epoch" ); auxdata.add( new DescribedValue( COOSYS_EPOCH_INFO, epoch ) ); } if ( coosys.hasAttribute( "equinox" ) ) { String equinox = coosys.getAttribute( "equinox" ); auxdata.add( new DescribedValue( COOSYS_EQUINOX_INFO, equinox ) ); } } /* Timesys. */ VOElement timesys = field.getTimesys(); if ( timesys != null ) { if ( timesys.hasAttribute( "timeorigin" ) ) { String torigin = timesys.getAttribute( "timeorigin" ); auxdata .add( new DescribedValue( TIMESYS_TIMEORIGIN_INFO, torigin ) ); } if ( timesys.hasAttribute( "timescale" ) ) { String tscale = timesys.getAttribute( "timescale" ); auxdata.add( new DescribedValue( TIMESYS_TIMESCALE_INFO, tscale ) ); } if ( timesys.hasAttribute( "refposition" ) ) { String refpos = timesys.getAttribute( "refposition" ); auxdata .add( new DescribedValue( TIMESYS_REFPOSITION_INFO, refpos ) ); } } /* Links. */ VOElement[] links = field.getChildrenByName( "LINK" ); for ( int j = 0; j < links.length; j++ ) { auxdata.add( getDescribedValue( (LinkElement) links[ j ] ) ); } /* Domain mappers. */ info.setDomainMappers( VOTableDomainMappers.getMappers( info ) ); return info; } /** * Identifies the column that was labelled with a given ID attribute. * * @param colRef ID string * @param table table to interrogate; this will presumably be based * on a VOStarTable, but it may be some kind of * wrapped form of one * @return index of the column in table whose FIELD * element had an ID attribute of colRef, * or -1 if none exists */ public static int getRefColumnIndex( String colRef, StarTable table ) { if ( table != null ) { int ncol = table.getColumnCount(); for ( int ic = 0; ic < ncol; ic++ ) { ColumnInfo info = table.getColumnInfo( ic ); if ( colRef.equals( info.getAuxDatumValue( ID_INFO, String.class ) ) ) { return ic; } } } return -1; } /** * Returns a DescribedValue representing a LINK element. * * @param link link element * @return value describing link */ static DescribedValue getDescribedValue( LinkElement link ) { try { URL url = link.getHref(); ValueInfo vinfo = new URLValueInfo( link.getHandle(), link.getDescription() ); return new DescribedValue( vinfo, url ); } catch ( MalformedURLException e ) { String href = link.getAttribute( "href" ); ValueInfo vinfo = new DefaultValueInfo( link.getHandle(), String.class, link.getDescription() ); return new DescribedValue( vinfo, href ); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy