<?xml version='1.0'?>
<!DOCTYPE sun SYSTEM "docs.dtd" [
  <!ENTITY coredocs       "https://docs.oracle.com/javase/8/docs/api/">

  <!-- I/O handler text. -->
  <!ENTITY in-ascii SYSTEM 'in-ascii.xml'>
  <!ENTITY in-cdf SYSTEM 'in-cdf.xml'>
  <!ENTITY in-colfits SYSTEM 'in-colfits-basic.xml'>
  <!ENTITY in-csv SYSTEM 'in-csv.xml'>
  <!ENTITY in-ecsv SYSTEM 'in-ecsv.xml'>
  <!ENTITY in-pds4 SYSTEM 'in-pds4.xml'>
  <!ENTITY in-parquet SYSTEM 'in-parquet.xml'>
  <!ENTITY in-hapi SYSTEM 'in-hapi.xml'>
  <!ENTITY in-mrt SYSTEM 'in-mrt.xml'>
  <!ENTITY in-feather SYSTEM 'in-feather.xml'>
  <!ENTITY in-fits SYSTEM 'in-fits.xml'>
  <!ENTITY in-gbin SYSTEM 'in-gbin.xml'>
  <!ENTITY in-ipac SYSTEM 'in-ipac.xml'>
  <!ENTITY in-tst SYSTEM 'in-tst.xml'>
  <!ENTITY in-votable SYSTEM 'in-votable.xml'>
  <!ENTITY in-wdc SYSTEM 'in-wdc.xml'>
  <!ENTITY out-ascii SYSTEM 'out-ascii.xml'>
  <!ENTITY out-csv SYSTEM 'out-csv.xml'>
  <!ENTITY out-ecsv SYSTEM 'out-ecsv.xml'>
  <!ENTITY out-parquet SYSTEM 'out-parquet.xml'>
  <!ENTITY out-feather SYSTEM 'out-feather.xml'>
  <!ENTITY out-fits SYSTEM 'out-fits.xml'>
  <!ENTITY out-html SYSTEM 'out-html.xml'>
  <!ENTITY out-ipac SYSTEM 'out-ipac.xml'>
  <!ENTITY out-latex SYSTEM 'out-latex.xml'>
  <!ENTITY out-mirage SYSTEM 'out-mirage.xml'>
  <!ENTITY out-text SYSTEM 'out-text.xml'>
  <!ENTITY out-tst SYSTEM 'out-tst.xml'>
  <!ENTITY out-votable SYSTEM 'out-votable.xml'>

  <!-- Description of table schemes. -->
  <!ENTITY scheme-docs SYSTEM 'scheme-docs.xml'>

  <!-- Text shared with other documents. -->
  <!ENTITY fits-plus SYSTEM 'fits-plus.xml'>
  <!ENTITY fits-wide SYSTEM 'fits-wide.xml'>

  <!-- Import entity definitions from other packages including customisation -->
  <!ENTITY URL.HEALPIX_FITS
         "https://healpix.sourceforge.io/data/examples/healpix_fits_specs.pdf">

  <!-- JCDF library URL -->
  <!ENTITY mbtwww 'https://www.star.bristol.ac.uk/mbt/'>
  <!ENTITY jcdfurl '&mbtwww;jcdf/'>
]>

<sun>

<docinfo>

<title>STIL - Starlink Tables Infrastructure Library</title>

<authorlist>
<author>Mark Taylor</author>
</authorlist>

<docnumber>252</docnumber>

<history><version><px>$Id: sun252.xml,v 1.371 2025/09/12 07:59:48 mbt Exp $</px></version></history>
<docdate>12 September 2025</docdate>

<contactlist>
<contact>STIL web page:
         <webref url="http://www.starlink.ac.uk/stil/"/></contact>
<contact>Author email:
         <webref url="mailto:m.b.taylor@bristol.ac.uk"
                 >m.b.taylor@bristol.ac.uk</webref></contact>
</contactlist>

</docinfo>

<!-- .......................................................... -->
<docbody>

<!-- Imports for self test harness -->
<blockcode>
<imports>
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.io.BufferedWriter;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.Arrays;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JComponent;
import javax.swing.TransferHandler;
import javax.xml.parsers.SAXParserFactory;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import uk.ac.starlink.auth.AuthManager;
import uk.ac.starlink.auth.UserInterface;
import uk.ac.starlink.fits.BintableStarTable;
import uk.ac.starlink.fits.FitsTableBuilder;
import uk.ac.starlink.table.AbstractStarTable;
import uk.ac.starlink.table.ArrayColumn;
import uk.ac.starlink.table.ColumnInfo;
import uk.ac.starlink.table.ColumnStarTable;
import uk.ac.starlink.table.DescribedValue;
import uk.ac.starlink.table.JoinStarTable;
import uk.ac.starlink.table.RandomStarTable;
import uk.ac.starlink.table.RowAccess;
import uk.ac.starlink.table.RowCollector;
import uk.ac.starlink.table.RowListStarTable;
import uk.ac.starlink.table.RowPermutedStarTable;
import uk.ac.starlink.table.RowRunner;
import uk.ac.starlink.table.RowSequence;
import uk.ac.starlink.table.RowSplittable;
import uk.ac.starlink.table.RowStore;
import uk.ac.starlink.table.StarTable;
import uk.ac.starlink.table.StarTableFactory;
import uk.ac.starlink.table.StarTableOutput;
import uk.ac.starlink.table.StarTableWriter;
import uk.ac.starlink.table.StoragePolicy;
import uk.ac.starlink.table.TableSequence;
import uk.ac.starlink.table.TableSink;
import uk.ac.starlink.table.Tables;
import uk.ac.starlink.table.URLValueInfo;
import uk.ac.starlink.table.WrapperRowAccess;
import uk.ac.starlink.table.WrapperRowSequence;
import uk.ac.starlink.table.WrapperStarTable;
import uk.ac.starlink.table.gui.TableSaveChooser;
import uk.ac.starlink.util.DataSource;
import uk.ac.starlink.util.FileDataSource;
import uk.ac.starlink.votable.DataFormat;
import uk.ac.starlink.votable.TableContentHandler;
import uk.ac.starlink.votable.TableElement;
import uk.ac.starlink.votable.TableHandler;
import uk.ac.starlink.votable.VOElement;
import uk.ac.starlink.votable.VOElementFactory;
import uk.ac.starlink.votable.VOSerializer;
import uk.ac.starlink.votable.VOStarTable;
import uk.ac.starlink.votable.VOTableBuilder;
import uk.ac.starlink.votable.VOTableVersion;
import uk.ac.starlink.votable.VOTableWriter;
</imports>
</blockcode>

<!-- .......................................................... -->

<abstract>

<px>STIL is a set of Java class libraries which allow input,
manipulation and output of tabular data and metadata.
Among its key features are support for many tabular formats
(including VOTable, FITS, ECSV, PDS4, Parquet, MRT, CDF, Feather,
text-based formats and SQL databases)
and support for dealing with very large tables in limited memory.
</px>

<px>As well as an abstract and format-independent definition of what 
constitutes a table, and an extensible framework for "pull-model"
table processing, it provides a number of format-specific
handlers which know how to serialize/deserialize tables.
The framework for interaction between the core table manipulation
facilities and the format-specific handlers is open and pluggable,
so that handlers for new formats can easily be added,
programmatically or at run-time.
</px>

<px>The VOTable handling in particular is provided by classes
which perform efficient XML parsing and can read and 
write VOTables in any of the defined formats (TABLEDATA, BINARY or FITS).
It supports table-aware SAX- or DOM-mode processing and
may be used on its own for VOTable I/O without much reference to
the format-independent parts of the library.
</px>

</abstract>

<sect>
<subhead><title>Introduction</title></subhead>

<p>STIL is a set of class libraries for the input, output
and manipulation of tables.  It has been developed for use with
astronomical tables, though it could be used for any kind of
tabular data.
It has no "native" external table format.  What it has is 
a model of what a table looks like, 
a set of java classes for manipulating such tables, 
an extensible framework for table I/O, 
and a number of format-specific I/O handlers for dealing with 
several known table formats.
</p>

<p>This document is a programmers' overview of the abilities of the STIL
libraries, including some tutorial explanation and example code.  
Some parts of it may also be useful background reading for users
of applications built on STIL.
Exhaustive descriptions of all the classes and methods are not
given here;
that information can be found in the <javadoc class=".">javadocs</javadoc>,
which should be read in conjunction with this document if you are 
actually using these libraries.
Much of the information here is repeated in the javadocs.
The hypertext version of this document links to the relevant places
in the javadocs where appropriate.
The latest released version of this document in several formats can
be found at <webref url="http://www.starlink.ac.uk/stil/"/>.
</p>

<subsect>
<subhead><title>What is a table?</title></subhead>

<p>In words, STIL's idea of what constitutes a table is something
which has the following:
<ul>
<li>Some per-table metadata (parameters)</li>
<li>A number of columns</li>
<li>Some per-column metadata</li>
<li>A number of rows, each containing one entry per column</li>
</ul>
This model is embodied in the
<javadoc class="uk.ac.starlink.table.StarTable"/> interface,
which is described in <ref id="starTable">the next section</ref>.
It maps quite closely, though not exactly, onto the table model
embodied in the <ref id="votable">VOTable</ref> definition, which
itself owes a certain amount to FITS tables.  This is not coincidence.
</p>

</subsect>
</sect>

<sect id="starTable">
<subhead><title>The StarTable interface</title></subhead>

<p>The most fundamental type in the STIL package is 
<javadoc class="uk.ac.starlink.table.StarTable"
         codetext="uk.ac.starlink.table.StarTable"/>; 
any time you are using a table, you will use an object which 
implements this interface.
</p>

<subsect>
<subhead><title>Table Metadata</title></subhead>

<p>A few items of the table metadata 
(<javadoc class="uk.ac.starlink.table.StarTable" 
          member="getName--">name</javadoc>,
 <javadoc class="uk.ac.starlink.table.StarTable"
          member="getURL()">URL</javadoc>)
are available directly as values from the <code>StarTable</code> interface.
A general parameter mechanism is provided for storing other items,
for instance user-defined ones.
The <javadoc class="uk.ac.starlink.table.StarTable" member="getParameters()"/>
method returns a list of 
<javadoc class="uk.ac.starlink.table.DescribedValue"/>
objects which contain a scalar or array value and some metadata describing it
(name, units, 
<webref url="https://www.ivoa.net/documents/UCD1+/"
        >Unified Content Descriptor</webref>).
This list can be read or altered as required.
</p>

<p>The <code>StarTable</code> interface also contains
the methods 
<javadoc class="uk.ac.starlink.table.StarTable" member="getColumnCount()"/>
and <javadoc class="uk.ac.starlink.table.StarTable" member="getRowCount()"/>
to determine the shape of the table.  Note however that for tables
with sequential-only access, it may not be possible to ascertain
the number of rows - in this case <code>getRowCount</code> will return -1.
Random-access tables (see <ref id="tableData"/>) will always return
a positive row count.
</p>
</subsect>

<subsect id="colMeta">
<subhead><title>Column Metadata</title></subhead>

<p>Each column in a <code>StarTable</code> is assumed to contain the
same sort of thing.  More specifically, for each table column there is 
a <javadoc class="uk.ac.starlink.table.ColumnInfo"/> object 
associated with each column which holds metadata describing 
the values contained in that column 
(the value associated with that column for each row in the table).
A <code>ColumnInfo</code> contains information about the
<javadoc class="uk.ac.starlink.table.ValueInfo"
         member="getName()">name</javadoc>,
<javadoc class="uk.ac.starlink.table.ValueInfo"
         member="getUnitString()">units</javadoc>,
<javadoc class="uk.ac.starlink.table.ValueInfo"
         member="getUCD()">UCD</javadoc>,
<javadoc class="uk.ac.starlink.table.ValueInfo"
         member="getContentClass()">class</javadoc>
etc of a column, as well as a mechanism for storing additional
('auxiliary') user-defined metadata.
It also provides methods for rendering the values in the column
under various circumstances.
</p>

<p>The class associated with a column, obtained from the 
<javadoc class="uk.ac.starlink.table.ValueInfo"
         member="getContentClass()"/> method, 
is of particular importance.  Every object in the column described
by that metadata should be an instance of the 
<javadoc class="java.lang.Class" docset="&coredocs;"/> that
<code>getContentClass</code> returns
(or of one of its subtypes), or <code>null</code>.
There is nothing in the tables infrastructure which can enforce
this, but a table which doesn't follow this rule is considered broken,
and application code is within its rights to behave unpredictably 
in this case.
Such a broken table might result from a bug in the I/O handler used
to obtain the table in the first place, or a badly formed table that
it has read, or a bug in one of the wrapper classes upstream from
the table instance being used.
Because of the extensible nature of the infrastructure, 
such bugs are not necessarily STIL's fault.
</p>

<p>Any (non-primitive) class can be used
but most table I/O handlers can only
cope with certain types of value - typically the primitive wrapper classes
(numeric ones like
<javadoc class="java.lang.Integer" docset="&coredocs;"/>,
<javadoc class="java.lang.Double" docset="&coredocs;"/> and 
<javadoc class="java.lang.Boolean" docset="&coredocs;"/>) and 
<javadoc class="java.lang.String" docset="&coredocs;"/>s, so these
are the most important ones to deal with.  The contents of a table
cell must always (as far as the access methods are concerned) be
an <javadoc class="java.lang.Object" docset="&coredocs;"/> or 
<code>null</code>, so primitive values cannot be used directly.
The general rule for primitive-like (numeric or boolean) values
is that a scalar should be represented by the appropriate wrapper class
(<javadoc class="java.lang.Integer" docset="&coredocs;"/>,
 <javadoc class="java.lang.Float" docset="&coredocs;"/>,
 <javadoc class="java.lang.Boolean" docset="&coredocs;"/> etc)
and an array by an array of primitives
(<code>int[]</code>,
 <code>float[]</code>,
 <code>boolean[]</code> etc).
Non-primitive-like objects 
(of which <code>String</code> is the most important example)
should be represented by their own class (for scalars) or an array 
of their own class (for arrays).
Note that it is <em>not</em> recommended to use multidimensional arrays
(i.e. arrays of arrays like <code>int[][]</code>);
a 1-dimensional Java array should be used, and information about
the dimensionality should be stored in the <code>ColumnInfo</code>'s 
shape attribute.
Thus to store a 3x2 array of integers, a 6-element array of type
<code>int[]</code> would be used, and the <code>ColumnInfo</code>'s
<javadoc class="uk.ac.starlink.table.ValueInfo" member="getShape()"/> 
method would return a two-element array <code>(3,2)</code>.
</p>

<p>In the case of floating point values
(columns with class <code>Float</code> or <code>Double</code>),
STIL implementation code treats <code>null</code> and NaN (Not-a-Number)
values somewhat interchangeably. 
Typically where STIL encounters a null floating point value,
arising either from a file format that can represent blanks,
or from processing that fails to provide a definite value,
it will be represented internally as a NaN for reasons of efficiency.
In general therefore, client code is advised not to rely on
distinguishing these cases.
</p>

</subsect>

<subsect id="tableData">
<subhead><title>Table Data</title></subhead>

<p>The actual data values in a table are considered to be a sequence
of rows, each containing one value for each of the table's columns.
As explained <ref id="colMeta">above</ref>, each such value 
is an <code>Object</code>, and information about its class
(as well as semantic metadata) is available from the column's
<javadoc class="uk.ac.starlink.table.ColumnInfo"/> object.
</p>

<p><code>StarTable</code>s come in two flavours, 
random-access and sequential-only;
you can tell which one a given table is by using its 
<javadoc class="uk.ac.starlink.table.StarTable" member="isRandom()"/>
method, and how its data can be accessed is determined by this.
In either case, most of the data access methods are declared to
throw an <javadoc class="java.io.IOException" docset="&coredocs;"/>
to signal any data access error.
</p>

<subsubsect id="sequential">
<subhead><title>Sequential Access</title></subhead>

<p>It is always possible to access a table's data sequentially,
that is starting with the first row and reading forward a row at
a time to the last row; it may or may not be possible to tell in advance
(using <javadoc class="uk.ac.starlink.table.StarTable" member="getRowCount()"/>)
how many rows there are.
To perform sequential access, use the
<javadoc class="uk.ac.starlink.table.StarTable" member="getRowSequence()"/>
method to get a
<javadoc class="uk.ac.starlink.table.RowSequence"/> object,
which is an iterator 
over the rows in the table.
The <code>RowSequence</code>'s 
<javadoc class="uk.ac.starlink.table.RowSequence" member="next()"/>
method moves forward a row without returning any data;
to obtain the data use either
<javadoc class="uk.ac.starlink.table.RowSequence" member="getCell(int)"/>
or <javadoc class="uk.ac.starlink.table.RowSequence" member="getRow()"/>;
the relative efficiencies of these depend on the implementation, but
in general if you want all or nearly all of the values in a row
it is a good idea to use <code>getRow</code>, if you just want one
or two use <code>getCell</code>.
You cannot move the iterator backwards.
When obtained, a <code>RowSequence</code> is positioned before the
first row in the table, 
so (unlike an <javadoc class="java.util.Iterator" docset="&coredocs;"/>) 
it is necessary to call <code>next</code>
before the first row is accessed.
</p>

<p>Here is an example of how to sum the values in one of the numeric columns
of a table.  Since only one value is required from each row,
<code>getCell</code> is used:
<blockcode><![CDATA[
    double sumColumn( StarTable table, int icol ) throws IOException {

        // Check that the column contains values that can be cast to Number.
        ColumnInfo colInfo = table.getColumnInfo( icol );
        Class colClass = colInfo.getContentClass();
        if ( ! Number.class.isAssignableFrom( colClass ) ) {
            throw new IllegalArgumentException( "Column not numeric" );
        }

        // Iterate over rows accumulating the total.
        double sum = 0.0;
        RowSequence rseq = table.getRowSequence();
        while ( rseq.next() ) {
            Number value = (Number) rseq.getCell( icol );
            sum += value.doubleValue();
        }
        rseq.close();
        return sum;
    }
]]></blockcode>
The next example prints out every cell value.  Since it needs all the values
in each cell, it uses <code>getRow</code>:
<blockcode><![CDATA[
    void writeTable( StarTable table ) throws IOException {
        int nCol = table.getColumnCount();
        RowSequence rseq = table.getRowSequence();
        while ( rseq.next() ) {
            Object[] row = rseq.getRow();
            for ( int icol = 0; icol < nCol; icol++ ) {
                System.out.print( row[ icol ] + "\t" );
            }
            System.out.println();
        }
        rseq.close();
    }
]]></blockcode>
In this case a tidier representation of the values might be given by
replacing the <code>print</code> call with: 
<blockcode>
<hidden>void dummy5( StarTable table, Object[] row, int icol ) {</hidden>
    System.out.print( table.getColumnInfo( icol )
                           .formatValue( row[ icol ], 20 ) + "\t" );
<hidden>}</hidden>
</blockcode>
</p>

</subsubsect>

<subsubsect id="randomAccess">
<subhead><title>Random Access</title></subhead>

<p>If a table's 
<javadoc class="uk.ac.starlink.table.StarTable" member="isRandom()"/>
method returns true, then it is possible to access the cells of a 
table in any order.
</p>

<p>The most straightforward way to do this is using the 
<javadoc class="uk.ac.starlink.table.StarTable" member="getCell(long, int)"/>
or <javadoc class="uk.ac.starlink.table.StarTable" member="getRow(long)"/>
methods directly on the table itself (not on a <code>RowSequence</code>).
These methods are supposed to be in general safe for use
from multiple threads concurrently;
however depending on the implementation that may be enforced in
a way that slows down concurrent access, for instance using synchronization.
</p>

<p>The preferred alternative in multithreaded contexts is to use the
<javadoc class="uk.ac.starlink.table.StarTable" member="getRowAccess()"/>
method to obtain a <javadoc class="uk.ac.starlink.table.RowAccess"/> object.
A <code>RowAccess</code> can be used for random access within a single thread,
in a way that may (depending on the implementation)
avoid contention for resources.
</p>

<p>Similar comments about 
whether to use the by-cell or by-row methods apply as
in <ref id="sequential">the previous section</ref>.
</p>

<p>If an attempt is made to call these random access methods 
on a non-random table 
(one for which <code>isRandom()</code> returns <code>false</code>),
an
<javadoc class="java.lang.UnsupportedOperationException" docset="&coredocs;"/>
will be thrown.
</p>
</subsubsect>

<subsubsect id="parallel">
<subhead><title>Parallel Processing</title></subhead>

<p>STIL version 4 introduces support for parallel processing of
tabular data.  This somewhat resembles, but is not based on,
parts of the streams framework from Java 8.
To perform a parallel operation on a <code>StarTable</code>,
you must provide an instance of the class
<javadoc class="uk.ac.starlink.table.RowCollector"/>,
and pass it along with a target table to the
<javadoc class="uk.ac.starlink.table.RowRunner"
         member="collect(uk.ac.starlink.table.RowCollector,
                         uk.ac.starlink.table.StarTable)"/>
method of a suitable <javadoc class="uk.ac.starlink.table.RowRunner"/>.
The <code>RowRunner</code> instance determines whether execution is
done sequentially or in parallel; usually <code>RowRunner.DEFAULT</code>
is a suitable instance (if there are many rows and multiple cores
are available it will run in parallel; if there are few rows or
the hardware only provides a single core it will run sequentially).
The <code>RowRunner</code> accesses the table data using the
<javadoc class="uk.ac.starlink.table.StarTable" member="getRowSplittable()"/>
method of the table in question;
the <code>RowSplittable</code> thus obtained behaves a bit like a
<code>java.util.Spliterator</code> in that it can be recursively
divided up into smaller pieces amenable to parallel processing.
Although all <code>StarTable</code>s must implement
the <code>getRowSplittable</code> method,
actual splitting cannot always be implemented,
so depending on the behaviour of the table in question,
there is no guarantee that processing will actually be
performed in parallel.
</p>

<p>Here is an example of how to sum the contents of a column
using (potentially) parallel processing:
<blockcode><![CDATA[
    static double sumColumnParallel( StarTable table, int icol ) throws IOException {
        double[] acc = RowRunner.DEFAULT.collect( new SumCollector( icol ), table );
        return acc[ 0 ];
    }

    /**
     * RowCollector implementation that sums values from a single column,
     * using a 1-element double[] array to accumulate values into.
     */
    static class SumCollector extends RowCollector<double[]> {
        final int icol_;
        SumCollector( int icol ) {
            icol_ = icol;
        }
        public double[] createAccumulator() {
            return new double[ 1 ];
        }
        public double[] combine(double[] acc1, double[] acc2) {
            acc1[ 0 ] += acc2[ 0 ];
            return acc1;
        }
        public void accumulateRows( RowSplittable rseq, double[] acc ) throws IOException {
            while ( rseq.next() ) {
                Object value = rseq.getCell( icol_ );
                if ( value instanceof Number ) {
                    acc[ 0 ] += ((Number) value).doubleValue();
                }
            }
        }
    }
]]></blockcode>
</p>

<p>The level of parallelism available from the JVM is determined from
the system property
<code>java.util.concurrent.ForkJoinPool.common.parallelism</code>,
which is normally set to one less than the number of processing cores
on the current machine.
Parallel processing can be inhibited by setting this value to 1.
</p>

</subsubsect>

<subsubsect id="randomTable">
<subhead><title>Adapting Sequential to Random Access</title></subhead>

<p>What do you do if you have a sequential-only table and you
need to perform random access on it?
The <javadoc class="uk.ac.starlink.table.Tables" 
             member="randomTable(uk.ac.starlink.table.StarTable)"
             codetext="Tables.randomTable"/>
utility method
takes any table and returns one which is guaranteed to provide random access.
If the original one is random, it just returns it unchanged,
otherwise it returns a table which contains the same data as the
submitted one, but for which <code>isRandom</code> is guaranteed to
return true.
It effectively does this by taking out a <code>RowSequence</code> and
reading all the data sequentially into some kind of (memory- or disk-based) 
data structure which can provide random access, returning a new 
StarTable object based on that data structure.
Exactly what kind of data structure is used for caching the data 
for later use is determined by the <code>StoragePolicy</code>
currently in effect - this is described in <ref id="storagePolicy"/>,
and the
<javadoc class="uk.ac.starlink.table.StoragePolicy"
         member="randomTable(uk.ac.starlink.table.StarTable)"
         codetext="StoragePolicy.randomTable"/>
method may be used explicitly instead to control this.
</p>

<p>Clearly, this might be an expensive process.  For this reason
if you have an application in which random access will be required
at various points, it is usually a good idea to ensure you have a
random-access table at the application's top level, 
and for general-purpose utility methods
to require random-access tables 
(throwing an exception if they get a sequential-only one).
The alternative practice of utility methods converting argument tables 
to random-access when they are called might result in this 
expensive process happening multiple times.
</p>

</subsubsect>

</subsect>

</sect>

<sect id="io">
<subhead><title>Table I/O</title></subhead>

<p>The table input and output facilities of STIL are handled
by format-specific input and output handlers;
supplied with the package are, amongst others, a VOTable input
handler and output handler, and this means that STIL can read
and write tables in VOTable format.  An input handler is
an object which can turn an external resource into a
<code>StarTable</code> object, and an output handler is one
which can take a <code>StarTable</code> object and store it 
externally in some way.
These handlers are independent components of the system, and
so new ones can be written, allowing all the STIL features
to be used on new table formats without having to make any changes
to the core classes of the library.
</p>

<p>There are two ways of using these handlers.  
You can either use them directly to read in/write out a table 
using a particular format, or you can use the generic I/O facilities
which know about several of these handlers and select an appropriate
one at run time.  The generic reader class is
<javadoc class="uk.ac.starlink.table.StarTableFactory"/>
which knows about input handlers implementing the 
<javadoc class="uk.ac.starlink.table.TableBuilder"/> interface,
and the generic writer class is
<javadoc class="uk.ac.starlink.table.StarTableOutput"/>
which knows about output handlers implementing the
<javadoc class="uk.ac.starlink.table.StarTableWriter"/> interface.
The generic approach is more flexible in a multi-format
environment (your program will work whether you point it at a
VOTable, FITS file or SQL query) and is generally easier to use,
but if you know what format you're going to be dealing with
you may have more control over format-specific options using the
handler directly.
</p>

<p>The following sections describe in more detail the generic 
input and output facilities, followed by descriptions of each of
the format-specific I/O handlers which are supplied with the package.
There is an additional section (<ref id="jdbc"/>) which deals with
table reading and writing using an SQL database.
</p>


<subsect id="pluggableIO">
<subhead><title>Extensible I/O framework</title></subhead>

<p>STIL can deal with externally-stored tables in a number of different
formats.  It does this using a set of handlers each of which knows
about turning an external data source into one or more java 
<javadoc class="uk.ac.starlink.table.StarTable"/> objects
or serializing one or more <code>StarTable</code> objects into 
an external form.
Such an "external table" will typically be a file on a local disk,
but might also be a URL pointing to a file on a remote host, 
or an SQL query on a remote database, or something else.
</p>

<p>The core I/O framework of STIL itself does not know about any
table formats, but it knows how to talk to format-specific input
or output handlers.  A number of these (VOTable, FITS, ASCII and others,
described in the following subsections)
are supplied as part of the STIL package, so for dealing with
tables in these formats you don't need to do any extra work.
However, the fact that these are treated in a standard way means
that it is possible to add new format-specific handlers and the
rest of the library will work with tables in that format just the
same as with the supplied formats.
</p>

<p>If you have a table format which is unsupported by STIL as it
stands, you can do one or both of the following:
<dl>
<dt>Write a new input handler:</dt>
<dd><p>Implement the <javadoc class="uk.ac.starlink.table.TableBuilder"/>
    interface to take a stream of data and return a
    <code>StarTable</code> object.
    Optionally, you can also implement the
    <javadoc class="uk.ac.starlink.table.MultiTableBuilder"/> subinterface
    if the format can contain multiple tables per file.
    Install it in a 
    <javadoc class="uk.ac.starlink.table.StarTableFactory"/>,
    either programmatically using the
    <javadoc class="uk.ac.starlink.table.StarTableFactory"
             member="getDefaultBuilders()"/> or
    <javadoc class="uk.ac.starlink.table.StarTableFactory"
             member="getKnownBuilders()"/> methods,
    or by setting the 
    <code><ref id="property.readers">startable.readers</ref></code>
    system property.
    This factory will then be able to pick up tables in this format 
    as well as other known formats.
    Such a <code>TableBuilder</code> 
    can also be used directly to read tables by code which knows
    that it's dealing with data in that particular format.
</p></dd>

<dt>Write a new output handler:</dt>
<dd><p>Implement the <javadoc class="uk.ac.starlink.table.StarTableWriter"/>
    interface to take a <code>StarTable</code> and write it to a given
    destination.
    Optionally, you can also implement the
    <javadoc class="uk.ac.starlink.table.MultiStarTableWriter"/> subinterface
    if the format can contain multiple tables per file.
    Install it in a 
    <javadoc class="uk.ac.starlink.table.StarTableOutput"/>
    either programmatically using the
    <javadoc class="uk.ac.starlink.table.StarTableOutput"
             member="setHandlers(uk.ac.starlink.table.StarTableWriter[])"/>
    method or by setting the 
    <code><ref id="property.writers">startable.writers</ref></code>
    system property.
    This StarTableOutput
    will be then be able to write tables in this format as well as others.
    Such a <code>StarTableWriter</code>
    can also be used directly to write tables by code which wants to write 
    data in that particular format.
</p></dd>
</dl>
</p>

<p>Because setting the 
<code>startable.readers</code>/<code>startable.writers</code>
system properties can be done by the user at runtime, an application
using STIL can be reconfigured to work with new table formats without
having to rebuild either STIL or the application in question.
</p>

<p>This document does not currently offer a tutorial on writing new table I/O
handlers; refer to the javadocs for the relevant classes.
</p>

</subsect>

<subsect id="genericInput">
<subhead><title>Generic Table Resource Input</title></subhead>

<p>This section describes the usual way of reading a table or tables from
an external resource such as a file, URL, 
<javadoc class="uk.ac.starlink.util.DataSource"/> etc, and
converting it into a <code>StarTable</code> object whose data
and metadata you can examine as described in 
<ref id="starTable"/>.  These resources have in common that the
data from them can be read more than once; this is necessary in
general since depending on the data format and intended use 
it may require more than one pass to provide the table data.
Reading a table in this way may or may not require local resources
such as memory or disk, depending on how the handler works -
see <ref id="storagePolicy"/> for information on how to influence
such resource usage.
</p>

<p>The main class used to read a table in this way is
<javadoc class="uk.ac.starlink.table.StarTableFactory"/>.
The job of this class is to keep track of which input handlers are
registered and to use one of them to read data from an input stream and
turn it into one or more <code>StarTable</code>s.
The basic rule is that you use one of the <code>StarTableFactory</code>'s 
<code>makeStarTable</code> or <code>makeStarTables</code>
methods to turn what you've got
(e.g. String, URL, <code>DataSource</code>) 
into a 
<javadoc class="uk.ac.starlink.table.StarTable"/> or a
<javadoc class="uk.ac.starlink.table.TableSequence"/>
(which represents a collection of <code>StarTable</code>s) and away you go.
If no StarTable can be created (for instance because the file named doesn't
exist, or because it is not in any of the supported formats)
then some sort of 
<code>IOException</code> or
<javadoc class="uk.ac.starlink.table.TableFormatException"/>
will be thrown.
Note that if the byte stream from the target resource 
is compressed in one of the supported 
formats (gzip, bzip2, Unix compress) it will be
uncompressed automatically
(the work for this is done by the 
<javadoc class="uk.ac.starlink.util.DataSource"/> class).
</p>

