Skip to content
Snippets Groups Projects
Commit 5f00aa79 authored by gmantele's avatar gmantele
Browse files

[TAP] Replace Savot by STIL + Change datatype management in TAPColumn...

[TAP] Replace Savot by STIL + Change datatype management in TAPColumn (vot-type is no more available from here), TAPType (no votable type inside it) and VotType (can now be created from a TAPType instance + a VotType instance can be converted easily in a TAPType object)
parent 0e225ed7
Branches
No related tags found
No related merge requests found
Showing
with 735 additions and 795 deletions
File moved
File added
File deleted
File deleted
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE project>
<project name="TAPUploaderTest" basedir="." default="jarBuild">
<target name="init">
<property name="srcRep" value="src" />
<property name="buildRep" value="build"/>
<property name="jarFile" value="uploadUtils.jar" />
<property name="jarDest" value=".." />
</target>
<target name="jarBuild" depends="init" >
<echo>Création du jar (${jarDest}/${jarFile})...</echo>
<jar destfile="${jarDest}/${jarFile}">
<zipfileset src="binarySavot.jar" excludes="META-INF/*" />
<zipfileset src="cds.savot.common.jar" excludes="META-INF/*" />
<zipfileset src="cds.savot.model.jar" excludes="META-INF/*" />
<zipfileset src="cds.savot.pull.jar" excludes="META-INF/*" />
<zipfileset src="cds.savot.writer.jar" excludes="META-INF/*" />
<zipfileset src="kxml2-min.jar" excludes="META-INF/*" />
<zipfileset src="cos.jar" excludes="META-INF/*" />
</jar>
</target>
</project>
\ No newline at end of file
File deleted
File deleted
File deleted
File deleted
File deleted
......@@ -21,40 +21,45 @@ package tap.data;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;
import tap.TAPException;
import tap.metadata.TAPColumn;
import tap.metadata.TAPType;
import tap.metadata.VotType;
import tap.metadata.VotType.VotDatatype;
import cds.savot.model.DataBinaryReader;
import cds.savot.model.FieldSet;
import cds.savot.model.SavotBinary;
import cds.savot.model.SavotField;
import cds.savot.model.SavotResource;
import cds.savot.model.SavotTD;
import cds.savot.model.SavotTR;
import cds.savot.model.SavotTableData;
import cds.savot.pull.SavotPullEngine;
import cds.savot.pull.SavotPullParser;
import uk.ac.starlink.table.ColumnInfo;
import uk.ac.starlink.table.DescribedValue;
import uk.ac.starlink.table.OnceRowPipe;
import uk.ac.starlink.table.RowSequence;
import uk.ac.starlink.table.StarTable;
import uk.ac.starlink.table.StarTableFactory;
import uk.ac.starlink.table.TableBuilder;
/**
* <p>{@link TableIterator} which lets iterate over a VOTable input stream using Savot ({@link SavotPullParser} more exactly).</p>
* <p>{@link TableIterator} which lets iterate over a VOTable input stream using STIL.</p>
*
* <p>{@link #getColType()} will return TAP type based on the type declared in the VOTable metadata part.</p>
*
* @author Gr&eacute;gory Mantelet (ARI) - gmantele@ari.uni-heidelberg.de
* @version 2.0 (06/2014)
* @author Gr&eacute;gory Mantelet (ARI)
* @version 2.0 (07/2014)
* @since 2.0
*/
public class VOTableIterator implements TableIterator {
/** Metadata of all columns identified before the iteration. */
private final TAPColumn[] colMeta;
/** Inner TableIterator. It lets iterate over a binary or a table data set in a transparent way. */
private final TableIterator it;
/** Number of columns to read. */
private final int nbColumns;
/** Sequence of rows over which we must iterate. */
private final RowSequence rowSeq;
/** Indicate whether the row iteration has already started. */
private boolean iterationStarted = false;
/** Indicate whether the last row has already been reached. */
private boolean endReached = false;
/** Index of the last read column (=0 just after {@link #nextRow()} and before {@link #nextCol()}, ={@link #nbColumns} after the last column has been read). */
private int colIndex;
/**
* Build a TableIterator able to read rows and columns inside the given VOTable input stream.
......@@ -70,51 +75,66 @@ public class VOTableIterator implements TableIterator {
throw new NullPointerException("Missing VOTable document input stream over which to iterate!");
try{
// Start parsing the VOTable:
SavotPullParser parser = new SavotPullParser(input, SavotPullEngine.SEQUENTIAL, null);
// Get the first resource:
SavotResource resource = parser.getNextResource();
if (resource == null)
throw new DataReadException("Incorrect VOTable format: missing resource node!");
// Extract the metadata about all fields:
FieldSet fields = resource.getFieldSet(0);
colMeta = extractColMeta(fields);
// Build the iterator over the data:
SavotBinary binary = resource.getData(0).getBinary();
if (binary != null)
it = new BinaryVOTableIterator(binary, fields, colMeta);
else
it = new DataVOTableIterator(resource.getData(0).getTableData(), colMeta);
// Set the VOTable builder/interpreter:
TableBuilder tb = (new StarTableFactory()).getTableBuilder("votable");
// Set the TableSink to use in order to stream the data:
OnceRowPipe rowPipe = new OnceRowPipe();
// Initiate the stream process:
tb.streamStarTable(input, rowPipe, null);
// Start by reading just the metadata:
StarTable table = rowPipe.waitForStarTable();
// Convert columns' information into TAPColumn object:
colMeta = extractColMeta(table);
nbColumns = colMeta.length;
// Set the sequence of rows on which this iterator will iterate:
rowSeq = table.getRowSequence();
}catch(TAPException te){
throw new DataReadException("Unexpected field datatype: " + te.getMessage(), te);
}catch(Exception ex){
throw new DataReadException("Unable to parse/read the given VOTable input stream!", ex);
}
}
/**
* Extract an array of {@link TAPColumn} objects. Each corresponds to one of the fields given in parameter,
* Extract an array of {@link TAPColumn} objects. Each corresponds to one of the columns listed in the given table,
* and so corresponds to the metadata of a column.
*
* @param fields List of metadata fields provided in a VOTable.
* @param table {@link StarTable} which contains only the columns' information.
*
* @return The corresponding list of {@link TAPColumn} objects.
*
* @throws TAPException If there is a problem while resolving the field datatype (for instance: unknown datatype, a multi-dimensional array is provided, a bad number format for the arraysize).
*/
private static final TAPColumn[] extractColMeta(final FieldSet fields){
private static final TAPColumn[] extractColMeta(final StarTable table) throws TAPException{
// Count the number columns and initialize the array:
TAPColumn[] columns = new TAPColumn[fields.getItemCount()];
TAPColumn[] columns = new TAPColumn[table.getColumnCount()];
// Add all columns meta:
for(int i = 0; i < fields.getItemCount(); i++){
for(int i = 0; i < columns.length; i++){
// get the field:
SavotField field = (SavotField)fields.getItemAt(i);
ColumnInfo colInfo = table.getColumnInfo(i);
// get the datatype:
String datatype = getAuxDatumValue(colInfo, "Datatype");
// get the arraysize:
String arraysize = ColumnInfo.formatShape(colInfo.getShape());
// get the xtype:
String xtype = getAuxDatumValue(colInfo, "xtype");
// Resolve the field type:
TAPType type = resolveVotType(field.getDataType(), field.getArraySize(), field.getXtype()).toTAPType();
TAPType type = resolveVotType(datatype, arraysize, xtype).toTAPType();
// build the TAPColumn object:
TAPColumn col = new TAPColumn(field.getName(), type, field.getDescription(), field.getUnit(), field.getUcd(), field.getUtype());
TAPColumn col = new TAPColumn(colInfo.getName(), type, colInfo.getDescription(), colInfo.getUnitString(), colInfo.getUCD(), colInfo.getUtype());
col.setPrincipal(false);
col.setIndexed(false);
col.setStd(false);
......@@ -126,6 +146,19 @@ public class VOTableIterator implements TableIterator {
return columns;
}
/**
* Extract the specified auxiliary datum value from the given {@link ColumnInfo}.
*
* @param colInfo {@link ColumnInfo} from which the auxiliary datum must be extracted.
* @param auxDatumName The name of the datum to extract.
*
* @return The extracted value as String.
*/
private static final String getAuxDatumValue(final ColumnInfo colInfo, final String auxDatumName){
DescribedValue value = colInfo.getAuxDatumByName(auxDatumName);
return (value != null) ? value.getValue().toString() : null;
}
/**
* Resolve a VOTable field type by using the datatype, arraysize and xtype strings as specified in a VOTable document.
*
......@@ -134,53 +167,24 @@ public class VOTableIterator implements TableIterator {
* @param xtype Attribute value of VOTable corresponding to the xtype.
*
* @return The resolved VOTable field type, or a CHAR(*) type if the specified type can not be resolved.
*
* @throws TAPException If a field datatype is unknown.
*/
private static VotType resolveVotType(final String datatype, final String arraysize, final String xtype){
private static VotType resolveVotType(final String datatype, final String arraysize, final String xtype) throws TAPException{
// If no datatype is specified, return immediately a CHAR(*) type:
if (datatype == null || datatype.trim().length() == 0)
return new VotType(VotDatatype.CHAR, VotType.NO_SIZE, true);
// 1. IDENTIFY THE DATATYPE:
return new VotType(VotDatatype.CHAR, "*");
// Identify the specified datatype:
VotDatatype votdatatype;
try{
votdatatype = VotDatatype.valueOf(datatype.toUpperCase());
}catch(IllegalArgumentException iae){
// if it can't be identified, return immediately a CHAR(*) type:
return new VotType(VotDatatype.CHAR, VotType.NO_SIZE, true);
throw new TAPException("unknown field datatype: \"" + datatype + "\"");
}
// 2. DETERMINE ITS ARRAYSIZE:
int votarraysize = VotType.NO_SIZE;
boolean votunlimitedSize = false;
// If no arraysize is specified, let's set it to 1 (for an elementary value):
if (arraysize == null || arraysize.trim().isEmpty())
votarraysize = 1;
// Otherwise, get it:
else{
String str = arraysize.trim();
// Determine whether an "unlimited size" character is specified:
votunlimitedSize = str.endsWith("*");
// If one is specified, remove it from the arraysize string:
if (votunlimitedSize)
str = str.substring(0, str.length() - 1);
// If a size is really specified (more characters than "*"), get the arraysize value:
if (str.length() > 0){
try{
votarraysize = Integer.parseInt(str);
}catch(NumberFormatException nfe){}
}
}
// And finally build the VOTable type:
return new VotType(votdatatype, votarraysize, votunlimitedSize, xtype);
// Build the VOTable type:
return new VotType(votdatatype, arraysize, xtype);
}
/**
......@@ -191,11 +195,11 @@ public class VOTableIterator implements TableIterator {
* </ul>
* @throws IllegalStateException
*/
private static void checkReadState(final boolean iterationStarted, final boolean endReached) throws IllegalStateException{
private void checkReadState() throws IllegalStateException{
if (!iterationStarted)
throw new IllegalStateException("No row has yet been read!");
else if (endReached)
throw new IllegalStateException("End of ResultSet already reached!");
throw new IllegalStateException("End of VOTable file already reached!");
}
@Override
......@@ -203,265 +207,57 @@ public class VOTableIterator implements TableIterator {
return colMeta;
}
@Override
public boolean nextRow() throws DataReadException{
return it.nextRow();
}
@Override
public boolean hasNextCol() throws IllegalStateException, DataReadException{
return it.hasNextCol();
}
@Override
public Object nextCol() throws NoSuchElementException, IllegalStateException, DataReadException{
return it.nextCol();
}
@Override
public TAPType getColType() throws IllegalStateException, DataReadException{
return it.getColType();
}
/**
* <p>{@link TableIterator} which lets iterate over a VOTable binary data part.</p>
*
* <p>This {@link TableIterator} is only usable by {@link VOTableIterator}.</p>
*
* @author Gr&eacute;gory Mantelet (ARI) - gmantele@ari.uni-heidelberg.de
* @version 2.0 (Jun 27, 2014)
* @since 2.0
*/
private static class BinaryVOTableIterator implements TableIterator {
/** Binary data reader which lets read rows and columns, and thus iterate over them. */
private final DataBinaryReader reader;
/** Metadata of all columns identified before the iteration. <i>(In this TableIterator, they are completely provided by {@link VOTableIterator}).</i> */
private final TAPColumn[] colMeta;
/** The last read row. Each item is a column value. */
private Object[] row;
/** Indicate whether the row iteration has already started. */
private boolean iterationStarted = false;
/** Indicate whether the last row has already been reached. */
private boolean endReached = false;
/** Index of the last read column (=0 just after {@link #nextRow()} and before {@link #nextCol()}). */
private int colIndex;
/**
* Build a TableIterator on the given binary data part of a VOTable whose fields are also described in parameter.
*
* @param binary Binary data part of a VOTable document.
* @param fields Description of all the fields that should be read.
* @param columnsMeta Metadata information extracted from the VOTable metadata part.
*
* @throws DataReadException If there is an error while starting reading the given binary data.
*/
public BinaryVOTableIterator(final SavotBinary binary, final FieldSet fields, final TAPColumn[] columnsMeta) throws DataReadException{
try{
reader = new DataBinaryReader(binary.getStream(), fields, false);
colMeta = columnsMeta;
}catch(IOException ioe){
throw new DataReadException("Can not open a stream to decode the binary VOTable data!", ioe);
}
}
@Override
public TAPColumn[] getMetadata(){
return null;
}
@Override
public boolean nextRow() throws DataReadException{
try{
// Go to the next row:
boolean rowFetched = reader.next();
// go to the next row:
boolean rowFetched = rowSeq.next();
endReached = !rowFetched;
// prepare the iteration over its columns:
if (rowFetched){
row = reader.getRow();
colIndex = -1;
colIndex = 0;
iterationStarted = true;
}else{
row = null;
colIndex = -1;
endReached = true;
}
return rowFetched;
}catch(IOException e){
throw new DataReadException("Unable to read a VOTable row!", e);
throw new DataReadException("Unable to read the next VOTable row!", e);
}
}
@Override
public boolean hasNextCol() throws IllegalStateException, DataReadException{
// Check the read state:
checkReadState(iterationStarted, endReached);
checkReadState();
// Determine whether the last column has been reached or not:
return (colIndex + 1 < row.length);
return (colIndex < nbColumns);
}
@Override
public Object nextCol() throws NoSuchElementException, IllegalStateException, DataReadException{
// Check the read state and ensure there is still at least one column to read:
if (!hasNextCol())
throw new NoSuchElementException("No more column to read!");
// Get the column value:
return row[++colIndex];
}
@Override
public TAPType getColType() throws IllegalStateException, DataReadException{
// Basically check the read state (for rows iteration):
checkReadState(iterationStarted, endReached);
// Check deeper the read state (for columns iteration):
if (colIndex < 0)
throw new IllegalStateException("No column has yet been read!");
else if (colIndex >= colMeta.length)
return null;
throw new NoSuchElementException("No more field to read!");
// Get the column value:
return colMeta[colIndex].getDatatype();
}
}
/**
* <p>{@link TableIterator} which lets iterate over a VOTable table data part.</p>
*
* <p>This {@link TableIterator} is only usable by {@link VOTableIterator}.</p>
*
* @author Gr&eacute;gory Mantelet (ARI) - gmantele@ari.uni-heidelberg.de
* @version 2.0 (Jun 27, 2014)
* @since 2.0
*/
private static class DataVOTableIterator implements TableIterator {
/** Iterator over the rows contained in the VOTable data part. */
private final Iterator<Object> data;
/** Metadata of all columns identified before the iteration. <i>(In this TableIterator, they are completely provided by {@link VOTableIterator}).</i> */
private final TAPColumn[] colMeta;
/** Iterator over the columns contained in the last read row. */
private Iterator<Object> colsIt;
/** Indicate whether the row iteration has already started. */
private boolean iterationStarted = false;
/** Indicate whether the last row has already been reached. */
private boolean endReached = false;
/** Index of the last read column (=0 just after {@link #nextRow()} and before {@link #nextCol()}). */
private int colIndex;
/**
* Build a TableIterator on the given table data part of a VOTable.
*
* @param dataset Table data part of a VOTable document.
* @param columnsMeta Metadata information extracted from the VOTable metadata part.
*/
public DataVOTableIterator(final SavotTableData dataset, final TAPColumn[] columnsMeta){
Collection<Object> trset = dataset.getTRs().getItems();
if (trset == null){
data = new NullIterator();
colMeta = columnsMeta;
iterationStarted = true;
endReached = true;
}else{
data = trset.iterator();
colMeta = columnsMeta;
}
}
@Override
public TAPColumn[] getMetadata(){
return null;
}
@Override
public boolean nextRow() throws DataReadException{
if (data.hasNext()){
// Go to the next row:
SavotTR row = (SavotTR)data.next();
// Prepare the iteration over its columns:
Collection<Object> tdset = row.getTDSet().getItems();
if (tdset == null)
colsIt = new NullIterator();
else
colsIt = tdset.iterator();
colIndex = -1;
iterationStarted = true;
return true;
}else{
// No more row to read => end of VOTable reached:
endReached = true;
return false;
}
}
@Override
public boolean hasNextCol() throws IllegalStateException, DataReadException{
// Check the read state:
checkReadState(iterationStarted, endReached);
// Determine whether the last column has been reached or not:
return colsIt.hasNext();
try{
return rowSeq.getCell(colIndex++);
}catch(IOException se){
throw new DataReadException("Can not read the value of the " + colIndex + "-th field!", se);
}
@Override
public Object nextCol() throws NoSuchElementException, IllegalStateException, DataReadException{
// Check the read state and ensure there is still at least one column to read:
if (!hasNextCol())
throw new NoSuchElementException("No more column to read!");
// Get the column value:
Object value = ((SavotTD)colsIt.next()).getContent();
colIndex++;
return value;
}
@Override
public TAPType getColType() throws IllegalStateException, DataReadException{
// Basically check the read state (for rows iteration):
checkReadState(iterationStarted, endReached);
checkReadState();
// Check deeper the read state (for columns iteration):
if (colIndex < 0)
throw new IllegalStateException("No column has yet been read!");
else if (colIndex >= colMeta.length)
return null;
// Get the column value:
return colMeta[colIndex].getDatatype();
}
}
/**
* Iterator over nothing.
*
* @author Gr&eacute;gory Mantelet (ARI) - gmantele@ari.uni-heidelberg.de
* @version 2.0 (06/2014)
* @version 2.0
*/
private final static class NullIterator implements Iterator<Object> {
@Override
public boolean hasNext(){
return false;
}
@Override
public Object next(){
return null;
}
@Override
public void remove(){}
if (colIndex <= 0)
throw new IllegalStateException("No field has yet been read!");
else if (colIndex > nbColumns)
throw new IllegalStateException("All fields have already been read!");
// Return the column type:
return colMeta[colIndex - 1].getDatatype();
}
}
......@@ -106,7 +106,7 @@ public class JSONFormat implements OutputFormat {
// {
out.object();
// "metadata": {...}
// "metadata": [...]
out.key("metadata");
// Write metadata part:
......@@ -114,7 +114,7 @@ public class JSONFormat implements OutputFormat {
writer.flush();
// "data": {...}
// "data": [...]
out.key("data");
// Write the data part:
......@@ -229,16 +229,11 @@ public class JSONFormat implements OutputFormat {
out.key("description").value(tapCol.getDescription());
// "datatype": "..."
VotType votType = tapCol.getVotType();
VotType votType = tapCol.getDatatype().toVotType();
out.key("datatype").value(votType.datatype);
// "arraysize": "..." (if any)
if (votType.unlimitedArraysize){
if (votType.arraysize > 0)
out.key("arraysize").value(votType.arraysize + "*");
else
out.key("arraysize").value("*");
}else if (votType.arraysize > 0)
if (votType.arraysize != null)
out.key("arraysize").value(votType.arraysize);
// "xtype": "..." (if any)
......
......@@ -64,7 +64,9 @@ public interface OutputFormat {
public String getFileExtension();
/**
* Formats the given query result and writes it in the given output stream.
* <p>Formats the given query result and writes it in the given output stream.</p>
*
* <p><i>Note: the given output stream should not be closed at the end of this function. It is up to the called to do it.</i></p>
*
* @param result The raw (table) result to format.
* @param output The output stream (a ServletOutputStream or a stream on a file) in which the formatted result must be written.
......
This diff is collapsed.
......@@ -44,8 +44,6 @@ public class TAPColumn implements DBColumn {
private TAPType datatype = new TAPType(TAPDatatype.VARCHAR);
private VotType votType = datatype.toVotType();
private boolean principal = false;
private boolean indexed = false;
......@@ -194,14 +192,6 @@ public class TAPColumn implements DBColumn {
*/
public final void setDatatype(final TAPType type){
datatype = type;
votType = datatype.toVotType();
}
/**
* @return The votType.
*/
public final VotType getVotType(){
return votType;
}
/**
......
......@@ -19,8 +19,6 @@ package tap.metadata;
* Copyright 2014 - Astronomishes Rechen Institute (ARI)
*/
import tap.metadata.VotType.VotDatatype;
/**
*
* <p>
......@@ -86,10 +84,10 @@ public class TAPType {
*
* @return The corresponding VOTable field type.
*
* @see #convertIntoVotType(TAPType)
* @see VotType#VotType(TAPType)
*/
public VotType toVotType(){
return convertIntoVotType(this);
return new VotType(this);
}
@Override
......@@ -100,158 +98,4 @@ public class TAPType {
return type.toString();
}
/**
* Convert the given TAP column type into a VOTable field type.
*
* @param taptype The TAP column type to convert.
*
* @return The corresponding VOTable field type.
*/
public static VotType convertIntoVotType(final TAPType taptype){
VotType vot = new VotType(VotDatatype.CHAR, VotType.NO_SIZE, false);
switch(taptype.type){
case SMALLINT:
vot = new VotType(VotDatatype.SHORT, 1, false);
break;
case INTEGER:
vot = new VotType(VotDatatype.INT, 1, false);
break;
case BIGINT:
vot = new VotType(VotDatatype.LONG, 1, false);
break;
case REAL:
vot = new VotType(VotDatatype.FLOAT, 1, false);
break;
case DOUBLE:
vot = new VotType(VotDatatype.DOUBLE, 1, false);
break;
case CHAR:
vot = new VotType(VotDatatype.CHAR, (taptype.length > 0 ? taptype.length : 1), false);
break;
case BINARY:
vot = new VotType(VotDatatype.UNSIGNED_BYTE, (taptype.length > 0 ? taptype.length : VotType.NO_SIZE), false);
break;
case VARBINARY:
vot = new VotType(VotDatatype.UNSIGNED_BYTE, (taptype.length > 0 ? taptype.length : VotType.NO_SIZE), (taptype.length > 0));
break;
case BLOB:
vot = new VotType(VotDatatype.UNSIGNED_BYTE, VotType.NO_SIZE, true, VotType.XTYPE_BLOB);
break;
case CLOB:
vot = new VotType(VotDatatype.CHAR, VotType.NO_SIZE, true, VotType.XTYPE_CLOB);
break;
case TIMESTAMP:
vot = new VotType(VotDatatype.CHAR, VotType.NO_SIZE, true, VotType.XTYPE_TIMESTAMP);
break;
case POINT:
vot = new VotType(VotDatatype.CHAR, VotType.NO_SIZE, true, VotType.XTYPE_POINT);
break;
case REGION:
vot = new VotType(VotDatatype.CHAR, VotType.NO_SIZE, true, VotType.XTYPE_REGION);
break;
case VARCHAR:
default:
vot = new VotType(VotDatatype.CHAR, (taptype.length > 0 ? taptype.length : VotType.NO_SIZE), (taptype.length > 0), null);
break;
}
return vot;
}
/**
* Convert the given VOTable field type into a TAP column type.
*
* @param vottype The VOTable field type to convert.
*
* @return The corresponding TAP column type.
*/
public static TAPType convertFromVotType(final VotType vottype){
if (vottype == null)
return new TAPType(TAPDatatype.VARCHAR);
switch(vottype.datatype){
case SHORT:
case BOOLEAN:
if ((vottype.arraysize <= 1 || vottype.arraysize == VotType.NO_SIZE) && !vottype.unlimitedArraysize)
return new TAPType(TAPDatatype.SMALLINT);
else
return new TAPType(TAPDatatype.VARBINARY);
case INT:
if ((vottype.arraysize <= 1 || vottype.arraysize == VotType.NO_SIZE) && !vottype.unlimitedArraysize)
return new TAPType(TAPDatatype.INTEGER);
else
return new TAPType(TAPDatatype.VARBINARY);
case LONG:
if ((vottype.arraysize <= 1 || vottype.arraysize == VotType.NO_SIZE) && !vottype.unlimitedArraysize)
return new TAPType(TAPDatatype.BIGINT);
else
return new TAPType(TAPDatatype.VARBINARY);
case FLOAT:
if ((vottype.arraysize <= 1 || vottype.arraysize == VotType.NO_SIZE) && !vottype.unlimitedArraysize)
return new TAPType(TAPDatatype.REAL);
else
return new TAPType(TAPDatatype.VARBINARY);
case DOUBLE:
if ((vottype.arraysize <= 1 || vottype.arraysize == VotType.NO_SIZE) && !vottype.unlimitedArraysize)
return new TAPType(TAPDatatype.DOUBLE);
else
return new TAPType(TAPDatatype.VARBINARY);
case UNSIGNED_BYTE:
if (vottype.arraysize > 0){
if (vottype.unlimitedArraysize)
return new TAPType(TAPDatatype.VARBINARY, vottype.arraysize);
else
return new TAPType(TAPDatatype.BINARY, vottype.arraysize);
}else
return new TAPType(TAPDatatype.VARBINARY);
case CHAR:
default:
TAPType taptype = null;
if (vottype.xtype != null && vottype.xtype.trim().length() > 0){
if (vottype.xtype.equalsIgnoreCase(VotType.XTYPE_BLOB))
taptype = new TAPType(TAPDatatype.BLOB);
else if (vottype.xtype.equalsIgnoreCase(VotType.XTYPE_CLOB))
taptype = new TAPType(TAPDatatype.CLOB);
else if (vottype.xtype.equalsIgnoreCase(VotType.XTYPE_TIMESTAMP))
taptype = new TAPType(TAPDatatype.TIMESTAMP);
else if (vottype.xtype.equalsIgnoreCase(VotType.XTYPE_POINT))
taptype = new TAPType(TAPDatatype.POINT);
else if (vottype.xtype.equalsIgnoreCase(VotType.XTYPE_REGION))
taptype = new TAPType(TAPDatatype.REGION);
}
if (taptype == null){
if (vottype.unlimitedArraysize)
taptype = new TAPType(TAPDatatype.VARCHAR, (vottype.arraysize > 0) ? vottype.arraysize : NO_LENGTH);
else{
if (vottype.arraysize <= 0 || vottype.arraysize == VotType.NO_SIZE)
taptype = new TAPType(TAPDatatype.VARCHAR);
else if (vottype.arraysize == 1)
taptype = new TAPType(TAPDatatype.CHAR, 1);
else
taptype = new TAPType(TAPDatatype.CHAR, vottype.arraysize);
}
}
return taptype;
}
}
}
......@@ -20,7 +20,9 @@ package tap.metadata;
* Astronomishes Rechen Institute (ARI)
*/
import cds.savot.writer.SavotWriter;
import tap.TAPException;
import tap.metadata.TAPType.TAPDatatype;
import uk.ac.starlink.votable.VOSerializer;
/**
* <p>Describes a full VOTable type. Thus it includes the following field attributes:</p>
......@@ -72,18 +74,11 @@ public final class VotType {
* @since 2.0 */
public final static String XTYPE_REGION = "adql:REGION";
/** No array size.
* @since 2.0 */
public static final int NO_SIZE = -1;
/** VOTable datatype
* @since 2.0 */
public final VotDatatype datatype;
/** A negative or null value means "*" (that's to say: an undetermined arraysize). */
public final int arraysize;
/** If true, it means either "n*" (where n is the arraysize when > 0) or "*".
* @since 2.0*/
public final boolean unlimitedArraysize;
/** Arraysize string of a VOTable field element. */
public final String arraysize;
/** Special type specification (i.e. POINT, TIMESTAMP, ...). */
public final String xtype;
......@@ -91,28 +86,134 @@ public final class VotType {
* Build a VOTable field type.
*
* @param datatype A datatype. <b>Null value forbidden</b>
* @param arraysize A non-null positive integer. (any value &le; 0 will be considered as an undetermined arraysize, that's to say {@link #NO_SIZE}).
* @param unlimitedSize Indicate whether a * must be appended at the end of the arraysize attribute (so in these 2 cases: "n*" or "*").
* @param arraysize VOTable arraysize string (<i>may be NULL</i>).
*/
public VotType(final VotDatatype datatype, final int arraysize, final boolean unlimitedSize){
this(datatype, arraysize, unlimitedSize, null);
public VotType(final VotDatatype datatype, final String arraysize){
this(datatype, arraysize, null);
}
/**
* Build a VOTable field type.
*
* @param datatype A datatype. <b>Null value forbidden</b>
* @param arraysize A non-null positive integer. (any value &le; 0 will be considered as an undetermined arraysize, that's to say {@link #NO_SIZE}).
* @param unlimitedSize Indicate whether a * must be appended at the end of the arraysize attribute (so in these 2 cases: "n*" or "*").
* @param xtype A special type (ex: adql:POINT, adql:TIMESTAMP, ...). Null value allowed.
* @param arraysize VOTable arraysize string (<i>may be NULL</i>).
* @param xtype A special type (ex: adql:POINT, adql:TIMESTAMP, ...). (<i>may be NULL</i>).
*/
public VotType(final VotDatatype datatype, final int arraysize, final boolean unlimitedSize, final String xtype){
public VotType(final VotDatatype datatype, final String arraysize, final String xtype){
// set the datatype:
if (datatype == null)
throw new NullPointerException("Missing VOTable datatype !");
throw new NullPointerException("missing VOTable datatype !");
else
this.datatype = datatype;
this.arraysize = (arraysize > 0) ? arraysize : NO_SIZE;
this.unlimitedArraysize = unlimitedSize;
this.xtype = xtype;
// set the array-size:
if (arraysize != null && arraysize.trim().length() > 0)
this.arraysize = arraysize.trim();
else
this.arraysize = null;
// set the xtype:
if (xtype != null && xtype.trim().length() > 0)
this.xtype = xtype.trim();
else
this.xtype = null;
}
/**
* Build a {@link VotType} object by converting the given {@link TAPType}.
*
* @param tapType {@link TAPType} to convert.
*/
public VotType(final TAPType tapType){
switch(tapType.type){
case SMALLINT:
this.datatype = VotDatatype.SHORT;
this.arraysize = "1";
this.xtype = null;
break;
case INTEGER:
this.datatype = VotDatatype.INT;
this.arraysize = "1";
this.xtype = null;
break;
case BIGINT:
this.datatype = VotDatatype.LONG;
this.arraysize = "1";
this.xtype = null;
break;
case REAL:
this.datatype = VotDatatype.FLOAT;
this.arraysize = "1";
this.xtype = null;
break;
case DOUBLE:
this.datatype = VotDatatype.DOUBLE;
this.arraysize = "1";
this.xtype = null;
break;
case CHAR:
this.datatype = VotDatatype.CHAR;
this.arraysize = Integer.toString(tapType.length > 0 ? tapType.length : 1);
this.xtype = null;
break;
case BINARY:
this.datatype = VotDatatype.UNSIGNED_BYTE;
this.arraysize = Integer.toString(tapType.length > 0 ? tapType.length : 1);
this.xtype = null;
break;
case VARBINARY:
/* TODO HOW TO MANAGE VALUES WHICH WHERE ORIGINALLY NUMERIC ARRAYS ?
* (cf the IVOA document TAP#Upload: votable numeric arrays should be converted into VARBINARY...no more array information and particularly the datatype)
*/
this.datatype = VotDatatype.UNSIGNED_BYTE;
this.arraysize = (tapType.length > 0 ? tapType.length + "*" : "*");
this.xtype = null;
break;
case BLOB:
this.datatype = VotDatatype.UNSIGNED_BYTE;
this.arraysize = "*";
this.xtype = VotType.XTYPE_BLOB;
break;
case CLOB:
this.datatype = VotDatatype.CHAR;
this.arraysize = "*";
this.xtype = VotType.XTYPE_CLOB;
break;
case TIMESTAMP:
this.datatype = VotDatatype.CHAR;
this.arraysize = "*";
this.xtype = VotType.XTYPE_TIMESTAMP;
break;
case POINT:
this.datatype = VotDatatype.CHAR;
this.arraysize = "*";
this.xtype = VotType.XTYPE_POINT;
break;
case REGION:
this.datatype = VotDatatype.CHAR;
this.arraysize = "*";
this.xtype = VotType.XTYPE_REGION;
break;
case VARCHAR:
default:
this.datatype = VotDatatype.CHAR;
this.arraysize = (tapType.length > 0 ? tapType.length + "*" : "*");
this.xtype = null;
break;
}
}
@Override
......@@ -134,19 +235,14 @@ public final class VotType {
@Override
public String toString(){
StringBuffer str = new StringBuffer("datatype=\"");
str.append(datatype).append('"');
StringBuffer str = new StringBuffer(VOSerializer.formatAttribute("datatype", datatype.toString()));
str.deleteCharAt(0);
if (arraysize > 0){
str.append(" arraysize=\"").append(SavotWriter.encodeAttribute("" + arraysize));
if (unlimitedArraysize)
str.append("*");
str.append('"');
}else if (unlimitedArraysize)
str.append(" arraysize=\"*\"");
if (arraysize != null)
str.append(VOSerializer.formatAttribute("arraysize", arraysize));
if (xtype != null)
str.append(" xtype=\"").append(SavotWriter.encodeAttribute(xtype)).append('"');
str.append(VOSerializer.formatAttribute("xtype", xtype));
return str.toString();
}
......@@ -155,9 +251,126 @@ public final class VotType {
* Convert this VOTable type definition into a TAPColumn type.
*
* @return The corresponding {@link TAPType}.
*
* @throws TAPException If the conversion is impossible (particularly if the array-size refers to a multi-dimensional array ; only 1D arrays are allowed).
*/
public TAPType toTAPType() throws TAPException{
/* Stop immediately if the arraysize refers to a multi-dimensional array:
* (Note: 'x' is the dimension separator of the VOTable attribute 'arraysize') */
if (arraysize != null && arraysize.indexOf('x') >= 0)
throw new TAPException("failed conversion of a VOTable datatype: multi-dimensional arrays (" + datatype + "[" + arraysize + "]) are not allowed!");
// Convert the VOTable datatype into TAP datatype:
switch(datatype){
/* NUMERIC TYPES */
case SHORT:
case BOOLEAN:
return convertNumericType(TAPDatatype.SMALLINT);
case INT:
return convertNumericType(TAPDatatype.INTEGER);
case LONG:
return convertNumericType(TAPDatatype.BIGINT);
case FLOAT:
return convertNumericType(TAPDatatype.REAL);
case DOUBLE:
return convertNumericType(TAPDatatype.DOUBLE);
/* BINARY TYPES */
case UNSIGNED_BYTE:
// BLOB exception:
if (xtype != null && xtype.equalsIgnoreCase(XTYPE_BLOB))
return new TAPType(TAPDatatype.BLOB);
// Or else, just (var)binary:
else
return convertVariableLengthType(TAPDatatype.VARBINARY, TAPDatatype.BINARY);
/* CHARACTER TYPES */
case CHAR:
default:
/* Special type cases: */
if (xtype != null){
if (xtype.equalsIgnoreCase(VotType.XTYPE_CLOB))
return new TAPType(TAPDatatype.CLOB);
else if (xtype.equalsIgnoreCase(VotType.XTYPE_TIMESTAMP))
return new TAPType(TAPDatatype.TIMESTAMP);
else if (xtype.equalsIgnoreCase(VotType.XTYPE_POINT))
return new TAPType(TAPDatatype.POINT);
else if (xtype.equalsIgnoreCase(VotType.XTYPE_REGION))
return new TAPType(TAPDatatype.REGION);
}
// Or if not known or missing, just a (var)char:
return convertVariableLengthType(TAPDatatype.VARCHAR, TAPDatatype.CHAR);
}
}
/**
* <p>Convert this numeric {@link VotType} object into a corresponding {@link TAPType} whose the datatype is provided in parameter.</p>
*
* <p>
* Thus, just the arraysize must be managed here. If there is no arraysize or if equals to '1', the given datatype will be used.
* Otherwise, it is ignored and a {@link TAPType} with VARBINARY is returned.
* </p>
*
* @param tapDatatype TAP datatype corresponding to this {@link VotType} (only when arraysize != '*' and 'n').
*
* @return The corresponding {@link TAPType}.
*/
protected TAPType convertNumericType(final TAPDatatype tapDatatype){
// If no arraysize:
if (arraysize == null || arraysize.equals("1"))
return new TAPType(tapDatatype);
// If only one dimension:
else
return new TAPType(TAPDatatype.VARBINARY);
/* Note: The test of multi-dimensional array should have been already done at the beginning of #toTAPType(). */
}
/**
* <p>
* Convert this variable length {@link VotType} (unsignedByte and char) object into a corresponding {@link TAPType}
* whose the variable length and fixed length versions are given in parameters.
* </p>
*
* <p>Thus, just the arraysize must be managed here. The following cases are taken into account:</p>
* <ul>
* <li><i>No arraysize or '*'</i>: variable length type (i.e. VARCHAR, VARBINARY),</li>
* <li><i>'n*'</i>: variable length type with the maximal length (i.e. VARCHAR(n), VARBINARY(n)),</li>
* <li><i>'n'</i>: fixed length type with the exact length (i.e. CHAR(n), BINARY(n)).</li>
* </ul>
*
* @param varType Variable length type (i.e. VARCHAR, VARBINARY).
* @param fixedType Fixed length type (i.e. CHAR, BINARY).
*
* @return The corresponding {@link TAPType}.
*
* @throws TAPException If the arraysize is not valid (that's to say, different from the following syntaxes: NULL, '*', 'n' or 'n*' (where n is a positive and not-null integer)).
*/
public TAPType toTAPType(){
return TAPType.convertFromVotType(this);
protected TAPType convertVariableLengthType(final TAPDatatype varType, final TAPDatatype fixedType) throws TAPException{
try{
// no arraysize or '*' => VARCHAR or VARBINARY
if (arraysize == null || arraysize.equals("*"))
return new TAPType(varType);
// 'n*' => VARCHAR(n) or VARBINARY(n)
else if (arraysize.charAt(arraysize.length() - 1) == '*')
return new TAPType(varType, Integer.parseInt(arraysize.substring(0, arraysize.length() - 1)));
// 'n' => CHAR(n) or BINARY(n)
else
return new TAPType(fixedType, Integer.parseInt(arraysize));
}catch(NumberFormatException nfe){
throw new TAPException("failed conversion of a VOTable datatype: non-numeric arraysize (" + arraysize + ")!");
}
}
}
package tap.formatter;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import java.io.BufferedOutputStream;
......@@ -34,6 +34,7 @@ import tap.metadata.TAPType;
import tap.metadata.TAPType.TAPDatatype;
import tap.parameters.TAPParameters;
import testtools.DBTools;
import uk.ac.starlink.votable.DataFormat;
import uws.service.UserIdentifier;
/**
......@@ -85,17 +86,17 @@ public class VOTableFormatTest {
TableIterator it = new ResultSetTableIterator(rs);
VOTableFormat formatter = new VOTableFormat(serviceConn);
VOTableFormat formatter = new VOTableFormat(serviceConn, DataFormat.FITS);
OutputStream output = new BufferedOutputStream(new FileOutputStream(votableFile));
formatter.writeResult(it, output, report, Thread.currentThread());
output.close();
// note: due to the pipe (|), we must call /bin/sh as a command whose the command to execute in is the "grep ... | wc -l":
String[] cmd = new String[]{"/bin/sh","-c","grep \"<TR>\" \"" + votableFile.getAbsolutePath() + "\" | wc -l"};
assertTrue(executeCommand(cmd).trim().equals("10"));
assertEquals("10", executeCommand(cmd).trim());
cmd = new String[]{"/bin/sh","-c","grep \"<INFO name=\\\"QUERY_STATUS\\\" value=\\\"OVERFLOW\\\"/>\" \"" + votableFile.getAbsolutePath() + "\" | wc -l"};
assertTrue(executeCommand(cmd).trim().equals("0"));
assertEquals("0", executeCommand(cmd).trim());
}catch(Exception t){
t.printStackTrace();
......@@ -117,17 +118,17 @@ public class VOTableFormatTest {
TableIterator it = new ResultSetTableIterator(rs);
VOTableFormat formatter = new VOTableFormat(serviceConn);
VOTableFormat formatter = new VOTableFormat(serviceConn, DataFormat.FITS);
OutputStream output = new BufferedOutputStream(new FileOutputStream(votableFile));
formatter.writeResult(it, output, report, Thread.currentThread());
output.close();
// note: due to the pipe (|), we must call /bin/sh as a command whose the command to execute in is the "grep ... | wc -l":
String[] cmd = new String[]{"/bin/sh","-c","grep \"<TR>\" \"" + votableFile.getAbsolutePath() + "\" | wc -l"};
assertTrue(executeCommand(cmd).trim().equals("5"));
assertEquals("5", executeCommand(cmd).trim());
cmd = new String[]{"/bin/sh","-c","grep \"<INFO name=\\\"QUERY_STATUS\\\" value=\\\"OVERFLOW\\\"/>\" \"" + votableFile.getAbsolutePath() + "\" | wc -l"};
assertTrue(executeCommand(cmd).trim().equals("1"));
assertEquals("1", executeCommand(cmd).trim());
}catch(Exception t){
t.printStackTrace();
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment