From acb62b0b60017cb2a0c1c6cf5e308714bfc0d75f Mon Sep 17 00:00:00 2001
From: gmantele <gmantele@ari.uni-heidelberg.de>
Date: Thu, 2 Oct 2014 19:08:15 +0200
Subject: [PATCH] [TAP] Add FITS as output format.

---
 src/tap/formatter/FITSFormat.java    | 125 +++++++++++++++++++++++++++
 src/tap/formatter/VOTableFormat.java |   8 +-
 2 files changed, 129 insertions(+), 4 deletions(-)
 create mode 100644 src/tap/formatter/FITSFormat.java

diff --git a/src/tap/formatter/FITSFormat.java b/src/tap/formatter/FITSFormat.java
new file mode 100644
index 0000000..42bf9fc
--- /dev/null
+++ b/src/tap/formatter/FITSFormat.java
@@ -0,0 +1,125 @@
+package tap.formatter;
+
+/*
+ * This file is part of TAPLibrary.
+ * 
+ * TAPLibrary is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * TAPLibrary is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with TAPLibrary.  If not, see <http://www.gnu.org/licenses/>.
+ * 
+ * Copyright 2014 - Astronomisches Rechen Institut (ARI)
+ */
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import tap.ServiceConnection;
+import tap.TAPException;
+import tap.TAPExecutionReport;
+import tap.data.TableIterator;
+import tap.formatter.VOTableFormat.LimitedStarTable;
+import uk.ac.starlink.fits.FitsTableWriter;
+import uk.ac.starlink.table.ColumnInfo;
+import uk.ac.starlink.table.StarTable;
+import uk.ac.starlink.table.StoragePolicy;
+import uws.service.log.UWSLog.LogLevel;
+
+/**
+ * Format any given query (table) result into FITS.
+ * 
+ * @author Gr&eacute;gory Mantelet (ARI)
+ * @version 2.0 (10/2014)
+ * @since 2.0
+ */
+public class FITSFormat implements OutputFormat {
+
+	/** Indicates whether a format report (start and end date/time) must be printed in the log output.  */
+	private boolean logFormatReport;
+
+	/** The {@link ServiceConnection} to use (for the log and to have some information about the service (particularly: name, description). */
+	protected final ServiceConnection service;
+
+	/**
+	 * Creates a FITS formatter.
+	 * 
+	 * @param service	The service to use (for the log and to have some information about the service (particularly: name, description).
+	 * 
+	 * @throws NullPointerException	If the given service connection is <code>null</code>.
+	 */
+	public FITSFormat(final ServiceConnection service) throws NullPointerException{
+		this(service, true);
+	}
+
+	/**
+	 * Creates a FITS formatter.
+	 * 
+	 * @param service				The service to use (for the log and to have some information about the service (particularly: name, description).
+	 * @param logFormatReport		<code>true</code> to append a format report (start and end date/time) in the log output, <code>false</code> otherwise.
+	 * 
+	 * @throws NullPointerException	If the given service connection is <code>null</code>.
+	 */
+	public FITSFormat(final ServiceConnection service, final boolean logFormatReport) throws NullPointerException{
+		if (service == null)
+			throw new NullPointerException("The given service connection is NULL !");
+
+		this.service = service;
+		this.logFormatReport = logFormatReport;
+	}
+
+	@Override
+	public String getMimeType(){
+		return "application/fits";
+	}
+
+	@Override
+	public String getShortMimeType(){
+		return "fits";
+	}
+
+	@Override
+	public String getDescription(){
+		return null;
+	}
+
+	@Override
+	public String getFileExtension(){
+		return "fits";
+	}
+
+	@Override
+	public void writeResult(TableIterator result, OutputStream output, TAPExecutionReport execReport, Thread thread) throws TAPException, InterruptedException{
+		try{
+			long start = System.currentTimeMillis();
+
+			// Extract the columns' metadata:
+			ColumnInfo[] colInfos = VOTableFormat.toColumnInfos(result, execReport, thread);
+
+			// Turns the result set into a table:
+			LimitedStarTable table = new LimitedStarTable(result, colInfos, execReport.parameters.getMaxRec());
+
+			// Copy the table on disk (or in memory if the table is short):
+			StarTable copyTable = StoragePolicy.PREFER_DISK.copyTable(table);
+
+			/* Format the table in FITS (2 passes are needed for that, hence the copy on disk),
+			 * and write it in the given output stream: */
+			new FitsTableWriter().writeStarTable(copyTable, output);
+
+			output.flush();
+
+			if (logFormatReport)
+				service.getLogger().logTAP(LogLevel.INFO, execReport, "FORMAT", "Result formatted (in FITS ; " + table.getNbReadRows() + " rows ; " + table.getColumnCount() + " columns) in " + (System.currentTimeMillis() - start) + "ms!", null);
+		}catch(IOException ioe){
+			throw new TAPException("Error while writing a query result in FITS!", ioe);
+		}
+	}
+
+}
diff --git a/src/tap/formatter/VOTableFormat.java b/src/tap/formatter/VOTableFormat.java
index 0a34f6a..a1beb23 100644
--- a/src/tap/formatter/VOTableFormat.java
+++ b/src/tap/formatter/VOTableFormat.java
@@ -444,7 +444,7 @@ public class VOTableFormat implements OutputFormat {
 	 * @throws TAPException				If there is any other error.
 	 * @throws InterruptedException		If the given thread has been interrupted.
 	 */
-	protected ColumnInfo[] toColumnInfos(final TableIterator result, final TAPExecutionReport execReport, final Thread thread) throws IOException, TAPException, InterruptedException{
+	public static final ColumnInfo[] toColumnInfos(final TableIterator result, final TAPExecutionReport execReport, final Thread thread) throws IOException, TAPException, InterruptedException{
 		// Get the metadata extracted/guesses from the ADQL query:
 		DBColumn[] columnsFromQuery = execReport.resultingColumns;
 
@@ -486,7 +486,7 @@ public class VOTableFormat implements OutputFormat {
 	 * 
 	 * @return	The most appropriate metadata.
 	 */
-	protected final TAPColumn getValidColMeta(final DBColumn typeFromQuery, final TAPColumn typeFromResult){
+	protected static final TAPColumn getValidColMeta(final DBColumn typeFromQuery, final TAPColumn typeFromResult){
 		if (typeFromQuery != null && typeFromQuery instanceof TAPColumn)
 			return (TAPColumn)typeFromQuery;
 		else if (typeFromResult != null){
@@ -505,7 +505,7 @@ public class VOTableFormat implements OutputFormat {
 	 * 
 	 * @return	The corresponding {@link ColumnInfo}.
 	 */
-	protected ColumnInfo getColumnInfo(final TAPColumn tapCol){
+	protected static final ColumnInfo getColumnInfo(final TAPColumn tapCol){
 		// Get the VOTable type:
 		VotType votType = tapCol.getDatatype().toVotType();
 
@@ -613,7 +613,7 @@ public class VOTableFormat implements OutputFormat {
 	 * @version 2.0 (10/2014)
 	 * @since 2.0
 	 */
-	private static class LimitedStarTable extends AbstractStarTable {
+	public static class LimitedStarTable extends AbstractStarTable {
 
 		/** Number of columns to read. */
 		private final int nbCol;
-- 
GitLab