<p>There are two distinct modes in which <code>StarTableFactory</code>
can work: automatic format detection and named format.
</p>

<p>In automatic format detection mode, the type of data contained in 
an input stream is determined by looking at it.  What actually happens
is that the factory hands the stream to each of the handlers in its
<em>default handler list</em> 
in turn, and the first one that recognises the format (usually based on
looking at the first few bytes) attempts to make a table from it.
If this fails, a handler may be identified by looking at the file name,
if available (e.g. a filename or URL ending in "<code>.csv</code>" will
be tried as a CSV file).
In this mode, you only need to specify the table location, like this:
<blockcode>
    public StarTable loadTable( File file ) throws IOException {
        return new StarTableFactory().makeStarTable( file.toString() );
    }
</blockcode>
This mode is available for formats such as
FITS, VOTable, ECSV, PDS4, Parquet, MRT, Feather and CDF
that can
be easily recognised, but is not reliable for text-based formats such as 
comma-separated values without recognisable filenames.
You can access and modify the list of 
auto-detecting handlers using the 
<javadoc class="uk.ac.starlink.table.StarTableFactory"
         member="getDefaultBuilders()"/> method.
By default it contains only handlers for VOTable, CDF, FITS-like formats,
ECSV, PDS4, Parquet, MRT, Feather and GBIN.
</p>

<p>In named format mode, you have to specify the name of the format as
well as the table location.  This can be solicited from the user if it's
not known at build time; the known format names can be got from the
<javadoc class="uk.ac.starlink.table.StarTableFactory"
         member="getKnownFormats()"/> method. 
The list of format handlers 
that can be used in this way can be accessed or
modified using the
<javadoc class="uk.ac.starlink.table.StarTableFactory"
         member="getKnownBuilders()"/> method; it usually contains all
the ones in the default handler list, but doesn't have to.
Table construction in named format mode might look like this:
<blockcode>
    public StarTable loadFitsTable( File file ) throws IOException {
        return new StarTableFactory().makeStarTable( file.toString(), "fits" );
    }
</blockcode>
This format also offers the possibility of configuring input handler
options in the handler name.
</p>

<p>If the table format is known at build time, you can alternatively use the
<code>makeStarTable</code> method of the appropriate format-specific
<javadoc class="uk.ac.starlink.table.TableBuilder"/>.
For instance you could replace the above example with this:
<blockcode>
<hidden>StarTable dummy6(File file) throws IOException{</hidden>
        return new FitsTableBuilder()
              .makeStarTable( DataSource.makeDataSource( file.toString() ),
                              false, StoragePolicy.getDefaultPolicy() );
<hidden>}</hidden>
</blockcode>
This slightly more obscure method offers more configurability but has
much the same effect; it may be slightly more efficient and may offer
somewhat more definite error messages in case of failure.
The various supplied <code>TableBuilder</code>s (format-specific 
input handlres) are listed in <ref id="tableBuilders"/>.
</p>

<p>The javadocs detail variations on these calls.
If you want to ensure that the table
you get provides random access (see <ref id="tableData"/>),
you should do something like this:
<blockcode>
    public StarTable loadRandomTable( File file ) throws IOException {
        StarTableFactory factory = new StarTableFactory();
        factory.setRequireRandom( true );
        StarTable table = factory.makeStarTable( file.toString() );
        return table;
    }
</blockcode>
Setting the <code>requireRandom</code> flag on the factory 
ensures that any table returned from its <code>makeStarTable</code>
methods returns <code>true</code> from its 
<javadoc class="uk.ac.starlink.table.StarTable" member="isRandom()"/>
method.
(Note prior to STIL version 2.1 this flag only provided a hint to the
factory that random tables were wanted - now it is enforced.)
</p>

</subsect>

<subsect id="genericStreamInput">
<subhead><title>Generic Table Streamed Input</title></subhead>

<p>As noted in the previous section, in general to make a 
<code>StarTable</code> you need to supply the location of a resource
which can supply the table data stream more than once, since it may be
necessary to make multiple passes.  In some cases however, depending
on the format-specific handler being used, it is possible to read
a table from a non-rewindable stream such as <code>System.in</code>.
In particular both the FITS and VOTable input handlers permit this.
</p>

<p>The most straightforward way of doing this is to use the
<code>StarTableFactory</code>'s 
<javadoc class="uk.ac.starlink.table.StarTableFactory"
         member="makeStarTable(java.io.InputStream,
                               uk.ac.starlink.table.TableBuilder)"
         codetext="makeStarTable(InputStream,TableBuilder)"/> method.
The following snippet reads a FITS table from standard input:
<blockcode><hidden>public StarTable readTableFromInput( InputStream in )
                   throws IOException {</hidden>
    return new StarTableFactory().makeStarTable( System.in, new FitsTableBuilder() );
<hidden>}</hidden>
</blockcode>
caching the table data as determined by the default storage policy
(see <ref id="storagePolicy"/>).
</p>

<p>It is possible to exercise more flexibility however if you 
don't need a stored <code>StarTable</code> object as the result of
the read.  If you just want to examine the table data as it comes
through the stream rather than to store it for later use, you can
implement a <javadoc class="uk.ac.starlink.table.TableSink"/> object
which will be messaged with the input table's metadata and data as
they are encountered, and pass it to the 
<javadoc class="uk.ac.starlink.table.TableBuilder"
         member="streamStarTable(java.io.InputStream,
                                 uk.ac.starlink.table.TableSink,
                                 java.lang.String)"/>
method of a suitable <code>TableBuilder</code>.  
This of course is cheaper on resources than
storing the data.  The following code prints the name of the 
first column and the average of its values (assumed numerical):
<blockcode>
    // Set up a class to handle table processing callback events.
    class ColumnReader implements TableSink {

        private long count;    // number of rows so far
        private double sum;    // running total of values from first column

        // Handle metadata by printing out the first column name.
        public void acceptMetadata( StarTable meta ) {
            String title = meta.getColumnInfo( 0 ).getName();
            System.out.println( "Title: " + title );
        }

        // Handle a row by updating running totals.
        public void acceptRow( Object[] row ) {
            sum += ((Number) row[ 0 ]).doubleValue();
            count++;
        }

        // At end-of-table event calculate and print the average.
        public void endRows() {
            double average = sum / count;
            System.out.println( "Average: " + average );
        }
    };

    // Streams the named file to the sink we have defined, getting the data
    // from the first TABLE element in the file.
    public void summarizeFirstColumn( InputStream in ) throws IOException {
        ColumnReader reader = new ColumnReader();
        new VOTableBuilder().streamStarTable( in, reader, "0" );
        in.close();
    }
</blockcode>
Again, this only works with a table input handler which is capable
of streamed input.
</p>

<p>Writing multiple tables to the same place (for instance, to multiple
extensions of the same multi-extension FITS file) works in a similar way,
but you use instead one of the <code>writeStarTables</code> methods
of <code>StarTableOutput</code>.  These take an array of tables rather
than a single one.
</p>

</subsect>


<subsect id="genericOutput">
<subhead><title>Generic Table Output</title></subhead>

<p>Generic serialization of tables to external storage is done using a 
<javadoc class="uk.ac.starlink.table.StarTableOutput"/> object.
This has a similar job to the <code>StarTableFactory</code>
described in <ref id="genericInput">the previous section</ref>;
it mediates between code which wants to output a table and a
set of format-specific output handler objects.
The <javadoc class="uk.ac.starlink.table.StarTableOutput"
             member="writeStarTable(uk.ac.starlink.table.StarTable,
                                    java.lang.String,
                                    java.lang.String)"/> method
is used to write out a <code>StarTable</code> object.
When invoking this method, you specify the location to which 
you want to output the table 
and a string specifying the format you would like to write in.
This is usually a short string like "fits" associated with one of
the registered output handlers - a list of known formats can be
got using the 
<javadoc class="uk.ac.starlink.table.StarTableOutput"
         member="getKnownFormats()"/> method.
</p>

<p>Use is straightforward:
<blockcode>
    void writeTableAsFITS( StarTable table, File file ) throws IOException {
        new StarTableOutput().writeStarTable( table, file.toString(), "fits" );
    }
</blockcode>
If, as in this example, you know what format you want to write the table in,
you could equally use the relevant 
<javadoc class="uk.ac.starlink.table.StarTableWriter"/> object directly
(in this case a
<javadoc class="uk.ac.starlink.votable.UnifiedFitsTableWriter"/>).
</p>

<p>As implied in the above, the location string is usually a filename.
However, it doesn't have to be - it is turned into an output stream
by the <code>StarTableOutput</code>'s
<javadoc class="uk.ac.starlink.table.StarTableOutput"
         member="getOutputStream(java.lang.String)"/> method.
By default this assumes that the location is a filename except when 
it has the special value "-" which is interpreted as standard output.
However, you can override this method to write to more exotic locations.
</p>

<p>Alternatively, you may wish to output to an 
<javadoc docset="&coredocs;" class="java.io.OutputStream"/> of your own.
This can be done as follows:
<blockcode>
    void writeTableAsFITS( StarTable table, OutputStream out ) throws IOException {
        StarTableOutput sto = new StarTableOutput();
        StarTableWriter outputHandler = sto.getHandler( "fits" );
        sto.writeStarTable( table, out, outputHandler );
    }
</blockcode>
</p>

</subsect>

<subsect id="handlerSpec">
<subhead><title>Specifying I/O Handlers</title></subhead>

<p>In auto mode (if a handler name or instance is not supplied
to the input/output handler when reading/writing a table)
the I/O framework tries to guess the right handler to use,
based on file content and/or filename.  This is often sufficient.
However, for both input and output the framework allows
the caller to supply explicitly a handler instance
(<javadoc class="uk.ac.starlink.table.TableBuilder"
 />/<javadoc class="uk.ac.starlink.table.StarTableWriter"/>)
or a string identifying such an instance.
In the case of an instance, it can be constructed and configured
in the usual way using constructor arguments or mutator methods;
see the per-handler javadocs.
</p>

<p>In the case of a String defining the handler
(e.g. methods
 <javadoc class="uk.ac.starlink.table.StarTableFactory"
          member="makeStarTable(uk.ac.starlink.util.DataSource,
                                java.lang.String)"
          codetext="StarTableFactory.makeStarTable(DataSource,String)"/>,
 <javadoc class="uk.ac.starlink.table.StarTableOutput"
          member="writeStarTable(uk.ac.starlink.table.StarTable,
                                 java.lang.String,
                                 java.lang.String)"
          codetext="StarTableOutput.writeStarTable(StarTable,String,String)"/>)
the basic content of the string can be
<em>either</em> the registered name of one of the handlers known to
the framework,
<em>or</em> the classname of a class having a no-arg constructor which
implements the relevant handler interface.
</p>

<p>So for instance the content of a method to write a table in
VOTable format like this:
<blockcode>
    void writeAsVOTable(StarTableOutput sto, StarTable table) throws IOException <hidden>{}</hidden>
</blockcode>
could be written in any of the following ways:
<blockcode>
<hidden>    void writeAsVOTable1(StarTableOutput sto, StarTable table) throws IOException {
</hidden>        sto.writeStarTable( table, System.out, new VOTableWriter() );
        sto.writeStarTable( table, "-", "votable" );
        sto.writeStarTable( table, "-", "uk.ac.starlink.votable.VOTableWriter" );<hidden>
    }</hidden>
</blockcode>
</p>

<p>However, since STIL v4, the string form may also specify
handler options in parenthesis, if such options are offered by
the handler in question.
So for instance the following are also possible:
<blockcode>
<hidden>    void writeAsVOTable2(StarTableOutput sto, StarTable table, OutputStream out) throws IOException {
</hidden>        sto.writeStarTable( table, "-", "votable(version=V12)" );
        sto.writeStarTable( table, "-", "uk.ac.starlink.votable.VOTableWriter(version=V12" );<hidden>
    }</hidden>
</blockcode>
which will write VOTables conforming to version 1.1 of the VOTable
handler specification.
These options are comma-separated <code>name=value</code> pairs,
and the <code>name</code> is mapped to bean-like configuration
methods on the handler class by use of the
<javadoc class="uk.ac.starlink.util.ConfigMethod" codetext="@ConfigMethod"/>
annotation.  See the <code>ConfigMethod</code> javadocs, and e.g. the
<javadoc class="uk.ac.starlink.votable.VOTableWriter"
         member="setVotableVersion(uk.ac.starlink.votable.VOTableVersion)"
         codetext="VOTableWriter.setVotableVersion"/>
method for an example.
When using STIL programmatically, these string-based options are
not really required since the mutator methods can be used,
but it can be very useful if the format string has been passed in
from a user.
</p>

<p>This configuration capability is much more flexible than the
few hard coded handler variant options that were provided in
STIL versions prior to version 4.
So e.g. "<code>votable(format=BINARY2,inline=true)</code>" is now preferred to
the older form "<code>votable-binary2-inline</code>",
though the older forms are retained for backward compatiblity.
</p>

 
</subsect>

<subsect id="tableBuilders">
<subhead><title>Supplied Input Handlers</title></subhead>

<p>The table input handlers supplied with STIL are listed in
this section, along with notes on any peculiarities they have in
turning a string into a <code>StarTable</code>.  
Each of the following subsections describes one or more
implementations of the <javadoc class="uk.ac.starlink.table.TableBuilder"/>
interface.  These can be used standalone, or with a
<javadoc class="uk.ac.starlink.table.StarTableFactory"/>.
Some of these formats can be detected automatically
(the <code>StarTableFactory</code> is capable of working out which
registered handler can work with a given file,
possibly using a supplied filename to help the guesswork)
while others must have the handler specified explicitly.
</p>

<p>In most cases the string supplied to name the table that 
<code>StarTableFactory</code> should read
is a filename or a URL, referencing a plain or compressed copy of
the stream from which the file is available.
In some cases an additional specifier can be given after a '#' 
character to give additional information about where in that stream
the table is located.
</p>


<subsubsect id="inFits">
<subhead><title>FITS</title></subhead>
&in-fits;

<p>There are actually two FITS-based input handlers,
<javadoc class="uk.ac.starlink.fits.FitsTableBuilder"/> and
<javadoc class="uk.ac.starlink.votable.FitsPlusTableBuilder"/>.
The former will work on any FITS file, and acquires its metadata
only from the FITS header of the relevant TABLE/BINTABLE HDU itself;
the latter works on <ref id="fitsPlus">FITS-plus</ref> files,
and acquires metadata from the embedded VOTable header.
</p>

<p>To retrieve all the tables in a multi-extension FITS files,
use one of the <code>makeStarTables</code> methods of 
<code>StarTableFactory</code> instead.
</p>

</subsubsect>

<subsubsect id="inColfits">
<subhead><title>Column-oriented FITS</title></subhead>
&in-colfits;

<p>Like <ref id="inFits">normal FITS</ref>,
there are two handlers for this format:
<javadoc class="uk.ac.starlink.votable.ColFitsPlusTableBuilder"/> 
(like <ref id="fitsPlus">FITS-plus</ref>) 
can read a VOTable as metadata from the primary HDU,
and
<javadoc class="uk.ac.starlink.fits.ColFitsTableBuilder"/>
does not.
This handler can read tables with more than the BINTABLE limit of
999 columns, as discussed in <ref id="wideFits"/>.
</p>

</subsubsect>

<subsubsect id="inVotable">
<subhead><title>VOTable</title></subhead>
&in-votable;

<p>The handler class for this format is
<javadoc class="uk.ac.starlink.votable.VOTableBuilder"/>.
</p>

<p>To retrieve all of the tables from a given VOTable document,
use one of the <code>makeStarTables</code> methods of StarTableFactory
instead.
</p>

<p>Much more detailed information about the VOTable I/O facilities,
which can be used independently of the generic I/O described in
this section, is given in <ref id="votable"/>.
</p>
</subsubsect>

<subsubsect id="inEcsv">
<subhead><title>ECSV</title></subhead>
&in-ecsv;

<p>The handler class for files of this format is
<javadoc class="uk.ac.starlink.ecsv.EcsvTableBuilder"/>.
</p>

</subsubsect>

<subsubsect id="inPds4">
<subhead><title>PDS4</title></subhead>
&in-pds4;

<p>The handler class for files of this format is
<javadoc class="uk.ac.starlink.pds4.Pds4TableBuilder"/>.
</p>

</subsubsect>

<subsubsect id="inMrt">
<subhead><title>Machine-Readable Table</title></subhead>
&in-mrt;

<p>The handler class for files of this format is
<javadoc class="uk.ac.starlink.table.formats.MrtTableBuilder"/>.
</p>

</subsubsect>

<subsubsect id="inParquet">
<subhead><title>Parquet</title></subhead>
&in-parquet;

<p>The handler class for files of this format is
<javadoc class="uk.ac.starlink.parquet.ParquetTableBuilder"/>.
</p>

</subsubsect>

<subsubsect id="inHapi">
<subhead><title>HAPI</title></subhead>
&in-hapi;

<p>The handler class for files of this format is
<javadoc class="uk.ac.starlink.hapi.HapiTableBuilder"/>.
</p>

</subsubsect>

<subsubsect id="inCdf">
<subhead><title>CDF</title></subhead>
&in-cdf;

<p>The handler class for files of this format is
<javadoc class="uk.ac.starlink.cdf.CdfTableBuilder"/>.
</p>

</subsubsect>

<subsubsect id="inFeather">
<subhead><title>Feather</title></subhead>
&in-feather;

<p>The handler class for files of this format is 
<javadoc class="uk.ac.starlink.feather.FeatherTableBuilder"/>.
</p>

</subsubsect>

<subsubsect id="inAscii">
<subhead><title>ASCII</title></subhead>
&in-ascii;

<p>The handler class for this format is
<javadoc class="uk.ac.starlink.table.formats.AsciiTableBuilder"/>.
</p>

</subsubsect>

<subsubsect id="inCsv">
<subhead><title>Comma-Separated Values</title></subhead>
&in-csv;

<p>The handler class for this format is
<javadoc class="uk.ac.starlink.table.formats.CsvTableBuilder"/>.
</p>

</subsubsect>

<subsubsect id="inTst">
<subhead><title>Tab-Separated Table</title></subhead>
&in-tst;

<p>The handler class for this format is
<javadoc class="uk.ac.starlink.table.formats.TstTableBuilder"/>.
</p>

</subsubsect>

<subsubsect id="inIpac">
<subhead><title>IPAC</title></subhead>
&in-ipac;

<p>The handler class for this format is
<javadoc class="uk.ac.starlink.table.formats.IpacTableBuilder"/>.
</p>

</subsubsect>

<subsubsect id="inGbin">
<subhead><title>GBIN</title></subhead>
&in-gbin;

<p>The handler class for this format is
<javadoc class="uk.ac.starlink.gbin.GbinTableBuilder"/>.
</p>

</subsubsect>

<subsubsect id="inWdc">
<subhead><title>WDC</title></subhead>
&in-wdc;

<p>The handler class for this format is
<javadoc class="uk.ac.starlink.table.formats.WDCTableBuilder"/>.
</p>

</subsubsect>


</subsect>

<subsect id="starTableWriters">
<subhead><title>Supplied Output Handlers</title></subhead>

<p>The table output handlers supplied with STIL are listed in this
section, along with any peculiarities they have in writing a 
<code>StarTable</code> to a destination given by a string 
(usually a filename).
As described in <ref id="genericOutput"/>, a <code>StarTableOutput</code>
will under normal circumstances permit output of a table in any
of these formats.  Which format is used is determined by the 
"format" string passed to 
<javadoc class="uk.ac.starlink.table.StarTableOutput"
         member="writeStarTable(uk.ac.starlink.table.StarTable, 
                                java.lang.String,
                                java.lang.String)"
         codetext="StarTableOutput.writeStarTable"/>
as described in the following subsections.
If a null format string
is supplied, the name of the destination string may be used 
to select a format (e.g. a destination ending ".fits" will, unless
otherwise specified, result in writing FITS format).
</p>

<p>Alternatively, the format-specific
<javadoc class="uk.ac.starlink.table.StarTableWriter"/>
implementation classes themselves can be used directly.
These have configuration methods corresponding to the
format name options listed below; consult the relevant
javadocs for details.
The main advantage of using a <code>StarTableOutput</code> to mediate
between output handler implementations
is to make it easy to switch between output formats,
especially if this is being done by the user at runtime.
</p>

<subsubsect id="outFits">
<subhead><title>FITS</title></subhead>
&out-fits;

<p>The handler class you should usually use for the various variants of
the FITS format is
<javadoc class="uk.ac.starlink.votable.UnifiedFitsTableWriter"/>,
though
<javadoc class="uk.ac.starlink.fits.FitsTableWriter"/> may be used
instead if fits-plus is not required,
and
<javadoc class="uk.ac.starlink.fits.HealpixFitsTableWriter"/>
is used for the HEALPix variant.
In earlier versions of STIL other <code>*FitsTableWriter</code>
classes were recommended, but these are now deprecated in favour
of the above (which have configuration methods to replicate the
behaviour of those legacy classes).
</p>

<p>To write the FITS header for the table extension, certain things
need to be known which may not be available from the <code>StarTable</code>
object being written; in particular the number of rows and the
size of any variable-sized arrays (including variable-length strings)
in the table.  This may necessitate two passes through the data to
do the write.
</p>

<p>To fix the value of the <code>TNULLn</code> magic value for
a given column on output, set the value of the 
<javadoc class="uk.ac.starlink.table.Tables" member="NULL_VALUE_INFO"
         codetext="Tables.NULL_VALUE_INFO"/>
auxiliary metadata value, e.g.:
<blockcode>
<hidden>static void setNullValue( ColumnInfo colInfo ) {</hidden>
    colInfo.setAuxDatum(new DescribedValue(Tables.NULL_VALUE_INFO, new Integer(-99)));
<hidden>}</hidden>
</blockcode>
</p>

<p>Writing columns containing (scalar or array) unsigned byte values
(<code>TFORMnn = 'B'</code>) cannot be done simply by providing
<code>byte</code> data to write, since the java byte type is signed.
To do it, you must provide a column of java <code>short</code>
(16-bit) integers, and set the value of the
<javadoc class="uk.ac.starlink.table.Tables" member="UBYTE_FLAG_INFO"
         codetext="Tables.UBYTE_FLAG_INFO"/>
auxiliary metadata item to <code>Boolean.TRUE</code>, e.g.:
<blockcode>
<hidden>static void setUnsignedByteFlag(ColumnInfo colInfo) {</hidden>
    colInfo.setAuxDatum(new DescribedValue(Tables.UBYTE_FLAG_INFO, Boolean.TRUE));
<hidden>}</hidden>
</blockcode>
</p>

<p>Writing columns containing (scalar or array) unsigned long values
cannot be done straightforwardly, since, like FITS, the Java long
type is signed.
Instead, you can provide a column of java <code>String</code> values
giving the integer representation of the numbers required,
and set the value of the
<javadoc class="uk.ac.starlink.fits.BintableStarTable" member="LONGOFF_INFO"
         codetext="BintableStarTable.LONGOFF_INFO"/>
auxiliary metadata item to the string representation of the offset 2**63,
e.g.:
<blockcode>
<hidden>static void setUnsignedLongFlag(ColumnInfo colInfo){</hidden>
   colInfo.setAuxDatum(new DescribedValue(BintableStarTable.LONGOFF_INFO, "9223372036854775808263"));
<hidden>}</hidden>
</blockcode>
This will result in the values being written, where in range, with FITS headers
<code>TFORMnn = 'K'</code>, <code>TZEROnn = '9223372036854775808263'</code>.
The same mechanism can be used for other long offsets if required
(though not for other integer types).
</p>

<p>See the "Binary Table Extension" section of the 
<webref url="http://fits.gsfc.nasa.gov/fits_standard.html"
        >FITS standard</webref> for more details of the FITS BINTABLE format.
These handlers can write tables with more than the BINTABLE limit of
999 columns, as discussed in <ref id="wideFits"/>.
</p>

<p>There is some support for the semi-standard
<webref url="&URL.HEALPIX_FITS;">HEALPix-FITS</webref>
serialization convention.
If some of the <code>HPX_*_INFO</code> metadata items defined by the class
<javadoc class="uk.ac.starlink.table.HealpixTableInfo"/> are present
in the output table's parameter list,
the corresponding HEALPix-specific FITS headers will be written
on a best-efforts basis into the output BINTABLE HDU.
This may or may not be good enough to make that FITS file readable by
external HEALPix-FITS-aware applications; one of the requirements
of the convention is that the HEALPix pixel index, if present,
appears in the first column of the table under the name "<code>PIXEL</code>".
An alternative is to use the handler
<javadoc class="uk.ac.starlink.fits.HealpixFitsTableWriter"/>,
which tries harder to write tables using the HEALPix convention.
This will fail unless the required <code>HPX_*_INFO</code> metadata
items mentioned above are present, and will reorder and rename
columns as required for maximum compatibility.
</p>

</subsubsect>

<subsubsect id="outVotable">
<subhead><title>VOTable</title></subhead>
&out-votable;

<p>The handler class for this format is
<javadoc class="uk.ac.starlink.votable.VOTableWriter"/>.
</p>

<p>For more control over writing VOTables, consult <ref id="voOutput"/>.
</p>

</subsubsect>

<subsubsect id="outEcsv">
<subhead><title>ECSV</title></subhead>
&out-ecsv;

<p>The handler class for this format is
<javadoc class="uk.ac.starlink.ecsv.EcsvTableWriter"/>.
</p>

</subsubsect>

<subsubsect id="outParquet">
<subhead><title>Parquet</title></subhead>
&out-parquet;

<p>The handler class for files of this format is
<javadoc class="uk.ac.starlink.parquet.ParquetTableWriter"/>.
</p>

</subsubsect>

<subsubsect id="outFeather">
<subhead><title>Feather</title></subhead>
&out-feather;

<p>The handler class for this format is
<javadoc class="uk.ac.starlink.feather.FeatherTableWriter"/>.
</p>

</subsubsect>

<subsubsect id="outAscii">
<subhead><title>ASCII</title></subhead>
&out-ascii;

<p>The handler class for this format is
<javadoc class="uk.ac.starlink.table.formats.AsciiTableWriter"/>.
</p>

</subsubsect>

<subsubsect id="outCsv">
<subhead><title>Comma-Separated Values</title></subhead>
&out-csv;

<p>The handler class for this format is
<javadoc class="uk.ac.starlink.table.formats.CsvTableWriter"/>.
</p>

</subsubsect>

<subsubsect id="outTst">
<subhead><title>Tab-Separated Table</title></subhead>
&out-tst;

<p>The handler class for this format is
<javadoc class="uk.ac.starlink.table.formats.TstTableWriter"/>.
</p>

</subsubsect>

<subsubsect id="outIpac">
<subhead><title>IPAC</title></subhead>
&out-ipac;

<p>The handler class for this format is
<javadoc class="uk.ac.starlink.table.formats.IpacTableWriter"/>.
</p>

</subsubsect>

<subsubsect id="outText">
<subhead><title>Plain Text</title></subhead>
&out-text;

<p>The handler class for this format is
<javadoc class="uk.ac.starlink.table.formats.TextTableWriter"/>.
</p>

</subsubsect>

<subsubsect id="outHtml">
<subhead><title>HTML</title></subhead>
&out-html;

<p>The handler class for this format is
<javadoc class="uk.ac.starlink.table.formats.HTMLTableWriter"/>.
</p>

</subsubsect>

<subsubsect id="outLatex">
<subhead><title>LaTeX</title></subhead>
&out-latex;

<p>The handler class for this format is
<javadoc class="uk.ac.starlink.table.formats.LatexTableWriter"/>.
</p>

</subsubsect>

<subsubsect>
<subhead><title>Mirage</title></subhead>
&out-mirage;

<p>The handler class for this format is
<javadoc class="uk.ac.starlink.mirage.MirageTableWriter"/>.
</p>
</subsubsect>

</subsect>

<subsect id="fitsConventions">
<subhead><title>Non-Standard FITS Conventions</title></subhead>

<p>STIL uses a few private conventions when writing and reading FITS files.
These are not private in the sense that non-STIL code is prevented from
cooperating with them, but STIL does not assume that other code,
or FITS tables it encounters, will use these conventions.
Instead, they offer (in some cases) added value for tables that were
written by STIL and are subsequently re-read by STIL, while causing
the minimum of trouble for non-STIL readers.
</p>

<subsubsect id="fitsPlus">
<subhead><title>FITS-plus</title></subhead>
&fits-plus;
</subsubsect>

<subsubsect id="wideFits">
<subhead><title>Wide FITS</title></subhead>
&fits-wide;
</subsubsect>

</subsect>

<subsect id="tableScheme">
<subhead><title>Table Schemes</title></subhead>

<p>When a string is used to specify a table,
it is usually the name of an external resource (file or URL)
containing a byte stream.
However, STIL also provides a pluggable interface for
referencing tables that do not originate from a byte scheme.
This is done using the <javadoc class="uk.ac.starlink.table.TableScheme"/>
interface, either directly, or via a
<code>StarTableFactory</code> that maintains a list of registered schemes.
</p>

<p>The form of a scheme specification is:
<verbatim>
   :&lt;scheme-name&gt;:&lt;scheme-specific-part&gt;
</verbatim>
where <code>&lt;scheme-name&gt;</code> is a registered scheme name or
the classname of a class that implements <code>TableScheme</code>
and has a no-arg constructor.
So for instance
<blockcode><hidden>void dummy11() throws IOException {
</hidden>    StarTable rows10 = new StarTableFactory().makeStarTable( ":loop:10" );
<hidden>}</hidden></blockcode>
creates a 10-row single-column table,
as described by the <ref id="scheme-loop">loop</ref> scheme documentation
below.
</p>

<p>The following subsections describe the schemes provided with STIL.
Others can be implemented and installed into the <code>StarTableFactory</code>
using the
<javadoc class="uk.ac.starlink.table.StarTableFactory"
         member="addScheme(uk.ac.starlink.table.TableScheme)"/> method,
or at runtime using the
<code>startable.schemes</code> system property.
</p>

&scheme-docs;

</subsect>


<subsect id="jdbc">
<subhead><title>I/O using SQL databases</title></subhead>

<p>With appropriate configuration, STIL can read and write
tables from a relational database such as 
<webref url="http://www.mysql.com/">MySQL</webref>.
You can obtain a <code>StarTable</code> which is the result 
of a given SQL query on a database table, 
or store a <code>StarTable</code> as a new table in an existing
database.
Note that this does <em>not</em> allow you to work on the database 'live'.
The classes that control these operations mostly live in the
<javadoc class="uk.ac.starlink.table.jdbc."/> package.
</p>

<p>If a username and/or password is required for use of the table, 
and this is not specified in the query URL,
<code>StarTableFactory</code> will arrange to prompt for it.
By default this prompt is to standard output (expecting a response
on standard input), but some other mechanism, for instance a 
graphical one, can be used by modifying the factory's
<javadoc class="uk.ac.starlink.table.StarTableFactory"
         member="getJDBCHandler()">JDBCHandler</javadoc>.
</p>

<subsubsect id="jdbcConfig">
<subhead><title>JDBC Configuration</title></subhead>

<p>Java/STIL does not come with the facility to use any particular
SQL database "out of the box"; some additional configuration must
be done before it can work.  
This is standard JDBC practice,
as explained in the documentation of the
<javadoc class="java.sql.DriverManager" docset="&coredocs;" 
         codetext="java.sql.DriverManager"/>
class.
In short, what you need to do is define the 
"<code>jdbc.drivers</code>" system property 
to include the name(s) of the JDBC driver(s) which you wish
to use.  For instance to enable use of MySQL with the Connector/J
database you might start up java with a command line like this:
<verbatim>
    java -classpath /my/jars/mysql-connector-java-3.0.8-stable-bin.jar:myapp.jar
         -Djdbc.drivers=com.mysql.jdbc.Driver 
         my.path.MyApplication
</verbatim>
One gotcha to note is that an invocation like this will not work
if you are using '<code>java -jar</code>' to invoke your application;
if the <code>-jar</code> flag is used then any class path set on
the command line or in the CLASSPATH environment variable or elsewhere
is completely ignored.  This is a consequence of Java's security model.
</p>

<p>For both the reader and the writer described below, the string
passed to specify the database query/table may or may not require
additional authentication before the read/write can be carried out.
The general rule is that an attempt will be made to connect with
the database without asking the user for authentication, 
but if this fails the user
will be queried for username and password, following which a second
attempt will be made.  If username/password has already been 
solicited, this will be used on subsequent connection attempts.
How the user is queried (e.g. whether it's done graphically or
on the command line) is controlled by the 
<javadoc class="uk.ac.starlink.table.jdbc.JDBCHandler"/>'s 
<javadoc class="uk.ac.starlink.table.jdbc.JDBCAuthenticator"/> object,
which can be set by application code if required.
If generic I/O is being used, you can use the
<code>get/setJDBCHandler</code> methods of the
<javadoc class="uk.ac.starlink.table.StarTableFactory"/> or
<javadoc class="uk.ac.starlink.table.StarTableOutput"/> being used.
</p>

<p>To the author's knowledge, STIL has so far been used
with the RDBMSs and drivers listed below.
Note however that
<strong>this information is incomplete and out of date</strong>.
If you have updates, feel free to pass them on and they may be
incorporated here.
<dl>
<dt><webref url="http://www.mysql.com/">MySQL</webref></dt>
<dd><p>MySQL has been tested on Linux with the
    <webref url="http://www.mysql.com/products/connector/j/"
            >Connector/J</webref> driver and seems to work;
    tested versions are server 3.23.55 with driver 3.0.8 and
    server 4.1.20 with driver 5.0.4.
    Sometimes tables with very many (hundreds of) columns cannot be
    written owing to SQL statement length restrictions.
    Note there is known to be a column metadata bug in version 3.0.6 of the
    driver which can cause a ClassCastException error when tables are written.
    </p></dd>

<dt><webref url="http://www.postgresql.org/">PostgreSQL</webref></dt>
<dd><p>PostgreSQL 7.4.1 apparently works with its own JDBC driver.
    Note the performance of this driver appears to be rather poor,
    at least for writing tables.
    </p></dd>

<dt><webref url="http://www.oracle.com/">Oracle</webref></dt>
<dd><p>You can use Oracle with the JDBC driver that comes as part of its
    <webref url="http://www.oracle.com/technetwork/database/database-technologies/instant-client/"
            >Basic Instant Client Package</webref>.
    URLs look something like "<code>jdbc:oracle:thin:@//hostname:1521/database#SELECT ...</code>".
    </p></dd>

<dt><webref url="http://www.microsoft.com/SQL">SQL Server</webref></dt>
<dd><p>There is more than one JDBC driver known to work with
    SQL Server, including
    <webref url="http://jtds.sourceforge.net/">jTDS</webref>
    and its own driver.
    Some evidence suggests that jTDS may be the better choice,
    but your mileage may vary.
    </p></dd>

<dt><webref url="https://www.sap.com/uk/products/sybase-ase.html"
            >Sybase ASE</webref></dt>
<dd><p>There has been a successful use of Sybase 12.5.2 and jConnect
    (jconn3.jar) using a JDBC URL like
    "<code>jdbc:sybase:Tds:hostname:port/dbname?user=XXX&amp;password=XXX#SELECT...</code>".
    An earlier attempt using Sybase ASE 11.9.2 failed.
    </p></dd>

</dl>
It is probably possible to use other RDBMSs and drivers,
but you may have to do some homework.
</p>

</subsubsect>

<subsubsect>
<subhead><title>Reading from a Database</title></subhead>

<p>See the <ref id="scheme-jdbc">JDBC Scheme</ref>.
</p>

</subsubsect>

<subsubsect>
<subhead><title>Writing to a Database</title></subhead>

<p>You can write out a <code>StarTable</code> as a new table in
an SQL-compatible RDBMS.  Note this will require appropriate access
privileges and may overwrite any existing table of the same name.
The general form of the string which specifies the destination of
the table being written is:
<verbatim><![CDATA[
    jdbc:<driver-specific-url>#<new-table-name>
]]></verbatim>
</p>

<p>Here is an example for MySQL with Connector/J:
<verbatim>
    jdbc:mysql://localhost/astro1?user=mbt#newtab
</verbatim>
which would write a new table called "newtab" in the MySQL database 
"astro1" on the local host with the access privileges of user mbt.
</p>

</subsubsect>

</subsect>

<subsect id="auth">
<subhead><title>Authentication</title></subhead>

<p>Where STIL is given an HTTP/HTTPS URL to read,
typically for table input,
in most cases it reads the resource via methods of the
<javadoc class="uk.ac.starlink.auth.AuthManager"/> class
rather than with a direct <code>URL.openStream()</code> or similar.
This has the effect of managing authentication in cases where
authentication requirements are specified in VO-compliant
ways by the remote service.
The default behaviour in this case is simply to deny access to
resources for which authentication is required, but if a non-trivial
<javadoc class="uk.ac.starlink.auth.UserInterface"/> object is
installed on the
<javadoc class="uk.ac.starlink.auth.AuthManager" member="getInstance()"
         >default <code>AuthManager</code> instance</javadoc>,
the user will be queried for credentials when a recognised
authentication challenge is encountered, and subsequent
access will be granted if authentication and authorization
are successful.
</p>

<p>This user interaction and negotiation of authentication is
invisible to STIL client code and requires no action
other than an initial setup of the preferred user interface.
This is typically done as follows:
<blockcode>
<hidden>void initAuth() {</hidden>
    AuthManager.getInstance().setUserInterface( UserInterface.CLI );
<hidden>}</hidden>
</blockcode>
This invocation would install a command-line user interface,
so that the user would be queried on the console for username and
password when required.
Other <code>UserInterface</code> implementations
(e.g. <javadoc class="uk.ac.starlink.auth.UserInterface" member="GUI"/>,
      <javadoc class="uk.ac.starlink.auth.UserInterface"
               member="createFixed(java.lang.String,java.lang.String)"/>)
can be used instead.
If no such setup is done
(or equivalently following <code>setUserInterface(null)</code>)
STIL should behave just as if URLs are dereferenced in the usual way.
However, the access is still being handled by the <code>AuthManager</code>
in this case, so unforseen differences in behaviour are not impossible.
</p>

<p><strong>Note:</strong>
These authentication arrangements in STIL are new at version 4.2,
and rely on VO standards that are still under discussion.
The API and behaviour may change in future releases,
and at time of writing not all data services that provide authentication
advertise it in a way that STIL can work with.
It is hoped that authentication interoperability
will improve in future versions of STIL and of server-side software.
The authentication management is currently handled by the
<javadoc class="uk.ac.starlink.auth."/> package bundled with STIL,
but the AUTH package may be split off into a standalone product at some
point in the future.
</p>

</subsect>

</sect>

<sect id="storagePolicy">
<subhead><title>Storage Policies</title></subhead> 

<p>Sometimes STIL needs to store the data from a table for later use.
This is necessary for instance when it creates a <code>StarTable</code> 
object by reading a VOTable document: it parses the XML by reading
through from start to finish, but must be able to supply the 
cell data through the <code>StarTable</code>'s data access methods
without doing another parse later.
Another example is when converting a sequential-only access table to
a random-access one (see the example below, and <ref id="randomTable"/>) - 
the data must be stored somewhere they can be accessed in a 
non-sequential way at a later date.
</p>

<p>The obvious thing to do is to store such data in object arrays
or lists in memory.  However, if the tables get very large this is no longer
appropriate because memory will fill up, and the application
will fail with an <code>OutOfMemoryError</code>
(Java's garbage collection based memory management means it 
is not much good at using virtual memory).  
So sometimes it would be better to store the data in a temporary disk file.
There may be other decisions to make as well, for instance the location
or format (perhaps row- or column-oriented) to use for 
a temporary disk file.
</p>

<p>Since on the whole you don't want to worry about these choices
when writing an application, STIL provides a way of dealing with them
which is highly configurable, but behaves in a 'sensible' way if
you don't take any special steps.  This is based around the
<javadoc class="uk.ac.starlink.table.StoragePolicy"/> class.
</p>

<p>A <code>StoragePolicy</code> is a factory for 
<javadoc class="uk.ac.starlink.table.RowStore"/> objects,
and a <code>RowStore</code> is an object to which you can write 
the metadata and data of a table once, and perform random access
reads on it at a later date.
Any of the STIL classes which need to do this sort of table data caching
use a <code>StoragePolicy</code> object; they have policy get/set methods
and usually constructors which take a <code>StoragePolicy</code> too.  
Application code which needs to stash table data away should follow
the same procedure.
</p>

<p>By way of example: the
<javadoc class="uk.ac.starlink.table.Tables"
         member="randomTable(uk.ac.starlink.table.StarTable)"/>
method takes a (possibly non-random-access) table and returns a random-access
one containing the same data.  Here is roughly how it does it:
<blockcode>
    static StarTable randomTable( StarTable seqTable, StoragePolicy policy )
            throws IOException {

        // Get a new row store object from the policy.
        RowStore rowStore = policy.makeRowStore();

        // Inform the row store about the table metadata - we do this by
        // passing the table itself, but this could be a data-less StarTable
        // object if the data were not available yet.
        rowStore.acceptMetadata( seqTable );

        // Loop over the rows in the input table, passing each one in turn
        // to the row store.
        RowSequence rowSeq = seqTable.getRowSequence();
        while ( rowSeq.next() ) {
            rowStore.acceptRow( rowSeq.getRow() );
        }

        // Inform the row store that there are no more rows to come.
        rowStore.endRows();

        // Extract and return a table from the row store.  This consists of
        // the metadata and data we've written in there, but is guaranteed
        // random access.
        return rowStore.getStarTable();
    }
</blockcode>
Most times you won't have to write this kind of code since the STIL
classes will be doing it behind the scenes for you.
</p>

<subsect id="policyList">
<subhead><title>Available Policies</title></subhead>

<p>The storage policies currently supplied as static members of
the <javadoc class="uk.ac.starlink.table.StoragePolicy"/> class
are as follows:
<dl>

<dt><javadoc class="uk.ac.starlink.table.StoragePolicy"
             member="PREFER_MEMORY"/></dt>
<dd><p>Stores table data in memory.  
    Currently implemented using an <code>ArrayList</code> of 
    <code>Object[]</code> arrays.
    </p></dd>

<dt><javadoc class="uk.ac.starlink.table.StoragePolicy"
             member="PREFER_DISK"/></dt>
<dd><p>Generally attempts to store data in a temporary disk file,
    using row-oriented storage (elements of each row are 
    mostly contiguous on disk).
    </p></dd>

<dt><javadoc class="uk.ac.starlink.table.StoragePolicy"
             member="ADAPTIVE"/></dt>
<dd><p>Stores table data in memory for relatively small tables,
    and in a temporary disk file for larger ones.  Storage is
    row-oriented.
    </p></dd>

<dt><javadoc class="uk.ac.starlink.table.StoragePolicy"
             member="SIDEWAYS"/></dt>
<dd><p>Generally attempts to store data in temporary disk files
    using column-oriented storage (elements of each column are
    contiguous on disk).
    This may be more efficient for certain access patterns
    for tables which are very large and, in particular, very wide.
    It's generally more expensive on system resources than
    PREFER_DISK however, (it writes and maps one file per column)
    so it is only the best choice in rather specialised circumstances.
    </p></dd>

<dt><javadoc class="uk.ac.starlink.table.StoragePolicy"
             member="DISCARD"/></dt>
<dd><p>Metadata is retained, but the rows are simply thrown away.
    The table returned from the row store has a row count of zero.
    </p></dd>

</dl>
</p>

<p>For the disk-based policies above (PREFER_DISK and SIDEWAYS),
if storage on disk is impossible (e.g. the security manager prevents
access to local disk) then they will fall back to memory-based storage.
They may also decide to use memory-based storage for rather small tables.
Any temporary disk files are written to the default temporary 
directory (<code>java.io.tmpdir</code>), 
and will be deleted when the RowStore is
garbage collected, or on normal termination of the JVM.
These policies are currently implemented using mapped file access.
</p>

<p>You are quite at liberty to implement and use your own 
<code>StoragePolicy</code> objects, possibly on top of existing ones.
For instance you could implement one which stored only the first
ten rows of any array.
</p>

</subsect>

<subsect>
<subhead><title>Default Policy</title></subhead>

<p>Any time a storage policy is required and has not been 
specified explicitly, STIL will get one by calling the static method 
<verbatim>
    StoragePolicy.getDefaultPolicy()
</verbatim>
(application code should follow the same procedure).
You can modify the value returned by this method in two ways:
you can use the <code>StoragePolicy.setDefaultPolicy()</code> static
method, or set the system property <code>startable.storage</code>
(this string is available as the constant
<javadoc class="uk.ac.starlink.table.StoragePolicy" member="PREF_PROPERTY"/>).
</p>

<p>The permissible values for <code>startable.storage</code> are currently
as follows:
<dl>

<dt>memory</dt>
<dd><p>Use the <code>PREFER_MEMORY</code> policy</p></dd>

<dt>disk</dt>
<dd><p>Use the <code>PREFER_DISK</code> policy</p></dd>

<dt>sideways</dt>
<dd><p>Use the <code>SIDEWAYS</code> policy</p></dd>

<dt>discard</dt>
<dd><p>Use the <code>DISCARD</code> policy</p></dd>

</dl>
Any other value is examined to see if it is the name of a loadable class
which is a subclass of <code>StoragePolicy</code> and has a no-arg
constructor.  If it is, an instance of this class is constructed 
and installed as the default.
</p>

<p>This means that without any code modification you can alter how
applications cache their table data by setting a system property
at runtime.  
The file <code>.starjava.properties</code> in the user's home directory
is examined during static initialization of <code>StoragePolicy</code>
for property assignments, so adding the line
<verbatim>
    startable.storage=disk
</verbatim>
in that file will have the same effect as specifying
<verbatim>
    -Dstartable.storage=disk
</verbatim>
on the java command line.
</p>

<p>If it has not been set otherwise, the 'default' default storage policy is
<javadoc class="uk.ac.starlink.table.StoragePolicy" member="ADAPTIVE"/>.
</p>

</subsect>

</sect>

<sect>
<subhead><title>GUI Support</title></subhead>

<p>STIL provides a number of facilities to make life easier if you 
are writing table-aware applications with a graphical user interface.
Most of these live in the 
<javadoc class="uk.ac.starlink.table.gui."/> package.
</p>

<subsect>
<subhead><title>Drag and Drop</title></subhead>

<p>From a user's point of view dragging is done 
by clicking down a mouse button on some visual
component (the "drag source") and moving the mouse until it is
over a second component (the "drop target") at which point the
button is released. 
The semantics of this are defined by the application,
but it usually signals that the dragged object (in this case a table)
has been moved or copied from the drag source to the drop target;
it's an intuitive and user-friendly way to offer transfer of an 
object from one place (application window) to another.
STIL's generic I/O classes provide methods to make drag and drop
of tables very straightforward.
</p>

<p>Dragging and dropping are handled separately but in either
case, you will need to construct a new 
<javadoc class="javax.swing.TransferHandler" docset="&coredocs;"
         codetext="javax.swing.TransferHandler"/>
object (subclassing <code>TransferHandler</code> itself and overriding
some methods as below) and install it on the Swing 
<code>JComponent</code> which is to
do be the drag source/drop target using its
<javadoc class="javax.swing.JComponent"
         member="setTransferHandler(javax.swing.TransferHandler)"
         docset="&coredocs;"/> method.
</p>

<p>To allow a Swing component to accept tables that are dropped onto it,
implement <code>TransferHandler</code>'s 
<javadoc class="javax.swing.TransferHandler"
         member="canImport(javax.swing.JComponent,
                           java.awt.datatransfer.DataFlavor[])"
         docset="&coredocs;"/>
and
<javadoc class="javax.swing.TransferHandler"
         member="importData(javax.swing.JComponent,
                            java.awt.datatransfer.Transferable)"
         docset="&coredocs;"/>
methods like this:
<blockcode>
<hidden>
    void processDroppedTable( StarTable table ) {}
</hidden>
    class TableDragTransferHandler extends TransferHandler {
        StarTableFactory factory = new StarTableFactory();

        public boolean canImport( JComponent comp, DataFlavor[] flavors ) {
            return factory.canImport( flavors );
        }

        public boolean importData( JComponent comp, Transferable dropped ) {
            try {
                StarTable table = factory.makeStarTable( dropped );
                processDroppedTable( table );
                return true;
            }
            catch ( IOException e ) {
                e.printStackTrace();
                return false;
            }
        }
    }
</blockcode>
Then any time a table is dropped on that window, your
<code>processDroppedTable</code> method will be called on it.
</p>

<p>To allow tables to be dragged off of a component, implement the
<javadoc class="javax.swing.TransferHandler"
         member="createTransferable(javax.swing.JComponent)"
         docset="&coredocs;"/>
method like this:
<blockcode>
<hidden>
    StarTable getMyTable() {return null;}
</hidden>
    class TableDropTransferHandler extends TransferHandler {
        StarTableOutput writer = new StarTableOutput();

        protected Transferable createTransferable( JComponent comp ) {
            StarTable table = getMyTable();
            return writer.transferStarTable( table );
        }
    }
</blockcode>
(you may want to override 
<javadoc class="javax.swing.TransferHandler"
         member="getSourceActions(javax.swing.JComponent)"
         docset="&coredocs;"/>
and
<javadoc class="javax.swing.TransferHandler"
         member="getVisualRepresentation(java.awt.datatransfer.Transferable)"
         docset="&coredocs;"/>
as well.
For some Swing components 
(see the
<webref url="http://docs.oracle.com/javase/1.5.0/docs/guide/swing/1.4/dnd.html"
        >Swing Data Transfer documentation</webref> for a list), 
this is all that is required.
For others, you will need to arrange to recognise the drag gesture
and trigger the <code>TransferHandler</code>'s 
<javadoc class="javax.swing.TransferHandler"
         member="exportAsDrag(javax.swing.JComponent,
                              java.awt.event.InputEvent,
                              int)"
         docset="&coredocs;"/> method as well;
you can use a <javadoc class="uk.ac.starlink.util.gui.DragListener"/> 
for this or see its source code for an example of how to do it.
</p>

<p>Because of the way that Swing's Drag and Drop
facilities work, this is not restricted to transferring tables 
between windows in the same application;
if you incorporate one or other of these capabilities into your
application, it will be able to exchange tables with any other
application that does the same, even if it's running in a different
Java Virtual Machine or on a different host - 
it just needs to have windows open on the same display device.  
<webref url="http://www.starlink.ac.uk/topcat/">TOPCAT</webref>
is an example; you can drag tables off of or onto the Table List in the
<docxref doc="sun253" loc="ControlWindow">Control Window</docxref>.
</p>


</subsect>

<subsect id="loadDialog">
<subhead><title>Table Load Dialogues</title></subhead>

<p>Some graphical components exist to make it easier to load or save tables.
They are effectively table-friendly alternatives to using a 
<javadoc class="javax.swing.JFileChooser" docset="&coredocs;"/>.
</p>

<p>In earlier versions of the library, there was a drop-in component
which gave you a ready-made dialogue to load tables from a wide range
of sources (local file, JDBC database, VO services, etc).
However, this was not widely used and imposed some restrictions
(dialogue modality) on the client application, so at STIL version 3.0
they have been withdrawn.
There is still a pluggable framework for implementing and using 
source-specific load dialogues, but client code now has to do a bit
more work to incorporate these into an actual application.
This is what <webref url="http://www.starlink.ac.uk/topcat/">TOPCAT</webref>
does.
</p>

<p>The main interface for this functionality is
<javadoc class="uk.ac.starlink.table.gui.TableLoadDialog"/>.
Implementations of this interface provide a GUI component which 
allows the user to specify what table will be loaded, and
performs the load of one or more tables based on this specification
when requested to do so.  An application can embed instances of
this into user-visible windows in order to provide load functionality.
A number of <code>TableLoadDialog</code> implementations are provided
within STIL for access to local disk, JDBC databases etc.
The starjava set contains more, including access to virtual observatory
services.
Further custom load types can be provided at runtime by providing
additional implementations of this interface.
The partial implementation
<javadoc class="uk.ac.starlink.table.gui.AbstractTableLoadDialog"/>
is provided for the convenience of implementors.
</p>

</subsect>

<subsect id="saveDialog">
<subhead><title>Table Save Dialogues</title></subhead>

<p><javadoc class="uk.ac.starlink.table.gui.TableSaveChooser"/>
is used for saving tables.
As well as allowing the user to select the table's destination,
it also allows selection of the output file format
from the list of those which the <code>StarTableOutput</code> 
knows about.
</p>

<p>Like the load dialogue, it provides a pluggable framework
for destination-specific GUI components.
These are provided by implementations of the
<javadoc class="uk.ac.starlink.table.gui.TableSaveDialog"/> class,
which can be plugged in as required.
Implementations for saving to local and remote filesystems and
JDBC databases are provided within STIL.
</p>

</subsect>

</sect>

<sect id="process">
<subhead><title>Processing StarTables</title></subhead>

<p>The <javadoc class="uk.ac.starlink.table."/> package provides
many generic facilities for table processing.
The most straightforward one to use is the 
<javadoc class="uk.ac.starlink.table.RowListStarTable"/>, described in the
<ref id="scratchTable">next subsection</ref>, which gives you a 
<code>StarTable</code> whose data are stored in memory, so
you can set and get cells or rows somewhat like a tabular version of an
<javadoc class="java.util.ArrayList" docset="&coredocs;"/>.
</p>

<p>For more flexible and efficient table processing, 
you may want to look at the later subsections below, which make
use of "pull-model" processing.
</p>

<p>If all you want to do is to read tables in or write them out however,
you may not need to read the information in this section at all.
</p>

<subsect id="scratchTable">
<subhead><title>Writable Table</title></subhead>

<p>If you want to store tabular data in memory,
possibly to output it using STIL's output facilities, 
the easiest way to do it is to use a 
<javadoc class="uk.ac.starlink.table.RowListStarTable"/> object.
You construct it with information about the kind of value which
will be in each column, and then populate it with data by adding rows. 
Normal read/write access is provided via a number of methods,
so you can insert and delete rows, set and get table cells, 
and so on.
</p>

<p>The following code creates and populates a table containing some 
information about some astronomical objects:
<blockcode>
<hidden>RowListStarTable getAstroTable() {</hidden>
    // Set up information about the columns.
    ColumnInfo[] colInfos = new ColumnInfo[ 3 ];
    colInfos[ 0 ] = new ColumnInfo( "Name", String.class, "Object name" );
    colInfos[ 1 ] = new ColumnInfo( "RA", Double.class, "Right Ascension" );
    colInfos[ 2 ] = new ColumnInfo( "Dec", Double.class, "Declination" );

    // Construct a new, empty table with these columns.
    RowListStarTable astro = new RowListStarTable( colInfos );

    // Populate the rows of the table with actual data.
    astro.addRow( new Object[] { "Owl nebula", 
                                 new Double( 168.63 ), new Double( 55.03 ) } );
    astro.addRow( new Object[] { "Whirlpool galaxy", 
                                 new Double( 202.43 ), new Double( 47.22 ) } );
    astro.addRow( new Object[] { "M108",
                                 new Double( 167.83 ), new Double( 55.68 ) } );
<hidden>return astro;}</hidden>
</blockcode>
</p>

</subsect>


<subsect>
<subhead><title>Wrap It Up</title></subhead>

<p>The <javadoc class="uk.ac.starlink.table.RowListStarTable"/>
described in the <ref id="scratchTable">previous section</ref>
is adequate for many table processing purposes, but since it controls
how storage is done (in a <code>List</code> of rows) it imposes a
number of restrictions - an obvious one is that all the data have
to fit in memory at once.
</p>

<p>A number of other classes are provided for more flexible table handling,
which make heavy use of the "pull-model" of processing, in which
the work of turning one table to another is not done at the time
such a transformation is specified, but only when the transformed table data
are actually required, for instance to write out to disk as a new
table file or to display in a GUI component such as a <code>JTable</code>.
One big advantage of this is that calculations which are never used
never need to be done.  Another is that in many cases it means you
can process large tables without having to allocate large amounts of
memory.  For multi-step processes, it is also often faster.
</p>

<p>The central idea to get used to is that of a "wrapper" table.
This is a table which wraps itself round another one (its "base" table), 
using calls to the base table to provide the basic data/metadata 
but making some some modifications before it returns it to the caller.
Tables can be wrapped around each other many layers deep like an onion.
This is rather like the way that 
<javadoc class="java.io.FilterInputStream" docset="&coredocs;"
         codetext="java.io.FilterInputStream"/>s
and to some extent
<javadoc class="java.util.stream.Stream" docset="&coredocs;"
         codetext="java.util.stream.Stream"/>s
work.
</p>

<p>Although they don't have to, most wrapper table classes inherit
from <javadoc class="uk.ac.starlink.table.WrapperStarTable"/>.
This is a no-op wrapper, which simply delegates all its calls to
the base table.  
Its subclasses generally leave most of the methods alone, but
override those which relate to the behaviour they want to change.
Here is an example of a very simple wrapper table, which simply
capitalizes its base table's name:
<blockcode>
    class CapitalizeStarTable extends WrapperStarTable {
        public CapitalizeStarTable( StarTable baseTable ) {
            super( baseTable );
        }
        public String getName() {
            return getBaseTable().getName().toUpperCase();
        }
    }
</blockcode>
As you can see, this has a constructor which passes the base table to
the <javadoc class="uk.ac.starlink.table.WrapperStarTable"
             member="WrapperStarTable(uk.ac.starlink.table.StarTable)"/>
constructor itself, which takes the base table as an argument.
Wrapper tables which do any meaningful wrapping will have
a constructor which takes a table, though they may take additional
arguments as well.
More often it is the data part which is modified and the metadata which is
left the same - some examples of this are given in <ref id="wrapExamples"/>.
Some wrapper tables wrap more than one table, 
for instance joining two base tables
to produce a third one which draws data and/or metadata from both
(e.g. <javadoc class="uk.ac.starlink.table.ConcatStarTable"/>,
      <javadoc class="uk.ac.starlink.table.JoinStarTable"/>).
</p>

<p>The idea of wrappers is used on some components other than 
<code>StarTable</code>s themselves: there are
<javadoc class="uk.ac.starlink.table.WrapperRowSequence"/>s and
<javadoc class="uk.ac.starlink.table.WrapperColumn"/>s as well.
These can be useful in implementing wrapper tables.
</p>

<p>Working with wrappers can often be more efficient than, 
for instance, doing a calculation
which goes through all the rows of a table calculating new values
and storing them in a <code>RowListStarTable</code>.
If you familiarise yourself with the set of wrapper tables supplied
by STIL, hopefully you will often find there are ones there which
you can use or adapt to do much of the work for you.
</p>
</subsect>

<subsect>
<subhead><title>Wrapper Classes</title></subhead>

<p>Here is a list of some of the wrapper classes provided,
with brief descriptions:
<dl>

<dt><javadoc class="uk.ac.starlink.table.ColumnPermutedStarTable"/></dt>
<dd><p>Views its base table with the columns in a different order.
</p></dd>

<dt><javadoc class="uk.ac.starlink.table.RowPermutedStarTable"/></dt>
<dd><p>Views its base table with the rows in a different order.
</p></dd>

<dt><javadoc class="uk.ac.starlink.table.RowSubsetStarTable"/></dt>
<dd><p>Views its base table with only some of the rows showing.
</p></dd>

<dt><javadoc class="uk.ac.starlink.table.gui.ProgressBarStarTable"/></dt>
<dd><p>Behaves exactly like its base table, but any RowSequence taken out
on it controls a 
<javadoc class="javax.swing.JProgressBar" docset="&coredocs;"/>, 
so the user can monitor progress in processing a table.
</p></dd>

<dt><javadoc class="uk.ac.starlink.table.ProgressLineStarTable"/></dt>
<dd><p>Like <code>ProgressBarStarTable</code>, but controls an animated
line of text on the terminal for command-line applications.
</p></dd>

<dt><javadoc class="uk.ac.starlink.table.JoinStarTable"/></dt>
<dd><p>Glues a number of tables together side-by-side.
</p></dd>

<dt><javadoc class="uk.ac.starlink.table.ConcatStarTable"/></dt>
<dd><p>Glues a number of tables together top-to-bottom.</p></dd>

</dl>
</p>
</subsect>

<subsect id="wrapExamples">
<subhead><title>Examples</title></subhead>

<p>This section gives a few examples of how STIL's wrapper classes
can be used or adapted to perform useful table processing.
If you follow what's going on here, you should be able to write
table processing classes which fit in well with the existing STIL
infrastructure.
</p>

<subsubsect id="sortExample">
<subhead><title>Sorted Table</title></subhead>

<p>This example shows how you can wrap a table to provide a sorted
view of it.  It subclasses 
<javadoc class="uk.ac.starlink.table.RowPermutedStarTable"/>,
which is a wrapper that presents its base table with the rows in a
different order.
<blockcode><![CDATA[
    class SortedStarTable extends RowPermutedStarTable {

        // Constructs a new table from a base table, sorted on a given column.
        SortedStarTable( StarTable baseTable, int sortCol ) throws IOException {

            // Call the superclass constructor - this will throw an exception
            // if baseTable does not have random access.
            super( baseTable );
            assert baseTable.isRandom();

            // Check that the column we are being asked to sort on has
            // a defined sort order.
            Class clazz = baseTable.getColumnInfo( sortCol ).getContentClass();
            if ( ! Comparable.class.isAssignableFrom( clazz ) ) {
                throw new IllegalArgumentException( clazz + " not Comparable" );
            }
           
            // Fill an array with objects which contain both the index of each
            // row, and the object in the selected column in that row.
            int nrow = (int) getRowCount();
            RowKey[] keys = new RowKey[ nrow ];
            for ( int irow = 0; irow < nrow; irow++ ) {
                Object value = baseTable.getCell( irow, sortCol );
                keys[ irow ] = new RowKey( (Comparable) value, irow );
            }

            // Sort the array on the values of the objects in the column;
            // the row indices will get sorted into the right order too.
            Arrays.sort( keys );

            // Read out the values of the row indices into a permutation array.
            long[] rowMap = new long[ nrow ];
            for ( int irow = 0; irow < nrow; irow++ ) {
                rowMap[ irow ] = keys[ irow ].index_;
            }

            // Finally set the row permutation map of this table to the one
            // we have just worked out.
            setRowMap( rowMap );
        }

        // Defines a class (just a structure really) which can hold 
        // a row index and a value (from our selected column).
        class RowKey implements Comparable { 
            Comparable value_;
            int index_;
            RowKey( Comparable value, int index ) {
                value_ = value;
                index_ = index;
            }
            public int compareTo( Object o ) {
                RowKey other = (RowKey) o;
                return this.value_.compareTo( other.value_ );
            }
        }
    }
]]></blockcode>
</p>
</subsubsect>

<subsubsect id="createExample">
<subhead><title>Turn a set of arrays into a StarTable</title></subhead>

<p>Suppose you have three arrays representing a set of points on the plane, 
giving an index number and an x and y coordinate, and you would like
to manipulate them as a StarTable.  One way is to use the 
<javadoc class="uk.ac.starlink.table.ColumnStarTable"/> class,
which gives you a table of a specified number of rows but initially
no columns, to which you can add data a column at a time.
Each added column is an instance of 
<javadoc class="uk.ac.starlink.table.ColumnData"/>;
the <javadoc class="uk.ac.starlink.table.ArrayColumn"/> class
provides a convenient implementation which wraps an array of objects
or primitives (one element per row).
<blockcode>
    StarTable makeTable( int[] index, double[] x, double[] y ) {
        int nRow = index.length;
        ColumnStarTable table = ColumnStarTable.makeTableWithRows( nRow );
        table.addColumn( ArrayColumn.makeColumn( "Index", index ) );
        table.addColumn( ArrayColumn.makeColumn( "x", x ) );
        table.addColumn( ArrayColumn.makeColumn( "y", y ) );
        return table;
    }
</blockcode>
</p>

<p>A more general way to approach this is to write a new implementation of
<javadoc class="uk.ac.starlink.table.StarTable"/>;
this is like what happens in Swing if you write your own
<javadoc class="javax.swing.table.TableModel" docset="&coredocs;"/>
to provide data for a 
<javadoc class="javax.swing.JTable" docset="&coredocs;"/>.
In order to do this you will usually want to subclass one of the existing 
implementations, probably
<javadoc class="uk.ac.starlink.table.AbstractStarTable"/>,
<javadoc class="uk.ac.starlink.table.RandomStarTable"/> or
<javadoc class="uk.ac.starlink.table.WrapperStarTable"/>.
Here is how it can be done:
<blockcode>
    class PointsStarTable extends RandomStarTable {

        // Define the metadata object for each of the columns.
        ColumnInfo[] colInfos_ = new ColumnInfo[] {
            new ColumnInfo( "Index", Integer.class, "point index" ),
            new ColumnInfo( "X", Double.class, "x co-ordinate" ),
            new ColumnInfo( "Y", Double.class, "y co-ordinate" ),
        };

        // Member variables are arrays holding the actual data.
        int[] index_;
        double[] x_;
        double[] y_;
        long nRow_;

        public PointsStarTable( int[] index, double[] x, double[] y ) {
            index_ = index;
            x_ = x;
            y_ = y;
            nRow_ = (long) index_.length;
        }

        public int getColumnCount() {
            return 3;
        }

        public long getRowCount() {
            return nRow_;
        }

        public ColumnInfo getColumnInfo( int icol ) {
            return colInfos_[ icol ];
        }

        public Object getCell( long lrow, int icol ) {
            int irow = checkedLongToInt( lrow );
            switch ( icol ) {
                case 0: return new Integer( index_[ irow ] );
                case 1: return new Double( x_[ irow ] );
                case 2: return new Double( y_[ irow ] );
                default: throw new IllegalArgumentException();
            }
        }
    }
</blockcode>
In this case it is only necessary to implement the 
<code>getCell</code>
method; 
<javadoc class="uk.ac.starlink.table.RandomStarTable"/> 
implements the other data access methods 
(<code>getRow</code>, <code>getRowSequence</code>, <code>getRowAccess</code>)
in terms of this.
Note that for more complicated behaviour, more methods may need
to be implemented.
</p>

</subsubsect>

<subsubsect>
<subhead><title>Add a new column</title></subhead>

<p>In this example we will append to a table
a new column in which each cell contains the
sum of all the other numeric cells in that row.
</p>

<p>First, we define a wrapper table class which contains only a single
column, the one which we want to add.
We subclass <javadoc class="uk.ac.starlink.table.AbstractStarTable"/>,
implementing its abstract methods as well as the <code>getCell</code>
method which may be required if the base table is random-access.
<blockcode><![CDATA[
    class SumColumnStarTable extends AbstractStarTable {

        StarTable baseTable_;
        ColumnInfo colInfo0_ = 
            new ColumnInfo( "Sum", Double.class, "Sum of other columns" );

        // Constructs a new summation table from a base table.
        SumColumnStarTable( StarTable baseTable ) {
            baseTable_ = baseTable;
        }

        // Has a single column.
        public int getColumnCount() {
            return 1;
        }

        // The single column is the sum of the other columns.
        public ColumnInfo getColumnInfo( int icol ) {
            if ( icol != 0 ) throw new IllegalArgumentException();
            return colInfo0_;
        }

        // Has the same number of rows as the base table.
        public long getRowCount() {
            return baseTable_.getRowCount();
        }

        // Provides random access iff the base table does.
        public boolean isRandom() {
            return baseTable_.isRandom();
        }

        // Get the row from the base table, and sum elements to produce value.
        public Object getCell( long irow, int icol ) throws IOException {
            if ( icol != 0 ) throw new IllegalArgumentException();
            return calculateSum( baseTable_.getRow( irow ) );
        }

        // Use a WrapperRowSequence based on the base table's RowSequence.
        // Wrapping a RowSequence is quite like wrapping the table itself;
        // we just need to override the methods which require new behaviour.
        public RowSequence getRowSequence() throws IOException {
            final RowSequence baseSeq = baseTable_.getRowSequence();
            return new WrapperRowSequence( baseSeq ) {
                public Object getCell( int icol ) throws IOException {
                    if ( icol != 0 ) throw new IllegalArgumentException();
                    return calculateSum( baseSeq.getRow() );
                }
                public Object[] getRow() throws IOException {
                    return new Object[] { getCell( 0 ) };
                }
            };
        }

        // Do the same for the RowAccess.
        public RowAccess getRowAccess() throws IOException {
            final RowAccess baseAcc = baseTable_.getRowAccess();
            return new WrapperRowAccess( baseAcc ) {
                public Object getCell( int icol ) throws IOException {
                    if ( icol != 0 ) throw new IllegalArgumentException();
                    return calculateSum( baseAcc.getRow() );
                }
                public Object[] getRow() throws IOException {
                    return new Object[] { getCell( 0 ) };
                }
            };
        }

        // getRowSplittable must also be overridden.  Here we use the
        // basic implementation from the utility class Tables,
        // but if you expect to be doing parallel processing a
        // more careful implementation based on the base table's
        // RowSplittable may be required.
        public RowSplittable getRowSplittable() throws IOException {
            return Tables.getDefaultRowSplittable( this );
        }

        // This method does the arithmetic work, summing all the numeric 
        // columns in a row (array of cell value objects) and returning 
        // a Double.
        Double calculateSum( Object[] row ) {
            double sum = 0.0;
            for ( int icol = 0; icol < row.length; icol++ ) {
                Object value = row[ icol ];
                if ( value instanceof Number ) {
                    sum += ((Number) value).doubleValue();
                }
            }
            return new Double( sum );
        }
    }
]]></blockcode>
We could use this class on its own if we just wanted a 1-column table 
containing summed values.
The following snippet however combines an instance of this class with
the table that it is summing from, resulting in an n+1 column table
in which the last column is the sum of the others:
<blockcode><![CDATA[
    StarTable getCombinedTable( StarTable inTable ) {
        StarTable[] tableSet = new StarTable[ 2 ];
        tableSet[ 0 ] = inTable;
        tableSet[ 1 ] = new SumColumnStarTable( inTable );
        StarTable combinedTable = new JoinStarTable( tableSet );
        return combinedTable;
    }
]]></blockcode>
</p>
</subsubsect>

</subsect>

</sect>

<sect id="votable">
<subhead><title>VOTable Access</title></subhead>

<p>VOTable is an XML-based format for storage and transmission of
tabular data, endorsed by the 
<webref url="http://www.ivoa.net">International Virtual Observatory 
Alliance</webref>,
who make available the schema
(<webref url="http://www.ivoa.net/xml/VOTable/v1.1"/>)
and documentation
(<webref url="http://www.ivoa.net/Documents/latest/VOT.html"/>).
The current version of STIL provides full support for versions 1.0, 1.1,
1.2, 1.3 and (draft) 1.4 of the format.
</p>

<p>As with the other handlers tabular data can be read from and
written to VOTable documents using the generic facilities described
in <ref id="io"/>.  However if you know you're going to be
dealing with VOTables the VOTable-specific parts of
the library can be used on their own; this may be more convenient and
it also allows access to some features specific to VOTables.
</p>

<p>The VOTable functionality is provided in the package
<javadoc class="uk.ac.starlink.votable."/>.
It has the following features:
<ul>
<li>Reads all VOTable data formats (TABLEDATA/FITS/BINARY)</li>
<li>Writes all VOTable data formats</li>
<li>Full access to document structure as a DOM</li>
<li>Full handling of array types</li>
<li>Flexible table output</li>
<li>Hybrid (SAX/DOM) parsing for memory &amp; CPU efficiency</li>
<li>Large table access (not limited by memory)</li>
<li>Fast</li>
<li>Resolution of relative URLs</li>
<li>Sequential/random access to tabular data</li>
<li>Best efforts parsing of non-conforming documents</li>
<li>Optional disk-based caching of table data when read</li>
</ul>
Most of these are described in subsequent sections.
</p>

<subsect id="voMapping">
<subhead><title>StarTable Representation of VOTables</title></subhead>

<p>As for other table formats, STIL represents a VOTable TABLE element
to the programmer as a <javadoc class="uk.ac.starlink.table.StarTable"/> object,
in this case a <javadoc class="uk.ac.starlink.votable.VOStarTable"/>.
Since the data models used by the <code>StarTable</code> interface 
and the VOTable definition of a TABLE
are pretty similar, it's mostly obvious how the one maps onto the other.
However, for those who want a detailed understanding of exactly how to 
interpret or control one from the other,
the following subsections go through these mappings in detail.
</p>

<subsubsect>
<subhead><title>Structure</title></subhead>

<p>It is important to understand that when STIL reads in a
a VOTable document, 
it creates one or more <ref id="starTable">StarTable</ref>s from 
one or all of the TABLE elements and then discards the document.
This means that information in the document's structure which does not
map naturally onto the StarTable model may be lost.
Such information currently includes
GROUPing of PARAMETERs and FIELDs, and the hierarchical relationship
between tables arranged in RESOURCE elements.
The meaning of references from FIELDs to COOSYS and TIMESYS elements
is preserved, though the textual details may change.
It is possible that some of this lost metadata will be stored in some way in 
VOTable-type StarTables
in the future, but some loss of information is an inevitable consequence
of the fact that STIL's model of a table is designed to provide a
generic rather than a VOTable-specific way of describing tabular data.
</p>

<p>If you want to avoid this kind of data loss, you should use
the custom VOTable document parser described in <ref id="votableDom"/>,
which retains the entire structure of the document.
</p>

</subsubsect>

<subsubsect>
<subhead><title>Parameters</title></subhead>

<p>When a <code>StarTable</code> is created by reading a TABLE element,
its parameter list (as accessed using 
<javadoc class="uk.ac.starlink.table.StarTable" member="getParameters()"/>)
is assembled by collecting all the PARAM elements in the TABLE element
and all the PARAM and INFO elements in its parent RESOURCE.
When a VOTable is written, all the parameters are written as
PARAMs in the TABLE.
</p>

</subsubsect>

<subsubsect>
<subhead><title>Column Metadata</title></subhead>

<p>There is a one-to-one correspondence between a <code>StarTable</code>'s
<code>ColumnInfo</code> objects (accessed using 
<javadoc class="uk.ac.starlink.table.StarTable" member="getColumnInfo(int)"/>)
and the FIELD elements contained in the corresponding TABLE.
The attributes of each fields are interpreted (for reading)
or determined (for writing) in a number of different ways:
<ul>
<li><code>datatype</code> and <code>arraysize</code> values depend
    on the class and shape of objects held in the column.
    </li>
<li><code>name</code>, <code>unit</code>, <code>ucd</code> and
    <code>Utype</code> values
    can be accessed using the corresponding methods on the 
    <code>ColumnInfo</code> object 
    (<code>get/set</code> <code>Name()</code>, <code>UnitString()</code>,
    <code>UCD()</code> and <code>Utype()</code> respectively).
    </li>
<li><code>ID</code> <code>width</code>, 
    <code>precision</code> and <code>type</code>
    are held as String-type <em>auxiliary metadata</em> items in the 
    <code>ColumnInfo</code> object, keyed by constants defined by
    the <code>VOStarTable</code> class
    (<javadoc class="uk.ac.starlink.votable.VOStarTable"
              member="ID_INFO"/>,
     <javadoc class="uk.ac.starlink.votable.VOStarTable"
              member="WIDTH_INFO"/>,
     <javadoc class="uk.ac.starlink.votable.VOStarTable"
              member="PRECISION_INFO"/> and
     <javadoc class="uk.ac.starlink.votable.VOStarTable"
              member="TYPE_INFO"/> respectively).
     </li>
<li><code>LINK</code> elements are represented by URL-type
    <em>auxiliary metadata</em> 
    items in the <code>ColumnInfo</code> object, keyed by their 
    <code>title</code> or, if it doesn't have one, <code>ID</code> attribute.
    </li>
<li>Magic bad values for integer columns are represented by the
    <javadoc class="uk.ac.starlink.table.Tables" member="NULL_VALUE_INFO"
             codetext="Tables.NULL_VALUE_INFO"/>
    auxiliary metadata item.</li>
<li>Columns representing <code>unsignedByte</code> values are marked with
    a <code>Boolean.TRUE</code> value of their
    <javadoc class="uk.ac.starlink.table.Tables" member="UBYTE_FLAG_INFO"
             codetext="Tables.UBYTE_FLAG_INFO"/>
    auxiliary metadata item, as well as having a java <code>short</code>
    integer data type (the java <code>byte</code> type is no good because
    it's signed).</li>
</ul>
</p>

<p>So if you have read a VOTable and want to determine the 
<code>name</code>, <code>ucd</code> and <code>ID</code> attributes
of the first column and its magic blank value, you can do it like this:
<blockcode>
<hidden>private StarTable readVOTable(){return null;};void dummy7() {</hidden>
    StarTable table = readVOTable();
    ColumnInfo col0 = table.getColumnInfo(0);
    String name0 = col0.getName();
    String ucd0 = col0.getUCD();
    String id0 = (String) col0.getAuxDatumValue(VOStarTable.ID_INFO,
                                                String.class);
    Number blank0 = (Number) col0.getAuxDatumValue(Tables.NULL_VALUE_INFO,
                                                   Number.class);
<hidden>}</hidden>
</blockcode>
</p>

<p>And if you are preparing a table to be written 
as a VOTable and want to set the 
<code>name</code>, <code>ucd</code> and <code>ID</code> attributes
of a certain column, fix it to use a particular magic null integer value,
and have it contain an element
<code>&lt;LINK title='docs' href='...'&gt;</code>"
 you can set its <code>ColumnInfo</code> up like this:
<blockcode>
    ColumnInfo configureColumn(String name, String ucd, String id, Number blank,
                               URL docURL) {
        ColumnInfo info = new ColumnInfo(name);
        info.setUCD(ucd);
        info.setAuxDatum(new DescribedValue(VOStarTable.ID_INFO, id));
        info.setAuxDatum(new DescribedValue(Tables.NULL_VALUE_INFO, blank));
        info.setAuxDatum(new DescribedValue(new URLValueInfo("docs",null), docURL));
        return info;
    }
</blockcode>
</p>

</subsubsect>

<subsubsect id="voDatamap">
<subhead><title>Data Types</title></subhead>

<p>The class and shape of each column in a <code>StarTable</code>
(accessed using the <code>get/setContentClass()</code> and
<code>get/setShape()</code> methods of
<javadoc class="uk.ac.starlink.table.ColumnInfo"/>)
correspond to the 
<code>datatype</code> and <code>arraysize</code> attributes of the 
corresponding FIELD element in the VOTable.
You are not expected to access the 
<code>datatype</code> and <code>arraysize</code> attributes directly.
</p>

<p>How Java classes map to VOTable data types for the content of 
columns is similar to elsewhere in STIL.
In general, scalars are represented by the corresponding primitive
wrapper class (<code>Integer</code>, <code>Double</code>, <code>Boolean</code>
etc), and arrays are represented by an array of primitives
of the corresponding type (<code>int[]</code>, <code>double[]</code>,
<code>boolean[]</code>).
Arrays are only ever one-dimensional - information about any
multidimensional shape they may have is supplied separately
(use the <code>getShape</code> method on the corresponding
<javadoc class="uk.ac.starlink.table.ColumnInfo"/>).
There are a couple of exceptions to this: arrays with
<code>datatype="char"</code> or <code>"unicodeChar"</code>
are represented by String objects
since that is almost always what is intended
(n-dimensional arrays of <code>char</code> are treated as if they were
(n-1)-dimensional arrays of Strings),
and <code>unsignedByte</code> types are represented as if they were
<code>short</code>s, since in Java bytes are always signed.
Complex values are represented as if they were an array of the corresponding
type but with an extra dimension of size two (the most rapidly varying).
</p>

<p>The following table summarises how all VOTable datatypes are represented:
<verbatim><![CDATA[
    datatype        Class for scalar   Class for arraysize>1
    --------        ----------------   ---------------------
    boolean         Boolean            boolean[]
    bit             boolean[]          boolean[]
    unsignedByte    Short              short[]
    short           Short              short[]
    int             Integer            int[]
    long            Long               long[]
    char            Char               String or String[]
    unicodeChar     Char               String or String[]
    float           Float              float[]
    double          Double             double[]
    floatComplex    float[]            float[]
    doubleComplex   double[]           double[]
]]></verbatim>
</p>


</subsubsect>


</subsect>

<subsect id='voFormats'>
<subhead><title>DATA Element Serialization Formats</title></subhead>

<p>The actual table data (the cell contents, as opposed to metadata
such as column names and characteristics) 
in a VOTable are stored in a TABLE's DATA element.
The VOTable standard allows it to be stored in a number of ways;
It may be present as XML elements in a TABLEDATA element, 
or as binary data in one of two serialization formats, BINARY or FITS;
if binary the data may either be available externally from a given URL
or present in a STREAM element encoded as character data using the
Base64 scheme
(Base64 is defined in
<webref url="http://www.ietf.org/rfc/rfc2045.txt">RFC2045</webref>).
For VOTable version &gt;=1.3, BINARY is deprecated in favour of the
new BINARY2 format.  See the VOTable 1.3 standard for discussion
of the differences.
</p>

<p>To summarise, the possible formats are:
<ul>
<li>TABLEDATA</li>
<li>BINARY at external URL <em>(deprecated at VOTable 1.3+)</em></li>
<li>BINARY inline (base64-encoded) <em>(deprecated at VOTable 1.3+)</em></li>
<li>BINARY2 at external URL <em>(VOTable 1.3+ only)</em></li>
<li>BINARY2 inline (base64-encoded) <em>(VOTable 1.3+ only)</em></li>
<li>FITS at external URL</li>
<li>FITS inline (base64-encoded)</li>
</ul>
and here are examples of what the different forms of the DATA 
element look like:
<verbatim>
<![CDATA[
  <!-- TABLEDATA format, inline -->
  <DATA>
    <TABLEDATA>
      <TR> <TD>1.0</TD> <TD>first</TD>  </TR>
      <TR> <TD>2.0</TD> <TD>second</TD> </TR>
      <TR> <TD>3.0</TD> <TD>third</TD>  </TR>
    </TABLEDATA>
  </DATA>

  <!-- BINARY format, inline -->
  <DATA>
    <BINARY>
      <STREAM encoding='base64'>
      P4AAAAAAAAVmaXJzdEAAAAAAAAAGc2Vjb25kQEAAAAAAAAV0aGlyZA==
      </STREAM>
    </BINARY>
  </DATA>

  <!-- BINARY format, to external file -->
  <DATA>
    <BINARY>
      <STREAM href="file:/home/mbt/BINARY.data"/>
    </BINARY>
  </DATA>
]]>
</verbatim>
External files may also be compressed using gzip.
The FITS ones look pretty much like the binary ones, though in the case
of an externally referenced FITS file, the file in the URL is a 
fully functioning FITS file with (at least) one BINTABLE extension.
</p>

<p>In the case of FITS data the VOTable standard leaves it up to the
application how to resolve differences between metadata in the 
FITS stream and in the VOTable which references it.  For a legal
VOTable document STIL behaves as if it uses the metadata from the 
VOTable and ignores any in FITS headers, but if they are inconsistent
to the extent that the FIELD elements and FITS headers describe different
kinds of data, results may be unpredictable.
</p>

<p>At the time of writing, most VOTables in the wild are written 
in TABLEDATA format.
This has the advantage that it is human-readable, and it's easy 
to write and read using standard XML tools.  However, it is not 
a very suitable format for large tables because of the high
overheads of processing time and storage/bandwidth, especially 
for numerical data.
For efficient transport of large
tables therefore, one of the binary formats is recommended.
</p>

<p>STIL can read and write VOTables in any of these formats.
In the case of reading, you just need to point the library at
a document or TABLE element and it will work out what format the 
table data are stored in and decode them accordingly - the user
doesn't need to know whether it's TABLEDATA or external gzipped FITS
or whatever.
In the case of writing, you can choose which format is used.
</p>
</subsect>

<subsect>
<subhead><title>Reading VOTables</title></subhead>

<p>STIL offers a number of options for reading a VOTable document,
described in the following sections. 
If you just want to read one table or all of the tables
stored in a VOTable document, 
obtaining the result as one or more <ref id="starTable">StarTable</ref>,
the most convenient way is to use the VOTable handler's versions
of the STIL generic table reading methods, 
as described in <ref id="voGeneric"/>.  
If you need access to the
structure of the VOTable document however, you can use the DOM or
SAX-like facilites described in the sections <ref id="votableDom"/> and
<ref id="votableSax"/> below.
</p>

<subsubsect id="voGeneric">
<subhead><title>Generic VOTable Read</title></subhead>

<p>The simplest way to read tables from a VOTable document is to use 
the generic table reading method described in <ref id="genericInput"/> 
(or <ref id="genericStreamInput"/> for streaming)
in which you just submit the location of a document to a 
<javadoc class="uk.ac.starlink.table.StarTableFactory"/>,
and get back one or more 
<javadoc class="uk.ac.starlink.table.StarTable"/> objects.
If you're after one of several TABLE elements in a document,
you can specify this by giving its number as the URL's
fragment ID (the bit after the '#' sign, or the third argument of
<javadoc class="uk.ac.starlink.votable.VOTableBuilder"
         member="streamStarTable(java.io.InputStream,
                                 uk.ac.starlink.table.TableSink,
                                 java.lang.String)"/>
for streaming).
</p>

<p>The following code would give you <code>StarTable</code>s
read from the first and fourth TABLE elements in the file "tabledoc.xml":
<blockcode>
<hidden>void dummy4() throws IOException {</hidden>
    StarTableFactory factory = new StarTableFactory();
    StarTable tableA = factory.makeStarTable( "tabledoc.xml", "votable" );
    StarTable tableB = factory.makeStarTable( "tabledoc.xml#3", "votable" );
<hidden>}</hidden>
</blockcode>
or equivalently
<blockcode>
<hidden>void dummy4a() throws IOException {</hidden>
    VOTableBuilder votBuilder = new VOTableBuilder();
    boolean wantRandom = false;
    StoragePolicy policy = StoragePolicy.getDefaultPolicy();
    StarTable tableA =
        votBuilder.makeStarTable( DataSource.makeDataSource( "tabledoc.xml" ),
                                  wantRandom, policy );
    StarTable tableB =
        votBuilder.makeStarTable( DataSource.makeDataSource( "tabledoc.xml#3" ),
                                  wantRandom, policy );
<hidden>}</hidden>
</blockcode>
Note this will perform two separate parses of the document,
one for each table built.
</p>

<p>If you want all the tables in the document, do this:
<blockcode>
<hidden>void dummy4b() throws IOException {</hidden>
    VOTableBuilder votBuilder = new VOTableBuilder();
    DataSource datsrc = DataSource.makeDataSource( "tabledoc.xml" );
    StoragePolicy policy = StoragePolicy.getDefaultPolicy();
    TableSequence tseq = votBuilder.makeStarTables( datsrc, policy );
    List tList = new ArrayList();
    for ( StarTable table; ( table = tseq.nextTable() ) != null; ) {
        tList.add( table );
    }
<hidden>}</hidden>
</blockcode>
which only performs a single pass and so is more efficient.
</p>

<p>All the data and metadata from the TABLEs in the 
VOTable document are available from the resulting 
<code>StarTable</code> objects,
as table parameters, <javadoc class="uk.ac.starlink.table.ColumnInfo"/>s
or the data themselves.
If you are just trying to extract the data and metadata from a 
single TABLE element somewhere in a VOTable document, this 
procedure is probably all you need.
</p>


</subsubsect>

<subsubsect id="votableDom">
<subhead><title>Table-Aware DOM Processing</title></subhead>

<p>VOTable documents consist of a hierarchy of RESOURCE, DEFINITIONS, 
COOSYS, TABLE elements and so on.  The methods described in the 
previous subsection effectively approximate this as a flat list of
TABLE elements.
If you are interested in the structure of the VOTable document
in more detail than the table items that can be extracted from it,
you will need to examine it in a different way, based on the XML.
The usual way of doing this for an XML document in Java is to
obtain a DOM (Document Object Model) based on the XML - 
this is an API defined by the 
<webref url="http://www.w3.org/TR/DOM-Level-2-Core/">W3C</webref> 
representing a tree-like structure of elements and attributes which can be 
navigated by using methods like 
<javadoc class="org.w3c.dom.Node" member="getFirstChild()"
         docset="&coredocs;"/> and
<javadoc class="org.w3c.dom.Node" member="getParentNode()"
         docset="&coredocs;"/>.
</p>

<p>STIL provides you with a DOM which can be viewed exactly like a 
standard one (it implements the DOM API) but has some special features.
<ul>
<li>All elements in it are instances of the 
    <javadoc class="uk.ac.starlink.votable.VOElement"/> class
    (which itself implements the DOM 
    <javadoc class="org.w3c.dom.Element" docset="&coredocs;"/> 
    interface).
    This provides a few convenience methods such as 
    <javadoc class="uk.ac.starlink.votable.VOElement" 
             member="getChildrenByName(java.lang.String)"/>
    which can be useful but don't
    do anything that you couldn't do with the <code>Element</code> interface
    alone.
    </li>

<li>Some of the elements, according to their name, are instances of
    specialised subclasses of <code>VOElement</code> which provide 
    methods specific to their r&#xf4;le in a VOTable document.
    For instance every GROUP element in the tree is represented
    by a <javadoc class="uk.ac.starlink.votable.GroupElement"/>;
    this class has a method
    <javadoc class="uk.ac.starlink.votable.GroupElement" member="getFields()"/>
    which returns all the FIELD elements associated with that group
    (this method examines its FIELDref children and locates their 
    FIELD elements elsewhere in the DOM).
    The various specific element types are not considered in detail here -
    see the javadocs for the subclasses of
    <javadoc class="uk.ac.starlink.votable.VOElement"/>.
    </li>

<li>The most important of these special element subclasses is
    <javadoc class="uk.ac.starlink.votable.TableElement"/>.
    A <code>TableElement</code> can provide the table data 
    stored within it; to access these data you don't need to know whether
    it is stored in TABLEDATA, FITS or BINARY form etc.
    </li>

<li>Full ID/ref cross-referencing is supported for elements which have
    ID attributes in the VOTable specification - this is required
    so that for instance FIELDref elements can access their FIELDs,
    and TABLE elements can define their structure by reference to
    previously defined ones.
    If you need to locate cross-references by hand you can use the
    <javadoc class="org.w3c.dom.Document"
             member="getElementById(java.lang.String)" docset="&coredocs;"/>
    method.
    </li>

<li>In most cases, the DOM you acquire will not contain the bulk data 
    in the VOTable XML.  Specifically, the children of TABLEDATA
    elements (a lot of TR and TDs) and of STREAM elements
    (long Base64-encoded strings containing FITS/binary data) 
    will be absent.  
    User code inspecting the DOM is rarely interested in these elements,
    only in the table data they represent, and this can be obtained
    from the corresponding TABLE element.
    </li>

<li>The DOM is modifiable - that is you can add, remove and relocate nodes
    within it in the standard ways permitted by the DOM API.
    </li>
</ul>
</p>

<p>To acquire this DOM you will use a
<javadoc class="uk.ac.starlink.votable.VOElementFactory"/>,
usually feeding a <code>File</code>, <code>URL</code> or 
<code>InputStream</code> to one of its <code>makeVOElement</code> methods.
The bulk data-less DOM mentioned above is possible because
the <code>VOElementFactory</code> processes the XML document using SAX, 
building a DOM as it goes along, but when
it gets to the bulk data-bearing elements it interprets their data 
on the fly and stores it in a
form which can be accessed efficiently later
rather than inserting the elements into the DOM.
SAX (Simple API for XML)
is an event driven processing model which, unlike DOM,
does not imply memory usage that scales with the size of the document.
In this way any but the weirdest VOTable documents
can be turned into a DOM of very modest size.
This means you can have all the benefits of a DOM 
(full access to the hierarchical structure) without the disadvantages
usually associated with DOM-based VOTable processing
(potentially huge memory footprint).
Of course in order to be accessed later, 
the data extracted from a stream of TR elements or from 
the inline content of a STREAM element has to get stored somewhere.
Where it gets put is determined by the 
<code>VOElementFactory</code>'s <code>StoragePolicy</code>
(see <ref id="storagePolicy"/>).
</p>

<p>If for some reason you want to work with a full DOM containing
the TABLEDATA or STREAM children, you can parse the document to 
produce a DOM <code>Document</code> or <code>Element</code> 
as usual (e.g. using a 
<javadoc class="javax.xml.parsers.DocumentBuilder" docset="&coredocs;"/>)
and feed that to one of the the 
<javadoc class="uk.ac.starlink.votable.VOElementFactory"/>'s 
<code>makeVOElement</code> methods instead.
</p>

<p>Having obtained your DOM, 
the easiest way to access the data of a TABLE element is to locate
the relevant <javadoc class="uk.ac.starlink.votable.TableElement"/>
in the tree
and turn it into a <javadoc class="uk.ac.starlink.table.StarTable"/>
using the <javadoc class="uk.ac.starlink.votable.VOStarTable"/>
adapter class.
You can interrogate the resulting object for its data and metadata 
in the usual way as described in <ref id="starTable"/>.
This <code>StarTable</code> may or may not provide random access
(<javadoc class="uk.ac.starlink.table.StarTable" member="isRandom()"/>
may or may not return true), according to how the data were obtained.
If it's a binary stream from a remote URL it may only be possible to
read rows from start to finish a row at a time, but if it was in
TABLEDATA form it will be possible to access cells in any order.
If you need random access for a table and you don't have it 
(or don't know if you do) then use the methods described in 
<ref id="randomTable"/>.
</p>

<p>It is possible to access the table data directly (without
making it into a <code>StarTable</code>) by using the 
<javadoc class="uk.ac.starlink.votable.TableElement" member="getData()"/>
method of the <code>TableElement</code>, but in this case you need
to work a bit harder to extract some of the data and metadata in
useful forms.  See the <javadoc class='uk.ac.starlink.votable.TabularData'/>
documentation for details.
</p>

<p>One point to note about <code>VOElementFactory</code>'s parsing is that
it is not restricted to elements named in the VOTable
standard, so a document which does not conform to the standard can
still be processed as a VOTable if parts of it contain VOTable-like
structures.
</p>

<p>Here is an example of using this approach to read the structure
of a, possibly complex, VOTable document.  This program locates the
third TABLE child of the first RESOURCE element and prints out its 
column titles and table data.
<blockcode><![CDATA[
    void printThirdTable( File votFile ) throws IOException, SAXException {

        // Create a tree of VOElements from the given XML file.
        VOElement top = new VOElementFactory().makeVOElement( votFile );

        // Find the first RESOURCE element using standard DOM methods.
        NodeList resources = top.getElementsByTagName( "RESOURCE" );
        Element resource = (Element) resources.item( 0 );

        // Locate the third TABLE child of this resource using one of the
        // VOElement convenience methods.
        VOElement vResource = (VOElement) resource;
        VOElement[] tables = vResource.getChildrenByName( "TABLE" );
        TableElement tableEl = (TableElement) tables[ 2 ];

        // Turn it into a StarTable so we can access its data.
        StarTable starTable = new VOStarTable( tableEl );

        // Write out the column name for each of its columns.
        int nCol = starTable.getColumnCount();
        for ( int iCol = 0; iCol < nCol; iCol++ ) {
            String colName = starTable.getColumnInfo( iCol ).getName();
            System.out.print( colName + "\t" );
        }
        System.out.println();

        // Iterate through its data rows, printing out each element.
        for ( RowSequence rSeq = starTable.getRowSequence(); rSeq.next(); ) {
            Object[] row = rSeq.getRow();
            for ( int iCol = 0; iCol < nCol; iCol++ ) {
                System.out.print( row[ iCol ] + "\t" );
            }
            System.out.println();
        }
    }
]]></blockcode>
</p>

<p>Versions of STIL prior to V2.0 worked somewhat differently to this -
they produced a tree structure representing the VOTable document which
resembled, but wasn't, a DOM (it didn't implement the W3C DOM API).
The current approach is more powerful and in some cases less fiddly to use.
</p>

</subsubsect>

<subsubsect id="votableSax">
<subhead><title>Table-Aware SAX Processing</title></subhead>

<p>SAX (Simple API for XML) is an event-based model for processing XML streams,
defined in the <javadoc class="org.xml.sax." docset="&coredocs;"/> package.
While generally a bit more effort to use than DOM, it provides
more flexibility and possibilities for efficiency,
since you can decide what to do with each element rather than 
always store it in memory.  Although a DOM built using the 
mechanism described in <ref id="votableDom">the previous section</ref>
will usually itself be pretty small, it will normally have to store 
table data somewhere in memory or on disk, so if you don't need, 
and wish to avoid, this overhead, you'd better use event-based processing
directly.  This section describes how to do that.
</p>

<p>The basic tool to use for VOTable-aware SAX-based processing is a 
<javadoc class="uk.ac.starlink.votable.TableContentHandler"/>,
which is a SAX <javadoc class="org.xml.sax.ContentHandler"
                        docset="&coredocs;"/> implementation
that monitors all the SAX events and when it comes across a 
TABLE element containing DATA
it passes SAX-like messages to a user-supplied 
<javadoc class="uk.ac.starlink.votable.TableHandler"/> which can do
what it likes with them.
<code>TableHandler</code> is a callback interface for dealing with
table metadata and data events defined by STIL in the spirit of the
existing SAX callback interfaces such as <code>ContentHandler</code>,
<code>LexicalHandler</code> etc.  You define a <code>TableHandler</code>
by implementing the methods 
<javadoc class="uk.ac.starlink.votable.TableHandler"
         member="startTable(uk.ac.starlink.table.StarTable)"/>,
<javadoc class="uk.ac.starlink.votable.TableHandler"
         member="rowData(java.lang.Object[])"/> and
<javadoc class="uk.ac.starlink.votable.TableHandler"
         member="endTable()"/>.
</p>

<p>For full details of how to use this, see the appropriate javadocs,
but here is a simple example which counts the rows in each TABLE
in a VOTable stream.
<verbatim>
    import javax.xml.parsers.SAXParserFactory;
    import org.xml.sax.ContentHandler;
    import org.xml.sax.InputSource;
    import org.xml.sax.XMLReader;
    import uk.ac.starlink.table.StarTable;
    import uk.ac.starlink.votable.TableContentHandler;
    import uk.ac.starlink.votable.TableHandler;
</verbatim>
<blockcode><![CDATA[
    void summariseVotableDocument( InputStream in ) throws Exception {

        // Set up a handler which responds to TABLE-triggered events in 
        // a suitable way.
        TableHandler tableHandler = new TableHandler() {

            long rowCount;  // Number of rows seen in this table.

            // Start of table: print out the table name.
            public void startTable( StarTable meta ) {
                rowCount = 0;
                System.out.println( "Table: " + meta.getName() );
            }

            // New row: increment the running total of rows in this table.
            public void rowData( Object[] row ) {
                rowCount++;
            }

            // End of table: print out the summary.
            public void endTable() {
                System.out.println( rowCount + " rows" );
            }
        };

        // Install it into a TableContentHandler ready for use.
        TableContentHandler votContentHandler = new TableContentHandler( true );
        votContentHandler.setTableHandler( tableHandler );

        // Get a SAX parser in the usual way.
        XMLReader parser = SAXParserFactory.newInstance().newSAXParser()
                          .getXMLReader();

        // Install our table-aware content handler in it.
        parser.setContentHandler( votContentHandler );

        // Perform the parse; this will go through the XML stream sending
        // SAX events to votContentHandler, which in turn will forward
        // table events to tableHandler, which responds by printing the summary.
        parser.parse( new InputSource( in ) );
    }
]]></blockcode>
</p>

</subsubsect>

<subsubsect id="voStandard">
<subhead><title>Standards Conformance</title></subhead>

<p>The VOTable parser provided is believed to be able to parse correctly
any VOTable document which conforms to the 1.0, 1.1, 1.2, 1.3 or (draft) 1.4
VOTable recommendations.
In addition, it will happily cope with documents which violate the
standard in that they contain extra elements or attributes; such 
elements or attributes will be inserted into the resulting DOM but
ignored as far as producing <code>StarTable</code>s goes.
In general, if there is something obvious that the parser can do to
make sense of a document outside of the letter of the standard, then
it tries to do that.
</p>

<p>There is currently one instance in which it can be useful for the parser 
deliberately to violate the standard, as a workaround for an error 
commonly encountered in VOTable documents.  According to the standard,
if a FIELD (or PARAM) element is declared like this:
<verbatim><![CDATA[
   <FIELD datatype="char"/>                 (1)
]]></verbatim>
it is considered equivalent to
<verbatim><![CDATA[
   <FIELD datatype="char" arraysize="1"/>   (2)
]]></verbatim>
that is, it describes a column in which each cell contains a single 
character (the same remarks apply to <code>datatype="unicodeChar"</code>).
In fact, when people (or machines) write (1) above, what they often
mean to say is
<verbatim><![CDATA[
   <FIELD datatype="char" arraysize="*"/>   (3)
]]></verbatim>
that is, it describes a column in which each cell contains a 
variable length string.  In particular, some tables returned from 
the service have contained this defect.
Working to the letter of the standard, this can lead to columns in
which only the first character of the string in cell is visible.
By default STIL interprets (1) above, in accordance with the standard,
to mean (2).
However, if you want to work around the problem and interpret (1) to mean (3),
by using <code>VOElementFactory</code>'s
<javadoc class="uk.ac.starlink.votable.VOElementFactory"
         member="setStrict(boolean)"/> method or
<javadoc class="uk.ac.starlink.votable.VOElementFactory"
         member="STRICT_DEFAULT"/> variable,
or from outside the program by setting the system property
<code>votable.strict="false"</code>.
</p>

</subsubsect>
</subsect>

<subsect id="voOutput">
<subhead><title>Writing VOTables</title></subhead>

<p>To write a VOTable using STIL you have to prepare a 
<javadoc class='uk.ac.starlink.table.StarTable'/> 
object which defines the output table's metadata and data.  
The <javadoc class="uk.ac.starlink.table."/> package
provides a rich set of facilities for creating and modifying these,
as described in <ref id="process"/>
(see <ref id="createExample"/> for an example of how to
turn a set of arrays into a <code>StarTable</code>).
In general the FIELD <code>arraysize</code> and <code>datatype</code> 
attributes are determined
from column classes using the same mappings described in 
<ref id='voDatamap'/>.
</p>

<p>A range of facilities for writing <code>StarTable</code>s out as
VOTables is offered, allowing control over the data format and the structure
of the resulting document.
</p>

<subsubsect>
<subhead><title>Generic table output</title></subhead>

<p>Depending on your application, you may wish to provide the option of
output to tables in a range of different formats including VOTable.  
This can be easily done using the generic output facilities described in 
<ref id="genericOutput"/>.
</p>
</subsubsect>


<subsubsect>
<subhead><title>Single VOTable output</title></subhead>

<p>The simplest way to output a table in VOTable format is to
use a <javadoc class='uk.ac.starlink.votable.VOTableWriter'/>,
which will output a VOTable document with the simplest structure capable 
of holding a TABLE element, namely:
<verbatim><![CDATA[
    <VOTABLE version='1.0'>
      <RESOURCE>
        <TABLE>
          <!-- .. FIELD elements here -->
          <DATA>
            <!-- table data here -->
          </DATA>
        </TABLE>
      </RESOURCE>
    </VOTABLE>
]]></verbatim>
The writer can be configured/constructed to write its output in any of
the formats described in <ref id='voFormats'/> (TABLEDATA,
inline FITS etc) by using its
<javadoc class="uk.ac.starlink.votable.VOTableWriter"
         member="setDataFormat(uk.ac.starlink.votable.DataFormat)"/> and
<javadoc class="uk.ac.starlink.votable.VOTableWriter"
         member="setInline(boolean)"/> methods.
In the case of streamed output which is not inline,
the streamed (BINARY or FITS) data will be written to a 
with a name similar to that of the main XML output file.
</p>

<p>Assuming that you have your <code>StarTable</code> ready to output, 
here is how you could write it out in two of the possible formats:
<blockcode>
    void outputAllFormats( StarTable table ) throws IOException {

        // Create a default StarTableOutput, used for turning location
        // strings into output streams.
        StarTableOutput sto = new StarTableOutput();

        // Obtain a writer for inline TABLEDATA output.
        VOTableWriter voWriter =
            new VOTableWriter( DataFormat.TABLEDATA, true, VOTableVersion.V13 );

        // Use it to write the table to a named file.
        voWriter.writeStarTable( table, "tabledata-inline.xml", sto );

        // Modify the writer's characteristics to use it for referenced FITS output.
        voWriter.setDataFormat( DataFormat.FITS );
        voWriter.setInline( false );

        // Use it to write the table to a different named file.
        // The writer will choose a name like "fits-href-data.fits" for the
        // actual FITS file referenced in the XML.
        voWriter.writeStarTable( table, "fits-href.xml", sto );
    }
</blockcode>
</p>
</subsubsect>

<subsubsect>
<subhead><title>TABLE element output</title></subhead>

<p>You may wish for more flexibility, such as the possibility 
to write a VOTable document with a more complicated
structure than a simple VOTABLE/RESOURCE/TABLE one, 
or to have more control over the output destination for referenced STREAM data.
In this case you can use the 
<javadoc class='uk.ac.starlink.votable.VOSerializer'/>
class which handles only the output of TABLE elements themselves
(the hard part), leaving you free to embed these in whatever XML
superstructure you wish.
</p>

<p>Once you have obtained your <code>VOSerializer</code> by specifying
the table it will serialize and the data format it will use, 
you should invoke its 
<javadoc class="uk.ac.starlink.votable.VOSerializer"
         member="writeFields(java.io.BufferedWriter)"/> method followed by
either 
<javadoc class="uk.ac.starlink.votable.VOSerializer"
         member="writeInlineDataElement(java.io.BufferedWriter)"/> or 
<javadoc class="uk.ac.starlink.votable.VOSerializer"
         member="writeHrefDataElement(java.io.BufferedWriter,
                                      java.lang.String,
                                      java.io.OutputStream)"/>.
For inline output, the output should be sent to the same stream 
to which the XML itself is written.  In the latter case however, 
you can decide where the streamed data go, allowing possibilities such
as sending them to a separate file in a location of your choosing,
creating a new MIME attachment to a message, or sending it down
a separate channel to a client.  In this case you will need to
ensure that the href associated with it (written into the STREAM element's
<code>href</code> attribute) will direct a reader to the right place.
</p>

<p>Here is an example of how you could write a number of inline tables 
in TABLEDATA format in the same RESOURCE element:
<blockcode><![CDATA[
    void writeTables( StarTable[] tables ) throws IOException {
        BufferedWriter out = 
            new BufferedWriter( new OutputStreamWriter( System.out ) );

        out.write( "<VOTABLE version='1.1'>\n" );
        out.write( "<RESOURCE>\n" );
        out.write( "<DESCRIPTION>Some tables</DESCRIPTION>\n" );
        for ( int i = 0; i < tables.length; i++ ) {
            VOSerializer.makeSerializer( DataFormat.TABLEDATA, tables[ i ] )
                        .writeInlineTableElement( out );
        }
        out.write( "</RESOURCE>\n" );
        out.write( "</VOTABLE>\n" );
        out.flush();
    }
]]></blockcode>
and here is how you could write a table with its data streamed to a 
binary file with a given name (rather than the automatically chosen
one selected by <code>VOTableWriter</code>):
<blockcode><![CDATA[
    void writeTable( StarTable table, File binaryFile ) throws IOException {
        BufferedWriter out = 
            new BufferedWriter( new OutputStreamWriter( System.out ) );

        out.write( "<VOTABLE version='1.1'>\n" );
        out.write( "<RESOURCE>\n" );
        out.write( "<TABLE>\n" );
        DataOutputStream binOut = 
            new DataOutputStream( new FileOutputStream( binaryFile ) );
        VOSerializer.makeSerializer( DataFormat.BINARY, table )
                    .writeHrefTableElement( out, "file:" + binaryFile, binOut );
        binOut.close();
        out.write( "</TABLE>\n" );
        out.write( "<RESOURCE>\n" );
        out.write( "<VOTABLE>\n" );
        out.flush();
    }
]]></blockcode>
</p>

<p>VOSerializer contains some more fine-grained methods too which can
be used if you want still further control over the output, for instance
to insert some GROUP elements after the FIELDs in a table.
Here is an example of that:
<blockcode>
<hidden>void dummy8(StarTable table) throws IOException {</hidden><![CDATA[
        BufferedWriter out = 
            new BufferedWriter( new OutputStreamWriter( System.out ) );

        out.write( "<VOTABLE version='1.1'>\n" );
        out.write( "<RESOURCE>\n" );
        out.write( "<TABLE>\n" );

        VOSerializer ser = VOSerializer
                          .makeSerializer( DataFormat.TABLEDATA, table );
        ser.writeFields( out );
        out.write( "<GROUP><FIELDref ref='RA'/><FIELDref ref='DEC'/></GROUP>" );
        ser.writeInlineTableElement( out );

        out.write( "</TABLE>\n" );
        out.write( "</RESOURCE>\n" );
        out.write( "</VOTABLE>" );
]]><hidden>}</hidden></blockcode>
</p>

<p>Note that since STIL v4.1, alongside 
<javadoc class="uk.ac.starlink.votable.VOSerializer"
         member="writeInlineDataElement(java.io.BufferedWriter)">
                >writeInlineDataElement(BufferedWriter)</javadoc>
VOSerializer has another method
<javadoc class="uk.ac.starlink.votable.VOSerializer"
         member="writeInlineDataElementUTF8(java.io.OutputStream)">
                >writeInlineDataElementUTF8(OutputStream)</javadoc>.
This does the same thing, but allows some optimisations,
so can be used for better performance if you know that the UTF-8 XML
encoding is in use.
</p>

</subsubsect>

<subsubsect id="voOutVersion">
<subhead><title>VOTable Version</title></subhead>

<p>There are a number of versions of the VOTable standard.
These do not differ very much, but there are some changes between
the versions - see for instance the change list in the appendix
of the most recent
<webref url="http://www.ivoa.net/Documents/latest/VOT.html"
        >VOTable document</webref>.
When writing VOTables, you can either specify explicitly which
version you want to write, or use the current default.
</p>

<p>The hard-coded default for VOTable output version
is given by the value of the
<javadoc class="uk.ac.starlink.votable.VOTableVersion"
         member="DEFAULT_VERSION_STRING"
         codetext="VOTableVersion.DEFAULT_VERSION_STRING"/> constant,
currently "<code>1.4</code>", but this can be overridden at runtime
by setting the "<code>votable.version</code>" system property,
e.g. to "1.2".
</p>

<p>To set the output version programmatically, you can supply one of the
provided static instances of the 
<javadoc class="uk.ac.starlink.votable.VOTableVersion"/>
in an appropriate context, e.g.:
<blockcode>
<hidden>void dummy9() {</hidden>
    new VOTableWriter( DataFormat.BINARY, true, VOTableVersion.V12 )
<hidden>;}</hidden>
</blockcode>
or
<blockcode>
<hidden>void dummy10( StarTable table ) throws IOException {</hidden>
    VOSerializer.makeSerializer( DataFormat.TABLEDATA, VOTableVersion.V10, table )
<hidden>;}</hidden>
</blockcode>
</p>

</subsubsect>

</subsect>

</sect>

<appendices>

<sect id="properties">
<subhead><title>System Properties</title></subhead>

<p>This section contains a list of
<javadoc class="java.lang.System" docset="&coredocs;"
         member="getProperties()">system properties</javadoc>
which influence the behaviour of STIL.  You don't have to set any
of these; the relevant components will use reasonable defaults if
they are undefined.  Note that in certain security contexts it may not
be possible to access system properties; 
in this case STIL will silently ignore any such settings.
</p>

<subsect id="property.tmpdir">
<subhead><title>java.io.tmpdir</title></subhead>

<p><code>java.io.tmpdir</code> is a standard Java system property which
is used by the disk-based storage policies.
It determines where the JVM writes temporary files, including those 
written by these storage policies
(see <ref id="storagePolicy"/> and <ref id="property.storage"/>).
The default value is typically "<code>/tmp</code>" on Unix-like platforms.
</p>

</subsect>

<subsect id="property.parallelism">
<subhead><title>java.util.concurrent.ForkJoinPool.common.parallelism</title>
         </subhead>

<p><code>java.util.concurrent.ForkJoinPool.common.parallelism</code>
is a standard Java system property which controls the number of
processing cores apparently available from the system.
By default it is typically set to one less than the number
of cores on the current machine.
To inhibit parallelisation you can set this to 1.
</p>

</subsect>

<subsect id="property.jdbc">
<subhead><title>jdbc.drivers</title></subhead>

<p>The <code>jdbc.drivers</code> property is a standard JDBC property
which names JDBC driver classes that can be used to talk to SQL databases.
See <ref id="jdbcConfig"/> for more details.
</p>
</subsect>

<subsect id="property.markworkaround">
<subhead><title>mark.workaround</title></subhead>

<p>The <code>mark.workaround</code> determines whether a workaround is
employed to fix bugs in which certain <code>InputStream</code> 
implementations lie about their abiilty to do <code>mark/reset</code>
operations
(<javadoc class="java.io.InputStream" docset="&coredocs;" member="mark(int)"/>
returns true when it should return false).
Several classes in various versions of Sun's J2SE do this.
It can result in an error with a message like
"Resetting to invalid mark".  Setting this property true works around it.
By default it is set false.
</p>

</subsect>

<subsect id="property.connectors">
<subhead><title>star.connectors</title></subhead>

<p>The <code>star.connectors</code> property names additional 
remote filestore implementations.  Its value is a colon-separated
list of class names, where each element of the list must be
the name of a class on the classpath which implements 
the <javadoc class="uk.ac.starlink.connect.Connector"/> interface.
It is used in the graphical filestore browsers in 
<ref id="loadDialog"/> and <ref id="saveDialog"/>.
See <javadoc class="uk.ac.starlink.connect.ConnectorManager"/> for
more details.
</p>
</subsect>

<subsect id="property.readers">
<subhead><title>startable.readers</title></subhead>

<p>The <code>startable.readers</code> property provides additional
input handlers which <javadoc class="uk.ac.starlink.table.StarTableFactory"/>
can use for loading tables in named format mode.
Its value is a colon-separated list of class names, where each
element of the list must be the name of a class on the classpath 
which implements
the <javadoc class="uk.ac.starlink.table.TableBuilder"/> interface
and has a no-arg constructor.  
When a new <javadoc class="uk.ac.starlink.table.StarTableFactory"/> 
is constructed, an instance of each such named class is created and
added to its known handler list.  Users of the library can therefore
read tables in the format that the new handler understands by giving
its format name when doing the load.
</p>

</subsect>

<subsect id="property.schemes">
<subhead><title>startable.schemes</title></subhead>

<p>The <code>startable.schemes</code> property
Can be set to a (colon-separated) list of custom table scheme handler classes.  Each class must implement the
<javadoc class="uk.ac.starlink.table.TableScheme"/> interface,
and must have a no-arg constructor.
The schemes thus named will be available
alongside the standard ones listed in <ref id="tableScheme"/>.
</p>

</subsect>

<subsect id="property.storage">
<subhead><title>startable.storage</title></subhead>

<p>The <code>startable.storage</code> property sets the initial
value of the default storage policy, which influences where bulk
table data will be cached.
The recognised values are:
<ul>
<li><code>memory</code>: table data will normally be stored in memory
    (<javadoc class="uk.ac.starlink.table.StoragePolicy" member="PREFER_MEMORY"
              codetext="StoragePolicy.PREFER_MEMORY"/>)</li>
<li><code>disk</code>: table data will normally be stored in 
    temporary disk files
    (<javadoc class="uk.ac.starlink.table.StoragePolicy" member="PREFER_DISK"
              codetext="StoragePolicy.PREFER_DISK"/>)</li>
<li><code>adaptive</code>: table data will be stored in memory for small
    tables and on disk for larger ones
    (<javadoc class="uk.ac.starlink.table.StoragePolicy" member="ADAPTIVE"
              codetext="StoragePolicy.ADAPTIVE"/>)</li>
<li><code>sideways</code>: table data will normally be stored in
    temporary disk files using a column-oriented arrangement
    (<javadoc class="uk.ac.starlink.table.StoragePolicy" member="SIDEWAYS"
              codetext="StoragePolicy.SIDEWAYS"/>)</li>
<li><code>discard</code>: table data will normally be thrown away,
    leaving only metadata
    (<javadoc class="uk.ac.starlink.table.StoragePolicy" member="DISCARD"
              codetext="StoragePolicy.DISCARD"/>)</li>
</ul>
The default setting is equivalent to "<code>adaptive</code>".
</p>

<p>You may also give the name of a subclass of
<javadoc class="uk.ac.starlink.table.StoragePolicy"/> which has a no-arg
constructor, in which case an instance of this class will be used
as the default policy.  See <ref id="storagePolicy"/> for further
discussion.
</p>

<p>See <ref id="storagePolicy"/> for further discussion of storage policies.
</p>

</subsect>

<subsect id="property.unmap">
<subhead><title>startable.unmap</title></subhead>

<p>The <code>startable.unmap</code> property controls how memory-mapped
buffers (<javadoc docset="&coredocs;" class="java.nio.MappedByteBuffer"/>s)
that have been allocated by STIL are unmapped when it can
be determined that they will no longer be used.
Specifically, it controls the implementation of the
<javadoc class="uk.ac.starlink.fits.Unmapper"/> class that will be used.
See the implementation and comments in that class for further discussion.
This is currently only used for FITS input, but it may be extended for
other purposes in future versions.
</p>

<p>Possible values are:
<ul>
<li><code>sun</code>: Best efforts unmapping based on available
    <code>sun.misc</code> classes, discovered by reflection.
    </li>
<li><code>cleaner</code>: Buffers are unmapped using non-J2SE classes including
    <code>sun.misc.Cleaner</code>, discovered by reflection.
    Expected to work for java6 through java8.
    </li>
<li><code>unsafe</code>: Buffers are unmapped using non-J2SE classes including
    <code>sun.misc.Unsafe</code>, discovered by reflection.
    Expected to work for java9 and possibly later versions.
    </li>
<li><code>none</code>: No attempt is made to unmap buffers explicitly.
    They will be unmapped only when garbage collected.
    </li>
</ul>
You can also use the classname of an <code>Unmapper</code> implementation
that has a no-arg constructor.
If no value is supplied, <code>sun</code>-like behaviour
is used where possible,
but it falls back to <code>none</code> if the relevant classes are
not available.
</p>

<p>In general you are advised to leave this parameter alone.
It is provided because the sun-like unmapping is doing fundamentally
inadvisable things, and although I think it's done in a way which will
not cause problems, nasty consequences like JVM crashes are possible
if I've made mistakes, so it's a good idea to have a switch here that
allows the dangerous behaviour to be switched off
(<code>startable.unmap=none</code>).
</p>

</subsect>

<subsect id="property.writers">
<subhead><title>startable.writers</title></subhead>

<p>The <code>startable.writers</code> property provides additional
output handlers which <javadoc class="uk.ac.starlink.table.StarTableOutput"/>
can use for writing tables.
Its value is a colon-separated list of class names, where each 
element of the list must be the name of a class on the classpath
which implements the
<javadoc class="uk.ac.starlink.table.StarTableWriter"/> interface and
has a no-arg constructor.
When a new <javadoc class="uk.ac.starlink.table.StarTableOutput"/>
is constructed, an instance of each such named class is created
and added to its handler list.  Users of the library can therefore
write tables in the format that the new handler knows how to 
write to by giving its format name when performing the write.
</p>

</subsect>

<subsect id="property.vonamespacing">
<subhead><title>votable.namespacing</title></subhead>

<p>The <code>votable.namespacing</code> property determines how XML
namespacing is handled in VOTable documents.
It may take one of the following fixed values:
<ul>
<li><code>none</code>:
    No namespace handling is done.  If the VOTable document contains 
    <code>xmlns</code> declarations, the parser will probably become confused.
    (<javadoc class="uk.ac.starlink.votable.Namespacing" member="NONE"
              codetext="Namespacing.NONE"/>)
    </li>
<li><code>lax</code>:
    Anything that looks like it is probably a VOTable element is treated as
    a VOTable element, regardless of whether the namespacing has been 
    declared correctly or not.
    (<javadoc class="uk.ac.starlink.votable.Namespacing" member="LAX"
              codetext="Namespacing.LAX"/>)
    </li>
<li><code>strict</code>:
    Only elements declared to be in one of the official VOTable namespaces
    are treated as VOTable elements.
    If the VOTable document does not contain appropriate <code>xmlns</code>
    declarations, the parser may not treat it as a VOTable.
    (<javadoc class="uk.ac.starlink.votable.Namespacing" member="STRICT"
              codetext="Namespacing.STRICT"/>)
    </li>
</ul>
Alternatively, the property value may be the fully qualified classname
of an implementation of the
<javadoc class="uk.ac.starlink.votable.Namespacing"/> class which
has a no-arg constructor; in this case that class will be instantiated
and it will be used for VOTable namespace handling.
</p>

<p>If no value is given, the default is currently <code>lax</code> handling.
In versions of STIL prior to 2.8, the behaviour was not configurable,
and corresponded approximately to a value for this property of 
<code>none</code>.
</p>

</subsect>

<subsect id="property.vostrict">
<subhead><title>votable.strict</title></subhead>

<p>The <code>votable.strict</code> property determines whether
VOTable parsing is done strictly according to the letter of the standard.
See <ref id="voStandard"/> for details.
</p>

</subsect>

<subsect id="property.voversion">
<subhead><title>votable.version</title></subhead>

<p>The <code>votable.version</code> property selects the version of the
VOTable standard which output VOTables will conform to by default.
May take the values
"<code>1.0</code>",
"<code>1.1</code>",
"<code>1.2</code>"
"<code>1.3</code>" or.
"<code>1.4</code>".
By default, version 1.3 VOTables are written.
</p>

</subsect>

</sect>

<sect>
<subhead><title>Table Tools</title></subhead>

<p>Some user applications based on STIL are available in the following
packages:
<dl>
<dt><webref url="http://www.starlink.ac.uk/stilts/">STILTS</webref></dt>
<dd><p>STIL Tool Set, contains command-line tools for generic table
    and VOTable manipulation.
    </p></dd>
<dt><webref url="http://www.starlink.ac.uk/topcat/">TOPCAT</webref></dt>
<dd><p>Tool for OPerations on Catalogues And Tables, an interactive
    GUI application for table visualisation and manipulation.
    </p></dd>
</dl>
</p>

</sect>

<sect id="release">
<subhead><title>Release Notes</title></subhead>

<p>STIL is released under the terms of the 
<webref url="http://www.gnu.org/copyleft/lgpl.html" plaintextref="yes"
        >GNU Lesser General Public License</webref>.
It has been developed and tested under Sun's Java SE 8,
but is believed to run under other 8/1.8 or later versions of the Java SE.
</p>

<p>An attempt is made to keep backwardly-incompatible changes to 
the public API of this library to a minimum.
However, rewrites and improvements may to lead
to API-level incompatibilities in some cases, as described in 
<ref id="history"/>.
The author would be happy to advise people who have used previous versions
and want help adapting their code to the current STIL release.
</p>

<subsect>
<subhead><title>Acknowledgements</title></subhead>

<p>My thanks are due to a number of people who have
contributed help to me in writing this 
document and the STIL software, including:
<ul>
<li>Alasdair Allan (Starlink, Exeter)</li>
<li>Malcolm Currie (Starlink, RAL)</li>
<li>Clive Davenhall (AstroGrid, RoE)</li>
<li>Pierre Didelon (CEA)</li>
<li>Peter Draper (Starlink, Durham)</li>
<li>David Giaretta (Starlink, RAL)</li>
<li>Paul Harrison (ESO)</li>
<li>Jonathan Irwin (IoA)</li>
<li>Nickolai Kouropatkine (Fermilab)</li>
<li>Clive Page (AstroGrid, Leicester)</li>
<li>Chris Stoughton (Fermilab)</li>
</ul>
</p>

<p>STIL is written in <webref url="http://java.sun.com/">Java</webref>
and contains code from the following non-Starlink libraries:
<ul>
<li><webref url="http://ant.apache.org/">Ant</webref>'s 
    Bzip2 compression/decompression code</li>
<li><webref url="&jcdfurl;">JCDF</webref>
    is used for reading CDF files</li>
<li><webref url="https://github.com/mbtaylor/jarrow">JARROW</webref>
    is used for reading Feather files</li>
<li><webref url="https://bitbucket.org/snakeyaml/snakeyaml">Snakeyaml</webref>
    is used for YAML parsing in the ECSV input handler</li>
<li><webref url="https://github.com/douglascrockford/JSON-java"
    >JSON-java</webref>
    is used for JSON I/O in Feather and ECSV handlers</li>
</ul>
</p>
</subsect>

<subsect id="depends">
<subhead><title>Package Dependencies</title></subhead>

<p>STIL is currently available in several forms; you may have the
<code>stil.jar</code> file which contains most of the important classes,
or a full starjava installation, or a standalone TOPCAT
jar file, or the <code>stil_jars.zip</code> file containing
various packages in separate jar files, or in some other form.
None of these is definitive;
different packages are required for different usages.
If you are keen to prepare a small class library you can identify
functionality you are not going to need and prepare a class library
omitting those classes.  In most cases, STIL classes
will cope with absence of such packages without falling over.
</p>

<p>The following is a list of what packages are required for what
functions:
<dl>

<dt><code>uk.ac.starlink.table</code></dt>
<dt><code>uk.ac.starlink.table.formats</code></dt>
<dt><code>uk.ac.starlink.table.jdbc</code></dt>
<dt><code>uk.ac.starlink.table.storage</code></dt>
<dt><code>uk.ac.starlink.table.text</code></dt>
<dt><code>uk.ac.starlink.util</code></dt>
<dd><p>Core table processing.</p></dd>

<dt><code>uk.ac.starlink.fits</code></dt>
<dd><p>FITS table processing.
    </p></dd>

<dt><code>uk.ac.starlink.votable</code></dt>
<dt><code>uk.ac.starlink.votable.dom</code></dt>
<dd><p>VOTable processing.</p></dd>

<dt><code>uk.ac.starlink.votable.soap</code></dt>
<dd><p><code>StarTable</code> &lt;-&gt; VOTable serialization/deserialization
    for use with SOAP RPC methods.  Moribund?</p></dd>

<dt><code>uk.ac.starlink.cdf</code></dt>
<dt><code>uk.ac.bristol.star.cdf</code></dt>
<dd><p>CDF table processing.</p></dd>

<dt><code>uk.ac.starlink.feather</code></dt>
<dt><code>uk.ac.bristol.star.feather</code></dt>
<dt><code>uk.ac.bristol.star.fbs.*</code></dt>
<dd><p>Feather table processing.</p></dd>

<dt><code>uk.ac.starlink.ecsv</code></dt>
<dt><code>org.yaml.snakeyaml.*</code></dt>
<dd><p>ECSV table processing</p></dd>

<dt><code>uk.ac.starlink.pds4</code></dt>
<dt><code>gov.nasa.pds.*</code></dt>
<dd><p>PDS4 table processing</p></dd>

<dt><code>uk.ac.starlink.mirage</code></dt>
<dd><p>Mirage-format table output</p></dd>

<dt><code>org.json</code></dt>
<dd><p>JSON library used as part of Feather support.</p></dd>

<dt><code>uk.ac.starlink.table.gui</code></dt>
<dd><p>Graphical components for tables, mainly load/save dialogues.</p></dd>

<dt><code>uk.ac.starlink.connect</code></dt>
<dt><code>org.apache.axis.*</code></dt>
<dd><p>Generic code for browsing remote filespaces in load/save dialogues.
    </p></dd>

<dt><code>uk.ac.starlink.astrogrid</code></dt>
<dt><code>org.astrogrid.*</code></dt>
<dd><p>Browsing MySpace remote filesystems in load/save dialogues.</p></dd>

<dt><code>uk.ac.starlink.srb</code></dt>
<dt><code>edu.sdsc.grid.*</code></dt>
<dd><p>Browsing SRB remote filesystems in load/save dialogues.</p></dd>

</dl>
</p>

</subsect>

<subsect id="history">
<subhead><title>Version History</title></subhead>

<p>
<dl>

<dt>Version 1.0 (30 Jan 2004)</dt>
<dd><p>
  Initial public release.
</p></dd>

<dt>Version 1.0-2 (11 Feb 2004)</dt>
<dd><p>
  <ul>
    <li>Added <javadoc class="uk.ac.starlink.table.RowListStarTable"/>.</li>
  </ul>
</p></dd>

<dt>Version 1.0-3 (12 Feb 2004)</dt>
<dd><p>
  <ul>
    <li>Considerably improved performance of inline (base64-encoded)
        BINARY/FITS table parsing.</li>
  </ul>
</p></dd>

<dt>Version 1.0-4 (17 Mar 2004)</dt>
<dd><p>
  <ul>
    <li>VOTable-derived StarTables now pick up parameters from INFO elements
        as well as PARAM elements.</li>
    <li>Text format output handler now by default outputs table parameters 
        as well as the table data and column metadata.</li>
  </ul>
</p></dd>

<dt>Version 1.1 (29 Mar 2004)</dt>
<dd><p>
   <ul>
     <li>New <ref id="outAscii">ASCII format output handler</ref> 
         can write tables in the same text-based format used by the 
         ASCII input handler.</li>
     <li><javadoc class="uk.ac.starlink.table.JoinStarTable"/> can now 
         deduplicate column names.</li>
     <li>New class <javadoc class="uk.ac.starlink.table.ConcatStarTable"/> 
         permits adding the rows of one table after the rows of another.</li>
   </ul>
</p></dd>

<dt>Version 1.1-1 (11 May 2004)</dt>
<dd><p>
  <ul>
    <li>Improved PostgreSQL compatibility</li>
  </ul>
</p></dd>

<dt>Version 2.0 (20 October 2004)</dt>
<dd><p>Version 2.0 is a major revision incorporating some 
    non-backwardly-compatible changes to the public API.
    The main differences are as follows.
  <dl>
  <dt>RowSequence interface modified</dt>
  <dd><p>The <javadoc class="uk.ac.starlink.table.RowSequence"/> interface
      has been modified; a new
      <javadoc class="uk.ac.starlink.table.RowSequence" member="close()"/>
      method has been introduced, and the old
      <code>advance()</code> and <code>getRowIndex()</code> methods have been
      withdrawn (these latter were not very useful and in some cases 
      problematic to implement).
      </p></dd>

  <dt>Setter methods added to StarTable interface</dt>
  <dd><p>The methods <code>setName()</code> and <code>setURL()</code>
      have been added to the <javadoc class="uk.ac.starlink.table.StarTable"/>
      interface.
      </p></dd>

  <dt>Pluggable storage policies</dt>
  <dd><p>The <javadoc class="uk.ac.starlink.table.StoragePolicy"/> class was
      introduced, which allows you to influence whether cached table data are
      stored in memory or on disk.
      This has led to backwardly-incompatible changes to public interfaces and
      classes:
      <javadoc class="uk.ac.starlink.table.TableBuilder"
               member="makeStarTable(uk.ac.starlink.util.DataSource, boolean,
                                     uk.ac.starlink.table.StoragePolicy)"/>
      now takes a new <code>StoragePolicy</code> argument, and
      <javadoc class="uk.ac.starlink.votable.VOElementFactory"/>'s methods
      are now instance methods rather than static ones.
      </p></dd>

  <dt>Input table format now either specified explicitly or detected 
      automatically</dt> 
  <dd><p>The <javadoc class="uk.ac.starlink.table.StarTableFactory"/> class's
      <code>makeStarTable</code> methods now come in two flavours - with and
      without a format name.  This corresponds to two table reading modes:
      named format mode and automatic format detection mode.
      In named format mode you specify the format of the table you are
      trying to read and in automatic format detection mode you rely 
      on the factory to work it out
      using magic numbers.  Although automatic detection works well
      for VOTable and FITS, it's poor for text-based formats like
      ASCII and CSV.
      This has resulted in addition of some new two-argument
      <code>makeStarTable</code> methods and the withdrawal of the
      <code>getBuilders</code> method in favour of two new methods
      <javadoc class="uk.ac.starlink.table.StarTableFactory"
               member="getDefaultBuilders()"/> and
      <javadoc class="uk.ac.starlink.table.StarTableFactory"
               member="getKnownBuilders()"/>
      (similarly for setter methods) which deal with the handlers 
      used in automatic detection mode and the ones available for
      named format mode respectively.
      Note that the ASCII table format is not automatically detected, 
      so to use ASCII tables you now have to specify the format explicitly.
      </p></dd>

  <dt>VOTable parsing overhauled</dt>
  <dd><p>The <javadoc class="uk.ac.starlink.votable.VOElement"/> class
      has been rewritten and now implements the DOM
      <javadoc class="org.w3c.dom.Element" docset="&coredocs;"/> interface.
      This means that the hierarchical structure which you can navigate
      to obtain information about the VOTable document and extract
      table data actually <em>is</em> a DOM rather than just being sat
      on top of one.  You can therefore now use it just as a normal 
      DOM tree (making use of the methods defined in the 
      <javadoc class="org.w3c.dom." docset="&coredocs;"/> interface, 
      interoperating with third-party components which require a DOM).
      This has had a number of additional benefits and consequences:
      <ul>

      <li>VOTable handling now fully meets version 1.1 of the VOTable standard.
          This includes full ID/ref crossreferencing (e.g. a TABLE element
          obtaining its structure by reference to a previously defined one)
          which was absent in previous versions.
          </li>

      <li>VOTable processing is now independent of Java version; in previous
          versions it failed on J2SE1.5/5.0 due to absence of some Crimson
          parser classes.
          </li>

      <li>The <javadoc class="uk.ac.starlink.votable.VOElementFactory"/>
          class now has instance methods rather than static methods.
          </li>

      <li>By installing a 
          <javadoc class="uk.ac.starlink.table.StoragePolicy" member="DISCARD"
                   codetext="StoragePolicy.DISCARD"/>
          into a <javadoc class="uk.ac.starlink.votable.VOElementFactory"/>
          it is now possible to obtain a data-less 
          (structure only, hence minimal resource) VOTable DOM.
          </li>

      </ul>
      </p></dd>

  <dt>TableSink interface modified</dt>
  <dd><p>Some <javadoc class="uk.ac.starlink.table.TableSink"/> methods
      now throw exceptions.
      </p></dd>

  <dt>Comma-Separated Value format supported</dt>
  <dd><p>There are now CSV <ref id="inCsv">input</ref> and 
      <ref id="outCsv">output</ref> handlers.
      The input handler is not by default installed in the 
      <code>StarTableFactory</code>'s list for automatic format detection,
      but CSV-format tables can be loaded using named format mode.
      The format is intended to match the (widely-used) variety used by
      Microsoft Excel amongst others (with optional column names).
      </p></dd>

  <dt>New 'FITS-plus' format introduced</dt>
  <dd><p>Handlers are introduced for a variant of FITS called 'FITS-plus'.
      This is a FITS file with a BINTABLE extension in HDU#1 as usual, 
      but with the VOTable text containing its metadata stored in
      a byte array in the primary HDU.  This means that the rich VOTable
      metadata are available when reading it with a matching input handler,
      but it looks like a perfectly normal FITS table without the metadata
      when read by a normal FITS-aware application.
      This is now the format in which FITS tables are written by default
      (unless you choose the format name "<code>basic-fits</code>").
      </p></dd>

  <dt>ASCII-format input handler improvements</dt>
  <dd><p>
      <ul>
      <li>Now runs in limited memory, but requires two passes of stream
          (data caching as per current
          <javadoc class="uk.ac.starlink.table.StoragePolicy"/>).
          </li>
      <li>Now uses <code>Short</code>/<code>Float</code> 
          types in preference to <code>Integer</code>/<code>Double</code>
          if the input data make this appropriate.
          </li>
      <li>Now preserves negative zero values (often important for sexagesimal
          representations).
          </li>
      <li>Now understands <code>d</code> or <code>D</code> as an exponent letter
          as well as <code>e</code> or <code>E</code>.
          </li>
      <li>A '!' character in column 1 is now understood to introduce a 
          comment line.
          </li>
      </ul>
      </p></dd>

  <dt>Table matching</dt>
  <dd><p>There have been several changes including performance enhancements
      and improved functionality in the table matching classes in the
      package <code>uk.ac.starlink.table.join</code>..
      These work and have full javadocs, but
      they are still experimental, subject to substantial change in future
      releases, and not documented properly in this document.
      </p></dd>

  <dt>Null handling improvements</dt>
  <dd><p>There is now a mechanism for flagging the magic value
      you would like to use when encoding nulls in an 
      integer output column
      (<javadoc class="uk.ac.starlink.table.Tables" member="NULL_VALUE_INFO"/>)
      Nulls in FITS and VOTable/FITS tables are now preserved correctly 
      on output.
      </p></dd>

  <dt>Miscellaneous</dt>
  <dd><p>There have been a number of miscellaneous improvements and 
      bugfixes in various parts of the library, including the following:
      <ul>
      <li>FITS files now store column descriptions in <code>TCOMMx</code>
          headers.
          </li>

      <li>A type-translation bug in the JDBC handler has been fixed, so that
          it now works with PostgreSQL 
          (and possibly other JDBC implementations).
          </li>

      <li>New class <javadoc class="uk.ac.starlink.table.EmptyStarTable"/>
           added.
           </li>
      </ul>
      </p></dd>

  </dl>
</p></dd>

<dt>Version 2.0-1 (October 2004)</dt>
<dd><p>
   <ul>
      <li>Fixed bugs related to reading streamed (rather than mapped) 
      FITS tables</li>
      <li>Fixed a bug in VOTable 1.1 schema namespace declaration on output</li>
   </ul>
</p></dd>


<dt>Version 2.0-2</dt>
<dd><p>
  <ul>
    <li>Better documentation (<ref id="voMapping"/>) and facilities 
        for manipulation of VOTable FIELD attributes from 
        <code>StarTable</code> object</li>
  </ul>
</p></dd>

<dt>Version 2.0-3</dt>
<dd><p>
  <ul>
    <li>Fixed two more bugs in VOTable 1.1 namespace declaration on output;
        output elements were being declared in the unnamed namespace rather
        than the VOTable 1.1 one, and the VOTable schema location was wrong.
        Both of these errors arose from the fact that the example VOTable 
        in the recommendation document was declared in a wrong/misleading
        fashion.</li>
    <li>Added architecture cartoon to SUN/252.</li>
  </ul>
</p></dd>

<dt>Version 2.1 (4 February 2005)</dt>
<dd>
  <p>Some of the public interfaces have been modified in backwardly 
  incompatible ways at this release.  However, it is not expected
  that much user code will need to be changed.
  <dl>

    <dt>RequireRandom flag in StarTableFactory</dt>
    <dd><p>The <code>wantRandom</code> flag has been changed in name and 
        semantics to <code>requireRandom</code> in
        <javadoc class="uk.ac.starlink.table.StarTableFactory"/>. 
        When set, any table returned from the factory is now guaranteed
        to have random access.
    </p></dd>

    <dt>Table output to streams</dt>
    <dd><p><code>StarTableOutput</code> now has a new method 
        <javadoc class="uk.ac.starlink.table.StarTableOutput"
                 member="writeStarTable(uk.ac.starlink.table.StarTable,
                                        java.io.OutputStream,
                                        uk.ac.starlink.table.StarTableWriter)"/>
        which writes a table to an <code>OutputStream</code> as well
        as the one which writes to a location string (usually filename).
        This is supported by changes to the <code>writeStarTable</code>
        methods which <javadoc class="uk.ac.starlink.table.StarTableWriter"/>
        implementations must provide.
    </p></dd>

    <dt>Table load dialogue</dt>
    <dd><p>The <code>uk.ac.starlink.table.gui.StarTableChooser</code>
        table loader dialogue has been improved in several ways.
        Loading is now done asynchronously, so that the GUI does not
        lock up when a long load is taking place (a load cancel button can
        be pressed).
        Additionally, custom load dialogues have been made pluggable,
        so that you can add new load sub-dialogues by implementing
        <javadoc class="uk.ac.starlink.table.gui.TableLoadDialog"/>
        (most likely subclassing <code>BasicTableLoadDialog</code>)
        and naming the class in the 
        <code>startable.load.dialogs</code>
        property.
        A dialogue for browsing AstroGrid's MySpace remote filestore is
        available, but for reasons of size STIL is not by default 
        packaged with all the
        classes required to make it work (AXIS and the CDK are missing).
    </p></dd>

    <dt>StarTable parameter method</dt>
    <dd><p>A new utility method 
        <javadoc class="uk.ac.starlink.table.StarTable"
                 member="setParameter(uk.ac.starlink.table.DescribedValue)"/>
        has been added to the <code>StarTable</code> interface.
    </p></dd>

    <dt>BeanStarTable</dt>
    <dd><p>A new StarTable implementation, 
        <javadoc class="uk.ac.starlink.table.BeanStarTable"/> which can store
        Java Beans has been introduced.  This is handy for storing arrays 
        of objects of the same kind without having to write a custom table
        implementation.
    </p></dd>

    <dt>Undeclared character <code>arraysize</code> workaround</dt>
    <dd><p>A workaround has been introduced to cope with a common error
        in VOTable documents in which FIELD elements for string values 
        lack the required <code>arraysize</code> attribute; by default
        it is now assumed to have the value "<code>*</code>" rather
        than "<code>1</code>" as the standard dictates.
        See <ref id="voStandard"/>.
    </p></dd>

    <dt>Minor changes</dt>
    <dd><p>
      <ul>
        <li>LINK elements can now be added to FIELDs in a VOTable on output
            by adding a suitable URL-type metadatum to the corresponding
            ColumnInfo.</li>
        <li>Temporary files are now deleted by finalizers (may lead to better 
            reclamation of temporary file space during operation).</li>
        <li>Fixed a bug in VOTable parsing when TD elements were empty.</li>
        <li>V1.1 VOTable tables written now contain the declaration
            <verbatim>
  xsi:schemaLocation="http://www.ivoa.net/xml/VOTable/v1.1
                      http://www.ivoa.net/xml/VOTable/v1.1"
            </verbatim>
            instead of
            <verbatim>
  xsi:noNamespaceSchemaLocation="http://www.ivoa.net/xml/VOTable/v1.1"
            </verbatim>
            (thanks to Paul Harrison for suggesting this correction).</li>
      </ul>
    </p></dd>
  </dl>
</p></dd>


<dt>Version 2.2 (17 March 2005)</dt>
<dd><p>New tool:
    <dl>
      <dt><code>tpipe</code> command introduced</dt>
      <dd><p>The <code>tpipe</code> command has been tentatively introduced
          at this release.
          This useful command-line tool is experimental and may undergo
          major changes or be moved to a separate package altogether
          in future releases.
          </p></dd>
    </dl>
    </p>

    <p>There have been changes to some of the main interfaces:
    <dl>

      <dt>RowSequence hasNext withdrawn</dt>
      <dd><p>The <code>hasNext()</code> method has been withdrawn 
          from the <javadoc class="uk.ac.starlink.table.RowSequence"/>
          interface and the <code>next()</code> method, which used to be 
          declared <code>void</code>, now returns 
          <code>boolean</code> indicating whether there is another row.  
          This is quite likely to break existing code, but the fix is easy; 
          simply replace:
          <verbatim>
              RowSequence rseq = table.getRowSequence();
              while ( rseq.hasNext() ) {
                  rseq.next();
                  ...
              }
          </verbatim>
          with
          <verbatim>
              RowSequence rseq = table.getRowSequence();
              while ( rseq.next() ) {
                  ...
              }
          </verbatim>
      </p></dd>

      <dt>TableBuilder streaming</dt>
      <dd><p>A new method
          <javadoc class="uk.ac.starlink.table.TableBuilder"
                   member="streamStarTable(java.io.InputStream,
                                           uk.ac.starlink.table.TableSink,
                                           java.lang.String)"/>
          has been added to the <code>TableBuilder</code> interface 
          to provide improved support for table streaming.
          </p></dd>

      <dt>GUI table choosers changed</dt>
      <dd><p>There have been several changes in the 
          <code>uk.ac.starlink.gui</code> package.
          <code>StarTableChooser</code> and <code>StarTableSaver</code>
          have been replaced by <code>TableLoadChooser</code> and
          <code>TableSaveChooser</code>, and these both now use a
          graphical widget which can view files in remote filestores
          (such as MySpace and SRB) if the relevant classes are present.
          </p></dd>

    </dl>
    </p>
    
    <p>Minor changes:
    <ul>
    <li>Added <code>Tables.sortTable</code> method.</li>
    <li>Added <code>ExplodedStarTable</code>.</li>
    <li>Added <code>ConcatStarTable</code>.</li>
    <li>VOTables now write <code>arraysize="1"</code> explicitly for 
        scalar character fields.</li>
    <li>VOTable BINARY input handler refuses to attempt reading assumed-size
        character fields.</li>
    <li>Several bugfixes in JDBC output handler for writing new SQL tables;
        now writes String (VARCHAR) fields, better NULL value handling,
        avoids some SQL reserved words for column names.</li>
    <li>Better NULL value handling for some text-like output formats.</li>
    </ul>
    </p></dd>

<dt>Version 2.3 (29 April 2005)</dt>
<dd><p><ul>
    <li>New streaming convenience method introduced on StarTableFactory.</li>
    <li>New Axis-based <code>StarTable</code>&lt;-&gt;VOTable
        serializer/deserializer classes in 
        <code>uk.ac.starlink.votable.soap</code> package.</li>
    <li>New <code>TableContentHandler</code> class provides table-aware
        SAX processing of VOTable document streams.</li>
    <li>Improved documentation of 
        <ref id="storagePolicy">storage policies</ref> in SUN/252.</li>
    <li>Missing <code>arraysize</code> attribute for character fields 
        is now interpreted by default according to the VOTable standard
        rather than by default being worked around - i.e. an unspecified
        <code>votable.strict</code> system property now counts as 
        true rather than false.  This is the reverse of the behaviours
        in versions 2.1 and 2.2.  See <ref id="voStandard"/>.</li>
    <li>Now overwrites existing tables when attempting to write tables
        to SQL database if a table of the same name already exists.</li>
    <li>Fixed PostgreSQL bug - can now write String columns correctly.</li>
    <li><code>tablecopy</code> command deprecated and <code>tpipe</code>
        withdrawn - these are now available within the new package
        <webref url="http://www.starlink.ac.uk/stilts/">STILTS</webref>.</li>
    </ul></p></dd>

<dt>Version 2.3-1 (30 June 2005)</dt>
<dd><p>
<ul>
  <li>Added convenience methods
      <code>writeInlineTableElement</code>, <code>writeHrefTableElement</code>
      to <code>VOSerializer</code>.</li>
  <li>Fixed a bug in <code>VOStarTable</code> parameter setting.</li>
  <li>Fixed a bug in <code>ConcatStarTable</code> which was leading to 
      ClassCastExceptions when used sequentially.</li>
</ul>
</p></dd>

<dt>Version 2.3-2 (30 September 2005)</dt>
<dd><p>
<ul>
<li>Some changes to <code>RowMatcher</code> class.</li>
<li>Fixed some bugs in the VOTable DOM implementation connected with
    <code>null</code> values.</li>
<li>MatchEngine now returns metadata on match scores.</li>
<li>The string "<code>null</code>" (unquoted) in ASCII input handler 
    is interpreted as a blank entry.</li>
<li>Fixed bug in ASCII input handler which misidentified blank lines,
    or DOS-format line ends, as end of file.</li>
</ul>
</p></dd>

<dt>Version 2.4 (10 May 2006)</dt>
<dd><p>The following API change has taken place:
<ul>
<li>New method
    <javadoc class="uk.ac.starlink.table.StarTableWriter" 
             member="getMimeType()"
             ><code>StarTableWriter.getMimeType</code></javadoc>
    has been introduced.</li>
</ul>
Additionally, there are the following minor improvements and bugfixes:
<ul>
<li>Now copes with 'K'-format FITS binary table columns (64-bit integers).</li>
<li>Added IPAC Table Format input handler.</li>
<li>Added noheader option to CSV output format.</li>
<li>Added <ref id="property.markworkaround"><code>mark.workaround</code></ref>
    system property.</li>
<li>Blank values in boolean columns are now handled as null rather than false
    (changes to FITS handlers, VOTable handlers and cell renderer).</li>
<li>Fixed bug which was writing some integer null values as empty TD elements
    (illegal) - now uses magic bad value where available.</li>
<li>CSV &amp; ASCII input handlers now (try to) detect sexagesimal and
    ISO-8601 format data columns and mark the unit string appropriately.</li>
<li>Fixed bug writing unclosed LINK elements in output VOTables.</li>
</ul>
</p></dd>

<dt>Version 2.5 (7 July 2006)</dt>
<dd><p>
    <ul>
    <li>Support for new column-oriented FITS file format
        (<ref id="inColfits"/>, <code>outColfits</code>).</li>
    <li>New StoragePolicy SIDEWAYS storage (<ref id="policyList"/>).</li>
    <li>FITS-PLUS files now only recognised if VOTMETA header card has the
        value "T", not just if it is present.</li>
    <li>Increased the maximum field width written by <code>text</code> and
        (especially) <code>ascii</code> output handlers.</li>
    <li>TUCDnn header cards now used in FITS files to transmit UCDs
        (non-standard mechanism).</li>
    </ul>
    </p></dd>

<dt>Version 2.6 (3 August 2006)</dt>
<dd><p>
    <ul>
    <li>Replaced <code>RowStepper</code> class by <code>RowSequence</code> in 
        <code>votable</code> package.  As well as being a bit tidier, this
        improves efficiency considerably for column-oriented access in
        some cases (esp. fits-plus/colfits-plus).</li>
    <li>Dramatically improved efficiency of <code>fits-plus</code> &amp; 
        (especially) <code>colfits-plus</code> format access in some situations 
        (related to above point).</li>
    <li><code>colfits-basic</code> format is now auto-detected.</li>
    <li>Added TST (tab-separated table) <ref id="inTst">input</ref> and
        <ref id="outTst">output</ref> handlers.</li>
    <li>Efficiency improvements for column-oriented access.</li>
    </ul>
    </p></dd>

<dt>Version 2.6-1 (Starlink Hokulei release)</dt>
<dd><p>
    <ul>
    <li>Modified and extended 
        <javadoc class="uk.ac.starlink.table.jdbc.JDBCFormatter"/>
        API for more flexible use with creating tables in RDBMS.</li>
    <li>Modified presentation of HTML version of SUN/252 using CSS.</li>
    <li>Fixed bug in handling of single quotes in FITS file metadata.</li>
    </ul>
    </p></dd>

<dt>Version 2.6-2 (23 July 2007)</dt>
<dd><p>
    <ul>
    <li>Add new exception
        <javadoc class="uk.ac.starlink.table.UnrepeatableSequenceException"
                 />.</li>
    <li>Add new classes 
        <javadoc
             class="uk.ac.starlink.table.jdbc.SequentialResultSetStarTable"/>
        and
        <javadoc class="uk.ac.starlink.table.jdbc.RandomResultSetStarTable"/>
        which are <code>StarTable</code> implementations built directly on
        JDBC <javadoc class="java.sql.ResultSet" docset="&coredocs;"/> 
        objects.</li>
    <li><javadoc class="uk.ac.starlink.table.JoinFixAction"/> interface and
        implementation changed.  Now better at deduplicating the names of
        joined tables.</li>
    <li>Fix error in output of FITS table <code>TNULL</code><m>n</m> header
        cards - write them as numeric not string values.</li>
    <li>Improve error message for broken CSV files.</li>
    </ul>
    </p></dd>

<dt>Version 2.6-3 (4 Sep 2007)</dt>
<dd><p>
    <ul>
    <li>Added
        <javadoc class="uk.ac.starlink.table.Tables"
                 member="getUtype(uk.ac.starlink.table.ValueInfo)"/> and
        <javadoc class="uk.ac.starlink.table.Tables"
                 member="setUtype(uk.ac.starlink.table.ValueInfo,
                                  java.lang.String)"/> 
        utility methods.</li>
    <li>Added
        <javadoc class="uk.ac.starlink.table.RowPipe"/> interface and
        implementations and
        new <code>createOutputSink</code> methods in
        <javadoc class="uk.ac.starlink.table.StarTableOutput"/>.</li>
    <li>FITS files now read/write Utypes using <code>TUTYPnn</code> header
        cards.</li>
    </ul>
    </p></dd>

<dt>Version 2.6-4 (30 Oct 2007)</dt>
<dd><p>
    <ul>
    <li>Minor changes to interface and implementation of
        <javadoc class="uk.ac.starlink.table.RowPipe"/> and
        <javadoc class="uk.ac.starlink.table.OnceRowPipe"/>.</li>
    </ul>
    </p></dd>

<dt>Version 2.6-5 (6 Dec 2007)</dt>
<dd><p>
    <ul>
    <li>Improvements and modifications to crossmatching functionality in
        <code>uk.ac.starlink.table.join</code>,
        including multi-pair join.</li>
    <li>FITS reader now imports table HDU header cards as table parameters.</li>
    <li>Embedded spaces in output ASCII format table column names 
        are now substituted with underscores.</li>
    <li>Added quoting of SQL identifiers for JDBC statement execution.</li>
    </ul>
    </p></dd>

<dt>Version 2.6-6 (28 Jan 2008)</dt>
<dd><p>
    <ul>
    <li>Downgraded from WARNING to INFO log messages about 
        the (extremely common) VOTable syntax error of omitting 
        a FIELD/PARAM element's <code>datatype</code> attribute.</li>
    <li>Avoid some truncations of double (and float?) fields in 
        <code>text</code>-mode output (may result in longer fields too).</li>
    </ul>
    </p></dd>

<dt>Version 2.6-7 (4 Apr 2008)</dt>
<dd><p>
    <ul>
    <li>Some missing classes reinstated in the stil.jar file.</li>
    <li>Minor changes to matching classes.</li>
    </ul>
    </p></dd>

<dt>Version 2.7 (19 Aug 2008)</dt>
<dd><p>
    <ul>
    <li>Variable-length arrays are now mostly supported for FITS binary tables:
        <ul>
        <li>Columns with TFORM cards containing the 'P' or 'Q' 
            data type descriptors will be read correctly for FITS BINTABLE
            extensions read from random access sources 
            (which basically means from disk).
            Tables read from a sequential-only stream will, as before, 
            fail to read variable length array-valued columns.
            </li>
        <li>The new
            <javadoc class="uk.ac.starlink.fits.VariableFitsTableWriter"/>
            TableWriter implementation can write tables in which variable-length
            columns are represented in the FITS BINTABLE extension by columns
            with the 'P' or 'Q' data type descriptors.
            </li>
        </ul>
    </li>
    <li>New method <javadoc class="uk.ac.starlink.table.StoragePolicy"
                            member="makeByteStore()"/>
        introduced in class <code>StoragePolicy</code>.</li>
    <li>Various <code>ValueInfo</code> keys for FITS-specific 
        column auxiliary metadata items are now available as static members of 
        <javadoc class="uk.ac.starlink.fits.BintableStarTable"/>.</li>
    <li>Fixes to <code>JDBCFormatter</code> - safer checks on column and table 
        name syntax.</li>
    <li>Sexagesimal field identification for ASCII input files less stringent
        (now permits minutes or seconds equal to 60).</li>
    <li>HEALPix bug fix
        (<webref url="https://github.com/kuropat/eag-HEALPix"
                 >PixTools</webref> bug fix update).</li>
    <li>Take more steps to use <code>StoragePolicy</code> when loading JDBC
        tables, avoiding some JDBC-driver-based out of memory issues.</li>
    </ul>
    </p></dd>

<dt>Version 2.7-1 (27 Mar 2009)</dt>
<dd><p>
    <ul>
    <li>More careful header consistency checks in fits-plus files -
        corrupted/modified fits-plus less likely to generate errors.</li>
    <li>Fits BINTABLE TZERO/TSCAL value reading improvements:
       <ul>
       <li>Columns with integer TZERO values now read as integers 
           rather than floating point values where possible.  
           This includes unsigned longs ('K'), which were previously 
           represented as doubles with lost precision.  
           Unsigned longs which are too large however (&gt;2<sup>63</sup>)
           are read as nulls.</li>
       <li>It's now configurable whether byte columns are written as signed
           bytes (TFORM=B,TZERO=-128) or as signed shorts (TFORM=I).</li>
       <li>More comprehensive testing.</li>
       <li>Fixed bug in calculating value scaled double ('D') values.</li>
       <li>Fixed bug in typing value for scaled float ('E') arrays.</li>
       </ul></li>
    <li>The fixed length Substring Array Convention for string arrays
        (<code>TFORMnn=rAw</code>) 
        is now understood for FITS binary tables.</li>
    </ul>
    </p></dd>

<dt>Version 2.7-2 (17 July 2009)</dt>
<dd><p>
    <ul>
    <li>VOTable <code>xtype</code> and <code>ref</code> column attributes
        can be read and written by use of suitable ColumnInfo aux data keys,
        defined as static members of VOStarTable.</li>
    </ul>
    </p></dd>

<dt>Version 2.8 (2 Oct 2009)</dt>
<dd><p>
    <ul>
    <li>VOTable namespace handling has been improved.
        Previously VOTable documents with <code>xmlns</code> namespacing
        declarations were mostly rejected by STIL.
        Now the behaviour is configurable.
        <ul>
        <li>A new class <javadoc class="uk.ac.starlink.votable.Namespacing"/>
            is introduced which takes care of pluggable namespace handling.
            </li>
        <li>The default is now <em>lax</em> handling; anything that looks
            like it is probably a VOTable element is treated as such.
            This means that documents both with and without xmlns 
            declarations should work.
            The behaviour of previous versions was approximately that
            corresponding to <em>none</em>.
            </li>
        <li>A new system property 
            <ref id="property.vonamespacing">votable.namespacing</ref>
            has been introduced to control behaviour from outside the JVM.
            </li>
        <li>New VOElement methods
            <code>getElementsByVOTagName</code> and
            <code>getVOTagName</code> have been introduced
            for convenience of use of elements in the VOTable namespace.
            </li>
        <li>The semantics of the VOElement methods
            <code>getChildByName</code> and <code>getChildrenByName</code>
            are slightly changed (now return only elements in VOTable
            namespace.
            </li>
        </ul>
        </li>
    <li>VOTable 1.2 is now supported.  The supported version is
        the PR 2009-09-29, which is not expected to differ significantly
        from the REC when it is approved.  Support for versions 1.0 and 1.1
        is unaffected.  API changes are:
        <ul>
        <li><code>FieldRefElement</code> and <code>ParamRefElement</code>
            have new <code>getUcd</code> and <code>getUtype</code> methods.
            </li>
        <li><code>FieldElement</code> has new <code>getXtype</code> method.
            </li>
        </ul>
        </li>
    <li>Be more careful in XML, including VOTable, output; 
        fix VOTable output encoding to be UTF-8,
        and ensure no illegal XML characters are written.</li>
    <li>HTML table output is now HTML 4.01 by default
        (includes THEAD and TBODY tags).</li>
    <li>Work around illegally truncated type declarations in IPAC tables.</li>
    <li>Bug fixed in crossmatching output: entries which should have been 
        null were sometimes written as non-null (typically large negative
        numbers) in FITS and in non-TABLEDATA VOTable output.
        This affected cells in otherwise non-nullable columns
        where the entire row was absent.  The previous behaviour is not
        likely to have been mistaken for genuine results.
        </li>
    </ul>
    </p></dd>

<dt>Versions 2.9*x</dt>
<dd><p>STIL versions 2.9x, 2.9-1x, 2.9-2x, 2.9-3x did not get a public
    release, since the backwardly-incompatible changes they contained
    were not stable, but were present in some versions of TOPCAT and STILTS.
    Details of what changed in which of these versions
    are only available by examining relevant versions of the
    XML sources for SUN/252 (sun252.xml) in the version control system.
    All the changes are amalgamated into version 3.0.
    </p></dd>

<dt>Version 3.0 (23 December 2010)</dt>
<dd><p>This major release contains some new capabilities and 
    some backward incompatibilities with respect to
    the previous public release, version 2.8.
    The major changes are in the following areas:
    <ul>
    <li>Multiple table read (new capability)</li>
    <li>Multiple table write (new capability)</li>
    <li>GUI load/save dialogues (major overhaul)</li>
    <li>New Adaptive storage policy as default</li>
    </ul>
    Anyone implementing a table read handler, write handler, 
    load dialogue or save dialogue
    will need to make some adjustments since the relevant 
    interfaces have changed.
    Anyone using the GUI load dialogue classes in package
    <javadoc class="uk.ac.starlink.table.gui."/>
    (as far as I know, nobody apart from me is)
    will require significant rewriting.
    Other users of the library will probably find no or only minor issues
    if compiling against the new version.
    In most cases significant changes will be marked by compilation errors
    rather than silent changes in behaviour.
    The exception is use of the new Adaptive storage policy which is now
    the default; this change is expected to be beneficial rather than
    problematic in most cases.
    </p>
    <p>If you experience any difficulties in upgrading from a previous version
    to this one, please contact the author, who will be happy to advise or
    assist.
    </p>
    <p>The changes in more detail are as follows:
    <dl>
    <dt>Multiple table read:</dt>
    <dd><p>
        <ul>
        <li>New <javadoc class="uk.ac.starlink.table.MultiTableBuilder"/>
            interface which
            <javadoc class="uk.ac.starlink.table.TableBuilder"/>s
            may implement if they know how to load multiple tables from the
            same source.  The FITS and VOTable handlers now implement this.
            </li>
        <li>Three new <code>makeStarTables</code> methods added to
            StarTableFactory.
            These return a <javadoc class="uk.ac.starlink.table.TableSequence"/>            which contains multiple tables.
            Multiple tables are only a possibility if the relevant handler
            implements the new
            <javadoc class="uk.ac.starlink.table.MultiTableBuilder"/>
            interface (of the supplied handlers, FITS and VOTable).
            </li>
        <li>StarTableFactory methods <code>makeStarTable(URL)</code> and
                                     <code>makeStarTable(URL,String)</code>
            are deprecated.
            They are very thin utility wrappers around existing methods
            which may be replaced easily in calling code using the
            <javadoc class="uk.ac.starlink.util.URLDataSource"/> class.
            These methods may be removed in a future release.
            </li>
        </ul>
        </p></dd>
    <dt>Multiple table write:</dt>
    <dd><p>
        <ul>
        <li>New interface
            <javadoc class="uk.ac.starlink.table.MultiStarTableWriter"/>,
            which extends
            <javadoc class="uk.ac.starlink.table.StarTableWriter"/>,
            for output handlers which can write multiple tables to the same
            container.
            Corresponding methods added to <code>StarTableOutput</code>.
            </li>
        <li>MultiStarTableWriter is implemented for most variants of the 
            FITS and VOTable output handlers, to generate multi-extension
            FITS and multi-TABLE VOTable outputs respectively.
            Implementations for some other output handlers generating
            output that may not be machine-readable as a multi-table file
            are provided as well.
            </li>
        </ul>
        </p></dd>
    <dt>GUI load/save dialogues:</dt>
    <dd><p>There have been substantial changes to the GUI load framework,
        mainly to support multiple table load and non-modal load dialogues.
        The main interface is still called 
        <javadoc class="uk.ac.starlink.table.gui.TableLoadDialog"/>,
        but its definition has changed considerably.
        See the <javadoc class="uk.ac.starlink.table.gui.">javadocs</javadoc>
        for details.
        </p>
        <p>The save dialogue framework has also undergone some
        incompatible changes to support writing of multiple files,
        though these are less dramatic.  There are backwardly incompatible
        effects on the APIs of
        <code>SaveWorker</code>, <code>TableSaveChooser</code>
        and <code>TableSaveDialog</code> and its implementations.
        </p></dd>
    <dt>Storage policies:</dt>
    <dd><p>
        <ul>
        <li>New StoragePolicy
            <javadoc class="uk.ac.starlink.table.StoragePolicy"
                     member="ADAPTIVE"/>,
            which effectively uses memory for relatively small tables
            and scratch disk files for relatively large ones.
            The intention is that for most purposes this can be used
            without the user or the programmer having to guess whether
            small or large tables are likely to be in use.
            The implementation makes use in some circumstances of
            direct byte buffer allocation (=<code>malloc()</code>),
            which means that the size of the controlling java process can
            grow beyond the size of the maximum specified java heap.
            The Adaptive storage policy is the new default.
            </li>
        <li>Added method <javadoc class="uk.ac.starlink.table.ByteStore"
                                  member="toByteBuffers()"/>
            to <code>ByteStore</code> class.
            </li>
        <li>Implementation changes in Disk storage policy to reduce memory
            footprint.
            </li>
        </ul>
        </p></dd>
    <dt>Other:</dt>
    <dd><p>
        <ul>
        <li>Library now distributed as zip of jars (<code>stil_jars.zip</code>)
            as an alternative to monolithic jar file (<code>stil.jar</code>).
            This may be more appropriate for those using the library in a
            framework that contains other third party class libraries.
            </li>
        <li><code>VOTableBuilder.makeStarTable</code> now works in a streaming
            fashion.
            This should be much faster in the case of a VOTable document
            containing many TABLE elements.  There is a possibility that
            behaviour will change slightly (some post-positioned INFO/PARAM
            elements may not get picked up, tables may be successfully 
            loaded from invalid XML documents) - I don't believe these
            are likely to cause trouble, but please alert me if you disagree.
            </li>
        <li>Updated VOTable 1.2 schema to final version
            (elementFormDefault="qualified").
            </li>
        <li>New attribute Utype for 
            <javadoc class="uk.ac.starlink.table.ValueInfo"/>s.
            ValueInfo has new method <code>getUtype</code>,
            DefaultValueInfo has new method <code>setUtype</code>,
            and <code>Tables.get</code>/<code>setUtype</code> is deprecated.
            </li>
        <li>FITS files now store table names in EXTNAME (and possibly EXTVAR)
            header cards.</li>
        <li>Added configurable JDBC type conversion framework for reading
            results from SQL queries.
            By default JDBC results with Date-type contents will now be
            turned into String values, but this can be configured
            by supplying a custom
            <javadoc class="uk.ac.starlink.table.jdbc.TypeMapper"/>.
            Previously they were left as Date-type objects, which meant that
            without special attention they could not be written by
            general-purpose table output handlers.
            </li>
        <li>Better behaviour (warn + failover) when attempting to read large
            files on 32-bit OS or JVM.
            </li>
        <li>VOTable PARAM output now works out nullability and unspecified
            array and element size values from the data rather than leaving
            them as unspecified.
            </li>
        <li>The <code>wantRandom</code> parameter of
            <code>TableBuilder.makeStarTable</code> has been deprecated in
            the documentation.
            </li>
        <li>Fixed an obscure bug which could under rare circumstances cause
            truncation of strings with leading/trailing whitespace read
            from text-format files.
            </li>
        <li>Fixed bug in TST table output.
            </li>
        <li>Fixed bug in CSV file parsing that could ignore header row in
            absence of non-numeric columns.
            </li>
        <li>Fixed minor bug in CSV file parsing which ignored first row in
            no-header CSV file when calculating column Element Size values.
            </li>
        </ul>
        </p></dd>
    </dl>
    </p></dd>

<dt>Version 3.0-1 (9 May 2011)</dt>
<dd><p>
    <ul>
    <li>Random Groups HDUs are now tolerated, though not interpreted,
        within FITS files.</li>
    <li>JDBC table input handler now effectively downcasts
        BigInteger/BigDecimal types to Long/Double.  
        The PostgreSQL JDBC driver seems to use the Big* types routinely 
        for numeric values (which I don't think it used to do).</li>
    <li>Add <code>getLength</code> method to <code>ByteStore</code>
        interface.</li>
    <li>Add workaround for J2SE bug
        <webref url='http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4795134'                >#4795134</webref>,
        which could cause errors when reading compressed FITS files.</li>
    <li>Fix FITS character handling bug which could cause corrupted FITS
        files on output in presence of non-ASCII characters.</li>
    </ul>
    </p></dd>

<dt>Version 3.0-2 (30 June 2011)</dt>
<dd><p>
    <ul>
    <li>Fixed a significant crossmatching bug in SkyMatchEngine.
        If all points in a table were on one side of the RA=0 line,
        but the error radius extended across that line, matches on the
        other side could be missed.  Matches could also be missed if
        different tables used different conventional ranges for RA
        (e.g. -180..180 in one case and 0..360 in another).
        This fix may in some, but not most, cases result in slower matching
        than previously.
        </li>
    <li>Added new public class
        <javadoc class="uk.ac.starlink.votable.VOTableDOMBuilder"/>
        which provides a SAX <code>ContentHandler</code> implementation
        with similar functionality to <code>VOElementFactory</code>.
        </li>
    </ul>
    </p></dd>

<dt>Version 3.0-3 (27 October 2011)</dt>
<dd><p>
    <ul>
    <li>PARAMref with no referent no longer causes uncaught
        NullPointerException.</li>
    </ul>
    </p>
    </dd>

<dt>Version 3.0-4 (17 December 2012)</dt>
<dd><p>
    <ul>
    <li>Support for VOTable version 1.3 is now implemented.
        When reading version-1.3 VOTables, empty <code>TD</code>
        elements, indicating null values, are permitted for all data types,
        and the new BINARY2 serialization format is supported.
        When writing version-1.3 VOTables in the TABLEDATA serialization
        format, empty TD elements are used rather than marking magic
        values with the <code>VALUES</code>/<code>null</code> attribute.</li>
    <li>Selection of VOTable version for output is now done on a more
        configurable basis;
        see the new subsection <ref id="voOutVersion"/>.</li>
    <li>VOTable output no longer writes schemaLocation 
        attribute by default.</li>
    <li>Infinite values are now encoded correctly in VOTable output
        ("<code>+Inf</code>"/"<code>-Inf</code>",
        not "<code>Infinity</code>"/"<code>-Infinity</code>").</li>
    <li>The VOTable MIME type is now parameterised
        with the (standard, VOTable 1.3) parameter "serialization"
        rather than the (non-standard) parameter "encoding"
        to indicate serialization type (TABLEDATA, BINARY etc)</li>
    <li>You can now reference tables in multi-extension FITS files by name
        (EXTNAME or EXTNAME-EXTVER) as an alternative to by HDU index.</li>
    <li>IPAC output format is now supported.</li>
    <li>IPAC format "<code>long</code>"/"<code>l</code>" column type,
        which has apparently now become official,
        is now accepted in input without warning.</li>
    <li>IPAC headers may now use minus signs instead of whitespace.</li>
    <li>Now copes with 1-column CSV files.</li>
    <li>Fix bug which failed when attempting to read FITS files with 
        complex array columns (<code>TFORMn=rC/rM</code>).</li>
    <li>Fix integer arithmetic bug in <code>FileByteStore</code> which failed
        when attempting to cache very large (multi-Gb) buffers.</li>
    <li>Fix serious and long-standing bug (bad TZERO header, causes subsequent
        reads to fail) for FITS output of boolean array columns.</li>
    </ul>
    </p></dd>

<dt>Version 3.0-5 (1 July 2013)</dt>
<dd><p>
    <ul>
    <li>Add read-only support for
        <webref url="http://cdf.gsfc.nasa.gov/">CDF</webref>
        (NASA Common Data Format) files.</li>
    <li>Fix CSV regression bug introduced at v3.0-4
        - CSV files now work again with MSDOS-style line breaks.</li>
    <li>Fixed FITS output bug which could result in badly-formed string-valued
        header cards (no closing quote).</li>
    </ul>
    </p></dd>

<dt>Version 3.0-6 (4 July 2014)</dt>
<dd><p>
    <ul>
    <li>Move <code>getCellRenderer</code> method out of <code>ValueInfo</code>
        interface, and <code>getCellEditor</code> out of
        <code>DefaultValueInfo</code>.  Those methods never belonged there.</li>
    <li>Add <code>getDomainMappers</code> method to <code>ValueInfo</code>.</li>
    <li>Fix TST input handler so TST files with fewer than 3 columns
        can be read.</li>
    <li>Removed the (GPL) LICENCE.txt file from the distribution.
        The software is to be considered as LGPL.</li>
    </ul>
    </p></dd>

<dt>Version 3.0-7 (3 October 2014)</dt>
<dd><p>
    <ul>
    <li>Add support in package <code>uk.ac.starlink.table.gui</code>
        for displaying table models up to 2^31 rows (larger than 2^27)
        in a JTable.</li>
    <li>Attempting to write FITS tables with &gt;999 columns now fails
        with a more helpful error message.</li>
    <li>Improved Unicode handling in VOTables.
        Fixed a serious bug in VOTable BINARY or BINARY2 output
        which generated unreadable output if any non-empty column
        had datatype="unicodeChar".
        Round-tripping VOTables will now also preserve datatype unicodeChar,
        rather than squashing the type to char
        (the serializer tests for a column aux metadata item of
        VOStarTable.DATATYPE_INFO with value "unicodeChar").
        Some lurking Unicode-related issues remain.</li>
    </ul>
    </p></dd>

<dt>Version 3.0-8 (13 November 2014)</dt>
<dd><p>
    <ul>
    <li>Add (experimental) read-only support for Gaia/DPAC
        <ref id="inGbin">GBIN</ref> format.</li>
    <li>Add utility code to <code>table.jdbc.TypeMappers</code> for
        convenience of JDBC export when you need a 'T' separator in
        Timestamp ISO-8601 serializations.</li>
    </ul>
    </p></dd>

<dt>Version 3.0-9 (6 Feb 2015)</dt>
<dd><p>
    <ul>
    <li>Reworked part of the FITS input implementation, 
        in particular adjusting the way memory mapping is done to reduce
        resource requirements on some platforms.
        If you notice any difference, it should be reduced virtual and
        perhaps resident memory usage, and some (~10%?) performance 
        improvements, when reading large FITS/colfits files.
        If you were having problems with large memory allocations leading to
        disk thrashing and system lockup when scanning files larger than RAM
        (this didn't happen on all OSes), these will hopefully have gone away.
        However, please report anything that appears to be working worse
        than before, or continued memory usage issues.</li>
    <li>Colfits files can now be accessed from streams, not just uncompressed
        disk files (though that's not necessarily a good idea).</li>
    <li>Fixed error in fits-var output
        (PCOUNT header card did not include block alignment gap).</li>
    <li>Add a hack that allows LDAC FITS tables to be treated sensibly
        in auto-format-detection mode.</li>
    <li>StarTable columns from VOTable FIELDs with unknown (illegal) datatypes
        are now interpreted as String rather than String[] types.
        This is much more sensible and avoids some serious problems when
        serializing to FITS.</li>
    <li>Add experimental <code>OnceRowPipe2</code> utility class.</li>
    </ul>
    </p></dd>

<dt>Version 3.0-10 (26 Feb 2015)</dt>
<dd><p>
    <ul>
    <li>Work round nom.tam.fits read bug that could cause EOFExceptions
        when reading VOTables with the inline FITS serialization,
        and possibly elsewhere.
        The symptoms appear to be new since v3.0-9, but could have caused
        problems elsewhere before that.</li>
    <li>An auxiliary metadata item
        <javadoc class="uk.ac.starlink.table.Tables" member="UBYTE_FLAG_INFO"
                 codetext="Tables.UBYTE_FLAG_INFO"/>
        is now available to mark columns representing unsigned byte values.
        This is set on input and respected on output by the VOTable and FITS
        I/O handlers (and on input only for CDF).
        </li>
    </ul>
    </p></dd>

<dt>Version 3.0-11 (14 April 2015)</dt>
<dd><p>
    <ul>
    <li>Remove (broken and useless) signatures from jar files in
        <code>stil_jars.zip</code> distribution file.</li>
    <li>Be less strict about recognising colfits files
        (tolerate implicit TDIMn headers).</li>
    <li>New system command option input table syntax;
        you can now use "<code>&lt;syscmd</code>" or "<code>syscmd|</code>"
        to supply input byte streams from Un*x pipelines.</li>
    </ul>
    </p></dd>

<dt>Version 3.0-12 (10 Jul 2015)</dt>
<dd><p>
    <ul>
    <li>Fix serious bug in time conversion for CDF TIME_TT2000 data types.</li>
    <li>Update JCDF library to v1.1
        (minor changes to do with leap seconds).</li>
    <li>Modify the heuristics that determine whether the first row of
        a CSV file is a header.</li>
    </ul>
    </p></dd>

<dt>Version 3.0-13 (17 Aug 2015)</dt>
<dd><p>
    <ul>
    <li>Fix a serious bug in processing of FITS bit vector
        (<code>TFORMn='rX'</code>) columns.
        Values read from these columns are presented as a
        <code>boolean[]</code> array.  In all previous versions of STIL
        the bits have appeared in that array in the wrong sequence
        (LSB..MSB per byte rather than the other way round).
        Apologies to anyone who may have got incorrect science results
        from this error in the past, and thanks to Paul Price for helping
        to diagnose it.</li>
    <li>Fix a less serious bug with <code>TFORMn='rX'</code> processing;
        attempting to read a single-element bit vector column
        (<code>TFORMn=1X</code> or <code>X</code>) previously
        resulted in an error making the file unreadable.
        Values read from such columns are now presented as
        Boolean scalars.</li>
    <li>Fix a VOTable reading bug relating to similar data
        (<code>datatype="bit"</code>) appearing in BINARY/BINARY2
        serializations.  This one was more obvious, it would usually
        generate an error when attempting to read the file.
        Since this bug has been present for ever, I assume that
        bit vectors in binary-encoded VOTables are not widely used.</li>
    </ul>
    </p></dd>

<dt>Version 3.0-14 (22 Oct 2015)</dt>
<dd><p>
    <ul>
    <li>Fix broken assertion; when reading multi-buffer (large) FITS files
        with assertions enabled, there was a spurious AssertionError.</li>
    <li>Upgrade to JCDF v1.2 - fixes a read failure when reading large
        (multi-Gb) CDF files.</li>
    <li>Adjust GBIN input handler:
        avoid descending into Class-typed members of gbin list objects,
        add logging for object-&gt;column translations,
        and improve configurability.</li>
    <li>VOTable DOM getParentNode implementation adjusted;
        I think it's more correct now, and
        this may make the VOTable DOM behave better with XPath.</li>
    </ul>
    </p></dd>

<dt>Version 3.1 (27 Nov 2015)</dt>
<dd><p>
    <ul>
    <li>Fix a long-standing crossmatch range-restriction bug
        in <code>uk.ac.starlink.table.join</code> classes.
        This could have caused missed associations (but not false positives)
        near the edge of coverage regions when using per-row errors,
        if the scale of the errors differed
        (especially differed significantly) between the matched tables.
        It affected MatchEngines ErrorCartesianMatchEngine,
        ErrorSkyMatchEngine, EllipseCartesianMatchEngine and
        EllipseSkyMatchEngine.
        Thanks to Grant Kennedy (IoA) for reporting this bug.</li>
    <li>There is a change in the signature of the
        <code>MatchEngine.getMatchBounds</code> method.
        This change is backwardly incompatible (hence the minor version
        number update), but I don't think anybody else is using this API
        directly, so hopefully the impact on users will be low.</li>
    <li>Try harder to identify epoch columns (suitable for time plot),
        in particular look for VOTable <code>xtype</code> of JD or MJD,
        and <code>units</code> of year.</li>
    </ul>
    </p></dd>

<dt>Version 3.1-1 (10 Jun 2016)</dt>
<dd><p>
    <ul>
    <li>This and subsequent releases target Java SE 6, so can no longer
        be used to build Java 5 compatible applications or run under
        the (now very ancient) Java 5 runtime.</li>
    <li>Fix bugs that led to timezone-dependent results when
        reading ISO-8601 or decimal year time columns.</li>
    <li>Fix numeric field truncation bug in LaTeX table output.</li>
    <li>Fix read failure for FITS files with non-blank TDIM for
        zero-length columns.</li>
    </ul>
    </p></dd>

<dt>Version 3.1-2 (13 Sep 2016)</dt>
<dd><p>
    <ul>
    <li>The GBIN input handler
        can now pick up more metadata from the classpath.
        For suitable tables, metadata included in datamodel classes
        if present can be interrogated to provide table 
        and column descriptions and UCDs.
        There are still some deficiencies of this functionality 
        (no column order, utypes and units missing, 
        large file "temp.xml" written to current directory)
        dependent on issues in the upstream Gaia libraries and ICD.</li>
    <li>Fix bug that caused read failures for large (&gt;0.5Gb) FITS files
        outside of current directory on 32-bit JVMs.
        This was a regression bug since v3.0-9.</li>
    <li>Fix long-standing bug in <code>FitsTableBuilder</code>
        that failed to close streams (hence release file descriptors)
        when opening FITS tables.
        Also implement <code>java.io.Closeable</code>
        in <code>BintableStarTable</code> and <code>ColFitsStarTable</code>
        to allow explicit release of file descriptors.</li>
    <li>Update JCDF library to v1.2-2 (2017-01-01 leap second).</li>
    </ul>
    </p></dd>

<dt>Version 3.2 (8 Mar 2017)</dt>
<dd><p>
    <ul>
    <li>Add a new method
        <code>getScoreScale()</code>
        to the <code>uk.ac.starlink.table.join.MatchEngine</code> interface.
        This is required to support reasonable behaviour for <em>Best</em>
        matching of <code>CombinationMatchEngine</code>s.
        It is a backwardly-incompatible change, but I'm not aware of
        <code>MatchEngine</code> implementations outside of the starjava
        codebase, so I doubt if it will cause trouble.
        </li>
    </ul>
    </p></dd>

<dt>Version 3.2-1 (29 Sep 2017)</dt>
<dd><p>
    <ul>
    <li>A non-standard convention has been introduced which allows all
        the FITS-based I/O handlers to use FITS BINTABLE extensions for
        tables with more than 999 columns.  See <ref id="wideFits"/>.
        In earlier versions, attempting to write such wide tables
        to a FITS file failed with an error.</li>
    <li>FITS headers using the ESO
        <webref url="https://fits.gsfc.nasa.gov/registry/hierarch_keyword.html"
                >HIERARCH</webref>
        convention are now recognised on input.
        Previously they were ignored.</li>
    <li>The VOTable input handler now looks for <code>COOSYS</code> elements
        referenced from <code>FIELD</code> elements and makes the relevant
        information available as column auxiliary metadata.
        The relevant aux metadata keys are defined in the
        <javadoc class="uk.ac.starlink.votable.VOStarTable"/> class as
        <code>COOSYS_*_INFO</code>.</li>
    <li>The VOTable and FITS-plus output handlers now write and reference
        <code>COOSYS</code> elements in output VOTables corresponding to
        the requirements of any
        <javadoc class="uk.ac.starlink.votable.VOStarTable"
                 />.<code>COOSYS_*_INFO</code>
        aux metadata items attached to table columns
        (<code>FIELD</code> elements).
        This means that COOSYS information associated with table columns
        can be round-tripped during read-write cycles that use
        VOTable-based serialization formats for both input and output.
        Note though that it doesn't currently work for
        references from <code>PARAM</code>s.</li>
    <li>The default version for output VOTables is now VOTable 1.3.
        New output formats <code>votable-binary2-inline</code> and
        <code>votable-binary2-href</code> are now offered alongside
        the five previously available VOTable variants.</li>
    <li>The FITS-plus output handler now writes VOTables using the
        default output VOTable version, as described in
        <ref id="voOutVersion"/>.  Previously VOTable 1.1 was used.</li>
    <li>Modified the thread-safe implementation of methods performing
        random data access (<code>getRow</code>, <code>getCell</code>) 
        to mapped byte buffers
        on <code>ColFitsStarTable</code> and <code>BintableStarTable</code>.
        These now use <code>ThreadLocal</code> accessors for reads from the
        underlying ByteBuffers rather than <code>synchronized</code> blocks.
        This performs much much better in the case of heavy contention
        (random access to table data from multiple concurrent threads)
        than before.  There may be a small degradation in single-threaded
        performance - tests indicate around 1-2%.</li>
    <li>Add new method for creating a GBIN-like table from a collection
        of existing GaiaRoot-like objects, without having to read them
        directly from a GBIN file.  See
        <javadoc class="uk.ac.starlink.gbin.GbinStarTable"
           member="createCollectionTable(uk.ac.starlink.gbin.GbinTableProfile,
                                         java.lang.Class,
                                         java.util.Collection)"
                 >GbinStarTable.createCollectionTable</javadoc>.</li>
    <li>Update PixTools to 2017-09-06 version
        (https://github.com/kuropat/eag-HEALPix,
         <code>447a7be073876dba32</code>).
        This fixes a bug in <code>vect2pix_ring</code> that gave
        the wrong value for HEALPix tile index in one half of each tile
        straddling the longitude=0 line in the equatorial region.
        It seems possible that this might have led to very infrequent
        missed associations when crossmatching in these regions,
        but tests appear to indicate that no such errors would
        actually have resulted.</li>
    <li>Long fields (&gt;10240 characters) in output CSV files
        are no longer truncated.</li>
    </ul>
    </p>
    </dd>

<dt>Version 3.2-2 (7 Nov 2017)</dt>
<dd><p>
    <ul>
    <li>Add tweak to VOTable reader that discards any INFO elements with
        names starting "<code>uk.ac.starlink.topcat.plot2.TopcatLayer</code>".
        This is a nasty workaround for a nasty bug in TOPCAT v4.5,
        which added such metadata items in large numbers to tables that
        were plotted and then saved.</li>
    </ul>
    </p>
    </dd>

<dt>Version 3.2-3 (13 Nov 2017)</dt>
<dd><p>
    <ul>
    <li>STIL classes should now build and run with recent versions
        (1.15.2, and probably later) of the nom.tam.fits library.
        It is still bundled with a custom nom.tam.fits (based on v0.96),
        but replacing that with a recent version ought to work.
        Behaviour in that case is expected, but not guaranteed,
        to be similar to using the bundled version.</li>
    </ul>
    </p></dd>

<dt>Version 3.3 (24 Apr 2018)</dt>
<dd><p>
    <ul>
    <li>Update JCDF to v1.2-3; fixes some CDF reading bugs.</li>
    <li>The IPAC table reader now matches data type specifications
        case-insensitively.</li>
    <li>Modify VOTable (<code>Multi</code>)<code>TableBuilder</code>
        implementation; it now picks up more of the DOM
        (everything up to the start of the next <code>TABLE</code> element,
        rather than just everything up to the end of the
        current <code>TABLE</code>) when a table is read.
        This is important for picking up DataLink-style
        Service Descriptors.</li>
    <li>The VOTable I/O handlers can now de/serialise DataLink-style Service
        Descriptors.  These show up as
        <javadoc class="uk.ac.starlink.votable.datalink.ServiceDescriptor"
                 />-typed table parameters.</li>
    <li>If you use a ColumnInfo as one of the parameters of a StarTable
        and serialise it to VOTable, its AuxData will now be honoured,
        for instance to write VOTable-specific PARAM attributes like
        <code>ref</code> and <code>ID</code>.</li>
    <li>Multiple-table VOTable documents are now written with each table
        in its own RESOURCE element, rather than all TABLEs as siblings
        within the same RESOURCE element.  This is to enable grouping of
        Service Descriptor RESOURCE elements with their associated TABLE.
        The output is unchanged for single-table VOTable output documents.
        Hopefully this slight change of output format will not cause
        compatibility problems with other software.</li>
    </ul>
    </p></dd>

<dt>Version 3.3-1 (18 May 2018)</dt>
<dd><p>
    <ul>
    <li>VOTable serializer will now write attribute values in single quotes
        if they contain a lot of double quote characters.</li>
    </ul>
    </p></dd>

<dt>Version 3.3-2 (2 Nov 2018)</dt>
<dd><p>
    <ul>
    <li>Slight improvements to the
        <ref id="jdbcConfig">JDBC Configuration</ref>
        section of this manual.</li>
    <li>GBIN reading fix to work around changed behaviour 
        in recent GaiaTools versions (&gt;=21.1.0 and &gt;=20.3.0)
        that caused GBIN table reading to fail.</li>
    </ul>
    </p></dd>

<dt>Version 3.3-3 (9 May 2019)</dt>
<dd><p>
    <ul>
    <li>Experimental support for VOTable 1.4 and its new
        <code>TIMESYS</code> element.
        This support corresponds to WD-VOTable-1.4-20190403.
        The functionality may be modified in future releases depending
        on how the VOTable 1.4 specification evolves.
        <ul>
        <li>For VOTable columns that reference TIMESYS elements,
            the relevant information is now accessible as
            column auxiliary metadata keyed by the
            <code>
            <javadoc class="uk.ac.starlink.votable.VOStarTable"
                     />.TIMESYS_*_INFO</code> static members.</li>
        <li>Any columns referencing TIMESYS elements
            read from VOTable-based formats (VOTable or FITS-plus)
            can now be written out to VOTable-based formats with
            equivalent TIMESYS references included,
            so TIMESYS round-tripping for columns works;
            however this will only be done if the VOTable output version
            is set to 1.4
            (<javadoc class="uk.ac.starlink.votable.VOTableVersion"
                      /><code>=V14</code>).
            By default (at least as long as 1.4 is not finalised)
            the output version is 1.3; it can be set either explicitly
            on output methods or globally as documented in that class.
            Currently this TIMESYS output works only for table columns
            (FIELDs) not parameters (PARAMs).</li>
        <li>New <javadoc class="uk.ac.starlink.votable.TimesysElement"/>
            <code>VOElement</code> subclass and some associated
            methods.</li>
        <li>Columns referencing TIMESYS elements now provide a suitable
            <javadoc class="uk.ac.starlink.table.TimeMapper"/>
            from their
            <javadoc class="uk.ac.starlink.table.ValueInfo"
                     member="getDomainMappers()"/> method.</li>
        </ul>
        </li>
    <li><code>FIELD/@ref</code> attributes are no longer imported as
        <javadoc class="uk.ac.starlink.votable.VOStarTable" member="REF_INFO"/>
        column aux metadata items,
        since they often interfere with TIMESYS references.
        Doing this was probably always a bad idea since the referencing
        is not kept track of within the application, so withdrawing
        this functionality makes sense, but beware that it might
        change or break some existing behaviour.</li>
    <li>Various changes to support the semi-standard
        <webref url="&URL.HEALPIX_FITS;">HEALPix-FITS</webref>
        serialization convention.
        A new class
        <javadoc class="uk.ac.starlink.table.HealpixTableInfo"/>
        is added to define metadata relating to a table containing
        HEALPix pixel data.
        The FITS <ref id="inFits">input</ref>
        and <ref id="outFits">output</ref> handlers attempt to honour
        this information on a best-efforts basis, and a new
        <code>fits-healpix</code> output handler is provided that
        tries harder to write HEALPix data tables
        conforming to the convention.</li>
    <li>The signatures of some metadata access methods in the
        core classes have been redefined to use generics.
        The altered methods, which previously used raw types, are
        <code>getParameters()</code>,
        <code>getColumnAuxDataInfos()</code>
        in <javadoc class="uk.ac.starlink.table.StarTable"/>,
        and
        <code>getAuxData()</code>,
        <code>setAuxData(List&lt;DescribedValue&gt;)</code>
        in <javadoc class="uk.ac.starlink.table.ColumnInfo"/>.
        These changes enforce behaviour that was previously required by
        contracts stated in the javadocs.
        Because of the backward compatibility features of generics
        this should not cause new errors at compilation or run time
        as long as the methods were being used in accordance with the
        existing documentation, but compilation warnings may change.
        </li>
    <li>Artifacts comprising a Maven package for STIL-IO
        (the classes excluding the matching capabilities)
        are now assembled as part of the build process.</li>
    <li>Fix bug/misfeature in CDF table parameter construction:
        CDF global attributes were ignored
        (with a "WARNING: Omitting complicated global attribute" message)
        if they contained any null entries.
        Now such entries are just ignored and the table parameter is
        constructed from the global attribute using the
        non-null entries.</li>
    <li>Be a bit more careful when writing FITS headers
        <code>TCOMMn</code>, <code>TUCDn</code> and <code>TUTYPn</code>
        if their value may overflow the 80-character mark,
        including refusing to write them even if nom-tam-fits
        long header support is switched on.</li>
    <li>Adjust <javadoc class="uk.ac.starlink.util.PipeReaderThread"/>
        implementation to work round occasional Debian-Astro
        build failures.</li>
    </ul>
    </p></dd>

<dt>Version 3.4 (18 November 2019)</dt>
<dd><p>
    <ul>
    <li>Provide String-based support for offset (e.g. unsigned) 64-bit integers
        in FITS files.
        64-bit integer columns (<code>TFORMn='K'</code>)
        with non-zero integer offsets
        (<code>TSCALn=1</code>, <code>TZEROn&lt;&gt;0</code>)
        are now read from FITS as Strings (see <ref id="inFits"/>),
        and such Strings can be written to FITS as long integers
        (see <ref id="outFits"/>).
        In previous versions an attempt was made to represent
        in-range values as Java longs, using a null value
        for out-of-range values.</li>
    <li>Avoid sometimes losing precision when reading ASCII/CSV values
        in the range +/-(1e-38..1e-45).</li>
    <li>Permit FITS and VOTable files with zero-length string columns.
        Previously all-null or zero-length string columns were
        sometimes forced to single-character values.</li>
    <li>Update mapped file unmapping implementation to work 
        for java9+.</li>
    </ul>
    </p></dd>

<dt>Version 3.4-1 (5 June 2020)</dt>
<dd><p>
    <ul>
    <li>The <label>ECSV</label> (Enhanced Character Separated Values)
        storage format is now supported for
        <ref id="inEcsv">input</ref> and
        <ref id="outEcsv">output</ref>.</li>
    <li>The <label>Feather</label>
        storage format is now supported for
        <ref id="inFeather">input</ref> and
        <ref id="outFeather">output</ref>.</li>
    <li>Replace <code>PixtoolsHealpixSkyPixellator</code> with
        <code>CdsHealpixSkyPixellator</code>
        in the <code>uk.ac.starlink.table.join</code> package,
        based on the
        <webref url="https://github.com/cds-astro/cds-healpix-java"
                >cds-healpix-java</webref> library
        from F-X Pineau (CDS).
        The new implementation is generally faster.</li>
    <li>FITS ASCII table extensions with TFORM values of <code>In</code>
        are now treated as 64-bit integers for <code>n&gt;=10</code>
        rather than <code>n&gt;10</code>.</li>
    <li>Improve performance when reading long String values
        from FITS files.</li>
    </ul>
    </p>
    </dd>

<dt>Version 4.0 (11 January 2021)</dt>
<dd><p>This major release provides support for parallel processing of
    table data, enhances table I/O specification options,
    addresses some long-standing issues that require
    backwardly incompatible API changes, and breaks out crossmatching
    classes into a separate package.  The documentation has been
    extended accordingly.
    Library users using existing STIL functionality for table I/O should
    not need to make too many code changes when upgrading from STIL v3,
    but those providing <code>StarTable</code> or I/O handler implementations
    may need to modify their implementations in accordance with the API
    changes; see below for details.
    <dl>
    <dt>Notable new functionality</dt>
    <dd><p>
        <ul>
        <li>Support for parallel table data processing added,
            see <ref id="parallel"/>.</li>
        <li>Improved patterns for threadsafe random access using
            <code>StarTable.getRowAccess</code> method,
            see <ref id="randomAccess"/>.</li>
        <li>I/O handler configuration options may be configured by
            user-supplied string, see <ref id="handlerSpec"/>.
            Config options are provided for several handler implementations;
            more may be forthcoming in future releases.</li>
        <li>Table Schemes introduced, which can be used to load tables
            not serialized as byte streams, see <ref id="tableScheme"/>.</li>
        <li>Auto file format detection now examines filenames to help
            guess input file format.</li>
        <li><code>StarTable.close()</code> can now be called to
            release global resources such as file descriptors and
            (where unmapping is supported) mapped files.</li>
        </ul>
        </p></dd>
    <dt>API changes</dt>
    <dd><p>
        <ul>
        <li>New data access methods added to <code>StarTable</code> interface
            to support multithreaded processing:
            <code>getRowAccess</code>, <code>getRowSplittable</code>.</li>
        <li>New <code>default</code> methods added to StarTable interface
            for convenience in parameter handling:
            <code>getParameterByName</code> and <code>setParameter</code>.</li>
        <li><code>StarTable</code> has a new <code>close</code> method
            that should relinquish any non-heap-based resources
            (file descriptors, mapped files).
            In most cases this can be implemented as a no-op.</li>
        <li><code>RowSequence</code> and <code>StarTable</code>
            now implement <code>java.io.Closeable</code>
            so they can be used in try-with-resources statements.</li>
        <li>New method <code>looksLikeFile</code> added to
            <code>TableBuilder</code> interface,
            to enable filename-based (file extension-based)
            input format guessing.</li>
        <li>Aux data access methods
            <code>getAuxData</code>, <code>getAuxDatumByName</code>,
            <code>setAuxDatum</code> are now on the <code>ValueInfo</code>
            interface, rather than on (<code>ValueInfo</code>'s subtype)
            the <code>ColumnInfo</code> class.  This means that
            table parameters, as well as table columns, can now sport
            auxiliary metadata.  That should really always have been
            the case.</li>
        <li>Add <code>ValueInfo.getXtype</code> method.</li>
        <li>Clarified requirements of class <code>RandomStarTable</code>;
            implemented data access methods <em>must</em> be thread-safe.</li>
        <li><code>RowData</code> introduced as super-interface of
            <code>RowSequence</code>.</li>
        <li>Class <code>RandomWrapperStarTable</code> has been withdrawn;
            the implementation was complicated and
            hard to upgrade, and it was probably(?) never used.</li>
        <li>Some other minor API changes.</li>
        </ul>
        </p></dd>
    <dt>Package contents</dt>
    <dd><p>
        <ul>
        <li>The classes in the namespace
            <code>uk.ac.starlink.table.join</code>
            (table joins and crossmatches)
            are no longer part of the STIL library.
            Those classes are still available elsewhere,
            but the functionality is distinct from the I/O that
            STIL mostly provides, there was never corresponding
            tutorial text in the user document,
            they required external dependencies that sometimes
            complicated STIL deployment, and they are not needed
            by most STIL users.
            The dependencies <code>cdshealpix.jar</code> and
            <code>htmIndex.jar</code> are no longer required.</li>
        </ul>
        </p></dd>
    <dt>Documentation</dt>
    <dd><p>
        <ul>
        <li>Descriptions of input handlers (<ref id="tableBuilders"/>)
            and output handlers (<ref id="starTableWriters"/>) updated.</li>
        <li>Add new documentation section <ref id="fitsPlus"/>
            describing the the FITS-plus convention.</li>
        <li>Add <code>Documented</code> interface to improve
            auto-documentation of I/O handlers.
            Handler documentation now resides in the code itself,
            and is extracted programmatically to generate entries
            in user documentation.</li>
        </ul>
        </p></dd>
    <dt>Miscellaneous enhancements and bugfixes</dt>
    <dd><p>
        <ul>
        <li>COOSYS and TIMESYS attributes are now preserved
            during VOTable I/O for table PARAMs
            (as well as for FIELDs, which was already the case).</li>
        <li>Add miscellaneous utility methods in <code>Tables</code>
            class etc.</li>
        <li>Fix ECSV output bug: encoding was incorrect for metadata
            scalars with certain non-alphanumeric first characters,
            leading to invalid YAML.</li>
        <li>Remove some unhelpful per-column metadata items from
            ECSV output.</li>
        <li>Prevented <code>HealpixSkyPixellator.calculateDefaultK</code>
            from returning -1 for large angles.</li>
        <li>Improve exception handling in
            <code>StreamTableSink</code>/<code>OnceRowPipe</code>
            implementations, make interruption work better.</li>
        <li>Fix bug that could give unhelpful table load error message
            for very short non-FITS files in auto-detection mode.</li>
        </ul>
        </p></dd>
    </dl>
    </p></dd>

<dt>Version 4.0-1 (16 April 2021)</dt>
<dd><p>
    <ul>
    <li>Variable-length array-valued columns in FITS tables
        (<code>P</code>/<code>Q</code> descriptors)
        can now be read even in compressed or streamed input.</li>
    <li>Add configuration option <code>header</code> for
        <ref id="inCsv">CSV input handler</ref>,
        to indicate whether header line is present.</li>
    <li>Add configuration option <code>maxSample</code> for
        <ref id="inCsv">CSV</ref> and <ref id="inAscii">ASCII</ref>
        input handlers to reduce 2-pass read time.</li>
    <li>The <ref id="outFits">fits-var</ref> output handler now avoids
        use of the THEAP keyword (no pre-heap gap is written).
        Heap padding is legal FITS, but bugs in other
        FITS software mean that some third party components
        (including <code>fverify</code>) have problems
        with such files.</li>
    <li>Fix bugs that meant writing long (&gt;2Gb)
        <ref id="outFits">fits-var</ref> files could output
        illegal/corrupted FITS.</li>
    <li>Fixed long-standing bug in <code>FileByteStore.copy()</code>;
        this potentially serious issue could have caused broken
        file caching of any/all streams, but mostly seemed to
        affect large (&gt;2Gb) streams in practice.</li>
    <li>JDBC output no longer attempts to create VARCHAR(0) columns.</li>
    </ul>
    </p></dd>

<dt>Version 4.0-2 (10 June 2021)</dt>
<dd><p>
    <dl>
    <dt>New functionality:</dt>
    <dd><p>
        <ul>
        <li>Apache Parquet format is now supported for
            <ref id="inParquet">input</ref> and
            <ref id="outParquet">output</ref>
            (note not all distributions include Parquet-MR
            support libraries).</li>
        <li>AAS Machine-Readable Table (MRT) format is now supported
            for <ref id="inMrt">input</ref>.</li>
        <li>ECSV format <ref id="inEcsv">input</ref> and
            <ref id="outEcsv">output</ref> handlers
            are upgraded to version 1.0 of the ECSV format,
            meaning they can now read and write array-valued columns.</li>
        <li><javadoc class="uk.ac.starlink.fits.AbstractFitsTableWriter"/>
            and subclasses now have configuration methods
            <code>setAllowSignedByte</code>,
            <code>setAllowZeroLengthString</code>,
            <code>setWide</code> and <code>setPadCharacter</code>.
            These should be used in preference to the various custom
            constructors, which have now been deprecated.</li>
        <li>Add new table scheme <ref id="scheme-test">test</ref>
            (<code>TestTableScheme</code>).</li>
        </ul>
        </p></dd>
    <dt>Minor enhancements and behaviour changes:</dt>
    <dd><p>
        <ul>
        <li>Space-delimited ECSV files now write empty fields quoted.</li>
        <li>Unknown or unsupported column datatype values in ECSV files
            are now treated like <code>string</code> rather than
            causing table read failure.</li>
        <li>Empty strings in FITS 1-character columns are now returned as
            blank values rather than ASCII NUL ('<code>\0</code>').</li>
        <li>Undersized, including zero-length, strings written to FITS
            columns are now by default terminated with an ASCII NUL
            rather than in some cases padded with spaces.</li>
        </ul>
        </p></dd>
    <dt>Bugfixes:</dt>
    <dd><p>
        <ul>
        <li>Fix regression bug since v4.0 that meant table
            drag'n'drop wasn't working.</li>
        <li>Fix regression bug since v4.0 that meant <code>jdbc:</code>
            URLs didn't work.</li>
        <li>Fix long-standing logic error in ASCII/CSV input handler that
            could misidentify column types and cause read failures.</li>
        <li>FITS TZERO headers are now written correctly with numeric values
            rather than string values.</li>
        </ul>
        </p></dd>
    </dl>
    </p></dd>

<dt>Version 4.0-3 (2 July 2021)</dt>
<dd><p>
    <ul>
    <li>STIL is now available at the
        <webref url="https://search.maven.org/search?q=g:uk.ac.starlink"
                >Maven Central Repository</webref>
        under the groupId <code>uk.ac.starlink</code>.
        Packaging details may change in future releases.</li>
    <li>Bzip2 decoding is now done by internal classes
        (<code>uk.ac.starlink.util.bzip2</code>) rather than
        external apache library; this reduces the dependency
        requirement in the POM.</li>
    <li>Guess meaning for some non-standard COORDSYS values in 
        HEALPix-FITS files, e.g. allow "GALACTIC" instead of "G".</li>
    </ul>
    </p></dd>

<dt>Version 4.0-4 (15 Oct 2021)</dt>
<dd><p>
    <ul>
    <li>Columns that are all blank in ASCII-like tables
        (CSV, ASCII, TST) are now interpreted as type String
        not boolean.</li>
    <li>Fix serious threading bug that could return nonsense values
        from fixed-length string fields during parallel processing
        of large cached/randomised tables.</li>
    <li>Files compressed using multi-stream bzip2 compression
        (e.g. <code>pbzip2</code> output) are now supported
        alongside single-stream bzip2.</li>
    </ul>
    </p></dd>

<dt>Version 4.0-5 (31 January 2022)</dt>
<dd><p>
    <ul>
    <li>The <ref id="inPds4">PDS4</ref>
        (NASA's Planetary Data System v4) file format is now
        supported for input tables.</li>
    <li>Improve identification of TIME_TT2000 columns as time values
       in certain CDF files.</li>
    <li>Bugfix update of JCDF to v1.2-4.</li>
    <li>Fix failure when attempting to read unsigned 32-bit integer
        values from parquet files.</li>
    </ul>
    </p></dd>

<dt>Version 4.1 (6 April 2022)</dt>
<dd><p>
    <ul>
    <li>Dependency on the nom.tam.fits library is removed
        All FITS I/O is now implemented internally.</li>
    <li>New classes are introduced in the
        <javadoc class="uk.ac.starlink.fits."/> package,
        to replace nom.tam.fits usage: see
        <javadoc class="uk.ac.starlink.fits.CardImage"/>,
        <javadoc class="uk.ac.starlink.fits.CardType"/>,
        <javadoc class="uk.ac.starlink.fits.CardFactory"/>,
        <javadoc class="uk.ac.starlink.fits.ParsedCard"/>,
        <javadoc class="uk.ac.starlink.fits.FitsHeader"/>,
        <javadoc class="uk.ac.starlink.fits.FitsUtil"/>,
        <javadoc class="uk.ac.starlink.fits.AsciiTableStarTable"/>.</li>
    <li>Other API changes: <code>java.io.DataOutput</code>
        parameter is replaced by <code>java.io.OutputStream</code>
        in some FITS and VOTable I/O methods (e.g.
        <javadoc class="uk.ac.starlink.votable.VOSerializer"
         member="writeHrefDataElement(java.io.BufferedWriter,
                                      java.lang.String,
                                      java.io.OutputStream)"/>).</li>
    <li>FITS header I/O now supports reading FITS 4.0 long-string headers
        (CONTINUE records).</li>
    <li>FITS header values of the form "(a,b,c,...)" used as BINTABLE table
        parameters are now interpreted where possible as numeric arrays;
        this works for long-string values (CONTINUE records) as well.</li>
    <li>New classes introduced in package UTIL for fast buffered I/O:
        <javadoc class="uk.ac.starlink.util.DataBufferedInputStream"/>,
        <javadoc class="uk.ac.starlink.util.DataBufferedOutputStream"/>.</li>
    <li>Substantial I/O performance improvements,
        mainly for FITS and VOTable, e.g.:
        writing to FITS 2x,
        reading FITS from a stream 2x,
        reading VOTable with inline BINARY/BINARY2 4x,
        writing VOTable with inline BINARY/BINARY2 2x,
        writing VOTable with TABLEDATA 1.5x.</li>
    <li>Add configuration options
        <code>compact</code> and <code>encoding</code> to
        <ref id="outVotable">VOTable output handler</ref>.
        By default thin (&lt;=4 column) TABLEDATA VOTables
        are now written in "compact" mode, using reduced whitespace.</li>
    <li>ECSV format now preserves table name.</li>
    <li>FITS BINTABLE reader now copes with (illegal?) embedded spaces
        in TDIMn headers.</li>
    <li>Adjust MRT null handling; "-" in a single-character field is no
        longer interpreted as null.</li>
    </ul>
    </p></dd>

<dt>Version 4.1-1 (10 June 2022)</dt>
<dd><p>
    <ul>
    <li>Fix FITS parsing issue that could result in StackOverflowError
        for long array-valued headers.</li>
    <li>Fix bug in multi-threaded read of string columns from
        colfits files.</li>
    <li>Reduce number of file mapping calls by FITS readers.</li>
    </ul>
    </p></dd>

<dt>Version 4.1-2 (8 July 2022)</dt>
<dd><p>
    <ul>
    <li>The
        <javadoc class="uk.ac.starlink.fits.CardFactory"
                 />.<javadoc class="uk.ac.starlink.fits.CardFactory$Config"
                             ><code>Config</code></javadoc>
        class now has a pluggable option to control handling of
        FITS header string/comment values that contain illegal
        FITS header characters.  Previously such characters would
        case FITS output to fail; now by default such characters
        are replaced with a '<code>?</code>' character.</li>
    </ul>
    </p></dd>

<dt>Version 4.1-3 (5 October 2022)</dt>
<dd><p>
    <ul>
    <li>Fix PDS4 reader to accept columns of type
        <code>ASCII_Numeric_Base16</code>
        without the read operation failing.</li>
    <li>Fix VOTable reader so BINARY/2 VOTables with no columns
        don't read forever.</li>
    </ul>
    </p></dd>

<dt>Version 4.1-4 (20 April 2023)</dt>
<dd><p>
    <ul>
    <li>Modify behaviour when writing URL-valued aux metadata
        items in VOTable <code>FIELD</code>s:
        null <code>name</code> for <code>ValueInfo</code>
        now results in <code>title</code>-less <code>LINK</code>
        instead of no <code>LINK</code>.</li>
    <li>The <ref id="jdbc">JDBC input handler</ref>
        should now cope with columns that
        are array-valued in the database, viewing their contents as
        <code>String[]</code> or primitive array values where possible.
        Previously they showed up as <code>java.sql.Array</code> objects,
        which are generally not easy to deal with in STIL.</li>
    <li>Add some configuration options to the
        <ref id="inGbin">GBIN</ref> input handler.</li>
    <li>Fail with an error rather than silently reading a broken table
        when encountering GaiaTools/zStd-jni bug during
        <ref id="inGbin">GBIN</ref> input.</li>
    <li>Empty/invalid fields encountered by the
        <ref id="inPds4">PDS4 reader</ref>
        no longer cause the table read to fail.</li>
    <li>Make FITS and VOTable output handlers robust against
        input tables that declare incorrect row counts.</li>
    <li>Modify column width determination in text-like output formats
        (<ref id="outText"><code>text</code></ref>,
         <ref id="outAscii"><code>ascii</code></ref>,
         <ref id="outIpac"><code>ipac</code></ref>)
        to avoid occasional unwanted truncation of formatted values.
        Tables are now read in two passes, the first to establish
        column widths and the second to write the data.
        By default all rows are sampled, but the
        <code>sampledRows</code> option can be configured so that
        only some rows are sampled, which is more like
        the old behaviour.</li>
    <li>PDS4 reader now reads <code>Ascii_Numeric_Base16/8/2</code>
        fields as numeric not string 
        (updated pds4-jparser library code).</li>
    <li>Slight change to RESOURCE structure of Primary HDU metadata 
        in multi-table FITS-plus output.
        This fixes a problem in which saved Service Descriptors
        could end up associated with the wrong tables.</li>
    <li>Write empty string not semi-standard "<code>nan</code>"
        token for NaN in <ref id="outEcsv">ECSV writer</ref>.</li>
    <li>Improve <ref id="inEcsv">ECSV reader</ref> performance,
        especially for Gaia DR3 bulk download files
        (which use semi-standard "<code>null</code>" token).</li>
    <li>The <javadoc
             class="uk.ac.starlink.votable.datalink.ServiceDescriptor"/>
        class now has access to <code>contentType</code> and
        <code>exampleURL</code> parameters introduced in DataLink 1.1.
        These are preserved by VOTable I/O.</li>
    </ul>
    </p></dd>

<dt>Version 4.2 (1 November 2023)</dt>
<dd><p>From this version the access to HTTP(S) URLs is by default done
    using the <javadoc class="uk.ac.starlink.auth.">AUTH</javadoc> package,
    which provides transparent authenticated access to resources
    protected by authentication declared according to the (currently draft)
    VO authentication standards.
    This ought not to affect STIL behaviour unless steps are taken to
    initialise authentication, but unforseen changes are possible.
    See <ref id="auth"/>.
    </p></dd>

<dt>Version 4.2-1 (29 February 2024)</dt>
<dd><p>
    <ul>
    <li>Implement HAPI <ref id="inHapi">input handler</ref>
        and <ref id="scheme-hapi">scheme</ref> support.</li>
    <li>VOTable <code>COOSYS/@refposition</code> attribute
        is now available from column aux metadata as
        <javadoc class="uk.ac.starlink.votable.VOStarTable"
                               member="COOSYS_REFPOSITION_INFO"
                 codetext="VOStarTable.COOSYS_REFPOSITION_INFO"/>.
        STIL now fully(?) supports WD-VOTable-1.5-20231120.</li>
    <li>Improve error reporting for corrupted/truncated FITS files.</li>
    <li>Use authentication and HTTP-level compression for
        external (href-referenced) VOTable STREAM data.</li>
    <li>Upgrade snakeyaml library (used for ECSV headers)
        from 1.25 to 2.2.
        No change in behaviour (or security) expected,
        but prevents vulnerability warnings in some circumstances.</li>
    <li>Fix bug that could generate spurious EOFExceptions when reading
        multi-table basic FITS files.</li>
    </ul>
    </p></dd>

<dt>Version 4.3 (7 August 2024)</dt>
<dd><p>
    <ul>
    <li>At this version the source code and build system have been
        substantially tidied up so that the packages can be built
        straightforwardly using modern versions of Java (8, 11, 17, 21)
        with a minimum of warnings.
        These changes should be mostly invisible to users,
        but some behaviour changes are possible.</li>
    <li>Some changes have been made to URL handling.
        Syntactically invalid URIs are now mostly rejected.
        This should not be noticeable in most cases,
        but questionable usages like embedded spaces in URLs may need
        to be replaced by their <code>%</code>-encoded equivalents.</li>
    <li>Documentation for
        <ref id="tableBuilders">input</ref> and
        <ref id="starTableWriters">output</ref> handler config options
        now mostly reports their default values.</li>
    <li>FITS output handling improved and reorganised to provide
        more flexible configuration options.
        Most FITS output handlers are now deprecated in favour of
        <javadoc class="uk.ac.starlink.votable.UnifiedFitsTableWriter"/>.
        This is all now documented under the <ref id="outFits">FITS</ref>
        heading, there is no longer a separate <code>colfits</code>
        section in the documentation.</li>
    <li>New <ref id="scheme-test"><code>test</code> scheme</ref> options
        "<code>g</code>" and "<code>w</code>".</li>
    <li>New config option <code>date</code> for
        <ref id="outVotable">VOTable output handler</ref>
        to control whether a datestamp comment is written.</li>
    <li>Fits-plus output handlers now honour <code>date</code> 
        config option for VOTable metadata 
        as well as the <code>DATE-HDU</code> header card.</li>
    <li>Upgrade parquet support libraries to parquet-mr 1.13.1.
        This means that snappy compression is now supported for
        MacOS ARM, as well as other, architectures.</li>
    <li>Add support for LZ4_RAW compression to Parquet I/O handlers.</li>
    <li>Parquet reader now copes with some other variants of
        array-valued columns.</li>
    <li>New config option <code>tryUrl</code>
        in <ref id="inParquet">parquet input handler</ref>,
        set false by default to avoid cryptic error messsages
        when trying to open remote parquet files.</li>
    <li>New config option <code>compression</code> in
        <ref id="outParquet">Parquet output handler</ref>.</li>
    <li>New config option <code>usedict</code> in
        <ref id="outParquet">Parquet output handler</ref>.</li>
    <li>Decrease size of parquet output files in most cases 
        when writing NaNs and empty strings/arrays.</li>
    <li>Fix parquet input handler bug related to compression.</li>
    <li>Fix VOTable output bug that wrote infinite floating point 
        array elements to TABLEDATA as "<code>+/-Infinity</code>"
        rather than "<code>+/-Inf"</code>.</li>
    <li>Xtypes are now written into FITS headers using the
        non-standard header card <code>TXTYPnnn</code>.</li>
    <li>Non-standard FITS headers
        <code>TUCDnnn</code> and <code>TUTYPnnn</code>
        are now written with comment parts, space permitting.</li>
    <li>Avoid writing <code>arraysize="1"</code>
        in <code>FIELD</code>/<code>PARAM</code> elements
        of VOTables with version &gt;=1.3,
        in accordance with VOTable 1.3 Erratum #3.</li>
    <li>New config option <code>useFloat</code>
        in <ref id="inMrt">MRT input handler</ref>;
        this is set false by default to avoid a rare bug
        that read large values as infinite.</li>
    </ul>
    </p></dd>

<dt>Version 4.3-1 (6 November 2024)</dt>
<dd><p>
    <ul>
    <li>Update JSON-java library to 20240303.
        Behaviour is not expected to change in interesting ways,
        but some vulnerabilities are addressed.</li>
    <li>More units (seconds with SI prefixes) are now properly handled
        when assigning
        Time <javadoc class="uk.ac.starlink.table.DomainMapper"/>
        for VOTable <code>TIMESYS</code>-referencing fields.</li>
    <li>Fix regression bug (since v4.2) in HTTP 3xx redirect handling  
        that failed to cope with relative Location field values.</li>
    </ul>
    </p></dd>

<dt>Version 4.3-2 (7 March 2025)</dt>
<dd><p>
    <ul>
    <li>Parquet <ref id="inParquet">input</ref> and
        <ref id="outParquet">output</ref> handlers now support the
        <webref url="https://www.ivoa.net/documents/Notes/VOParquet/"
                >VOParquet convention</webref>
        for embedded metadata.</li>
    <li>Fix some significant parquet I/O bugs related to
        array-valued columns.</li>
    <li>The parquet reader can now read data with the un-annotated
        BINARY (BYTE_ARRAY) type;
        it is interpreted as a byte array.</li>
    <li>The <ref id="inCdf">CDF input handler</ref> can now extract
        multiple tables from a single CDF file.</li>
    <li>Upgrade JCDF to version 1.2.5.
        Significant improvements to CDF read performance.</li>
    <li>Embedded underscores may be used in numeric literals
        when specifying table schemes
        <ref id="scheme-loop"><code>loop</code></ref> and
        <ref id="scheme-test"><code>test</code></ref>.</li>
    <li>Fix NullPointerException when writing TABLEDATA VOTable output
        for null entries in <code>String[]</code>-valued columns.</li>
    <li>Add <code>kvmap</code> config option to
        <ref id="outParquet">parquet output handler</ref>.</li>
    <li>Fix <code>ivoa_cookie</code> authentication handling 
        to work in presence of multiple cookies.</li>
    </ul>
    </p></dd>

<dt>Version 4.3-3 (30 July 2025)</dt>
<dd><p>
    <ul>
    <li>VOTable and associated output handlers can now write
        <code>String[]</code>-valued columns without known
        element lengths -
        see <javadoc class="uk.ac.starlink.votable.StringElementSizer"/>
        configured via new class
        <javadoc class="uk.ac.starlink.votable.VOSerializerConfig"/>.
        Related: earlier <code>VOSerializer.makeSerializer</code>
        methods are now deprecated in favour of
        <javadoc class="uk.ac.starlink.votable.VOSerializer"
         member="makeSerializer(uk.ac.starlink.votable.VOSerializerConfig,
                                uk.ac.starlink.table.StarTable)"
           >makeSerializer(VOSerializerConfig,StarTable)</javadoc>.</li>
    <li>Fix bug when writing long HIERARCH FITS headers
        that could cause output failure.</li>
    <li>Add new <code>delimiter</code> config option to
        <ref id="inCsv">CSV input</ref> and
        <ref id="outCsv">CSV output</ref> handlers.</li>
    <li><javadoc class="uk.ac.starlink.table.formats.CsvTableBuilder"
                 >CSV</javadoc> and
        <javadoc class="uk.ac.starlink.table.formats.AsciiTableBuilder"
                 >ASCII</javadoc> input handlers
        now have a
        <javadoc
        class="uk.ac.starlink.table.formats.RowEvaluatorTableBuilder"
        member="setDecoders(
                uk.ac.starlink.table.formats.RowEvaluator.Decoder[])"/>
        method to control what column types are inferred.</li>
    </ul>
    </p></dd>

<dt id="latest">Version 4.3-4 (12 September 2025)</dt>
<dd><p>
    <ul>
    <li>Fix regression issue (v4.3-3)
        that caused output trouble for tables read from ASCII/CSV files
        including completely blank columns.</li>
    </ul>
    </p></dd>

</dl>
</p>
</subsect>

</sect>

</appendices>

</docbody>
</sun>
