Commit 3f59de36 authored by gmantele's avatar gmantele
Browse files

[TAP] Add missing formats (particularly all the different VOTable...

[TAP] Add missing formats (particularly all the different VOTable serializations) in the TAP configuration file
parent 22eca328
Loading
Loading
Loading
Loading
+141 −5
Original line number Diff line number Diff line
@@ -29,14 +29,20 @@ import static tap.config.TAPConfiguration.KEY_UDFS;
import static tap.config.TAPConfiguration.KEY_UPLOAD_ENABLED;
import static tap.config.TAPConfiguration.KEY_UPLOAD_MAX_FILE_SIZE;
import static tap.config.TAPConfiguration.KEY_USER_IDENTIFIER;
import static tap.config.TAPConfiguration.VALUE_ALL;
import static tap.config.TAPConfiguration.VALUE_ANY;
import static tap.config.TAPConfiguration.VALUE_CSV;
import static tap.config.TAPConfiguration.VALUE_DB;
import static tap.config.TAPConfiguration.VALUE_FITS;
import static tap.config.TAPConfiguration.VALUE_HTML;
import static tap.config.TAPConfiguration.VALUE_JSON;
import static tap.config.TAPConfiguration.VALUE_LOCAL;
import static tap.config.TAPConfiguration.VALUE_NONE;
import static tap.config.TAPConfiguration.VALUE_SV;
import static tap.config.TAPConfiguration.VALUE_TEXT;
import static tap.config.TAPConfiguration.VALUE_TSV;
import static tap.config.TAPConfiguration.VALUE_VOT;
import static tap.config.TAPConfiguration.VALUE_VOTABLE;
import static tap.config.TAPConfiguration.VALUE_XML;
import static tap.config.TAPConfiguration.fetchClass;
import static tap.config.TAPConfiguration.getProperty;
@@ -54,14 +60,19 @@ import tap.ServiceConnection;
import tap.TAPException;
import tap.TAPFactory;
import tap.db.DBConnection;
import tap.formatter.FITSFormat;
import tap.formatter.HTMLFormat;
import tap.formatter.JSONFormat;
import tap.formatter.OutputFormat;
import tap.formatter.SVFormat;
import tap.formatter.TextFormat;
import tap.formatter.VOTableFormat;
import tap.log.DefaultTAPLog;
import tap.log.TAPLog;
import tap.metadata.TAPMetadata;
import tap.metadata.TableSetParser;
import uk.ac.starlink.votable.DataFormat;
import uk.ac.starlink.votable.VOTableVersion;
import uws.UWSException;
import uws.service.UserIdentifier;
import uws.service.file.LocalUWSFileManager;
@@ -131,8 +142,7 @@ public final class ConfigurableServiceConnection implements ServiceConnection {
		// 6. CONFIGURE OUTPUT:
		// default output format = VOTable:
		outputFormats = new ArrayList<OutputFormat>(1);
		outputFormats.add(new VOTableFormat(this));
		// set additional output formats:
		// set output formats:
		addOutputFormats(tapConfig);
		// set output limits:
		initOutputLimits(tapConfig);
@@ -314,14 +324,39 @@ public final class ConfigurableServiceConnection implements ServiceConnection {

	private void addOutputFormats(final Properties tapConfig) throws TAPException{
		// Fetch the value of the property for additional output formats:
		String formats = TAPConfiguration.getProperty(tapConfig, KEY_OUTPUT_FORMATS);
		String formats = getProperty(tapConfig, KEY_OUTPUT_FORMATS);

		// SPECIAL VALUE "ALL":
		if (formats == null || formats.equalsIgnoreCase(VALUE_ALL)){
			outputFormats.add(new VOTableFormat(this, DataFormat.BINARY));
			outputFormats.add(new VOTableFormat(this, DataFormat.BINARY2));
			outputFormats.add(new VOTableFormat(this, DataFormat.TABLEDATA));
			outputFormats.add(new VOTableFormat(this, DataFormat.FITS));
			outputFormats.add(new FITSFormat(this));
			outputFormats.add(new SVFormat(this, ",", true));
			outputFormats.add(new SVFormat(this, "\t", true));
			outputFormats.add(new TextFormat(this));
			outputFormats.add(new HTMLFormat(this));
			return;
		}

		// LIST OF FORMATS:
		// Since it is a comma separated list of output formats, a loop will parse this list comma by comma:
		String f;
		int indexSep;
		int indexSep, indexLPar, indexRPar;
		boolean hasVotableFormat = false;
		while(formats != null && formats.length() > 0){
			// Get a format item from the list:
			indexSep = formats.indexOf(',');
			// if a comma is after a left parenthesis
			indexLPar = formats.indexOf('(');
			if (indexSep > 0 && indexLPar > 0 && indexSep > indexLPar){
				indexRPar = formats.indexOf(')', indexLPar);
				if (indexRPar > 0)
					indexSep = formats.indexOf(',', indexRPar);
				else
					throw new TAPException("Missing right parenthesis in: \"" + formats + "\"!");
			}
			// no comma => only one format
			if (indexSep < 0){
				f = formats;
@@ -339,9 +374,18 @@ public final class ConfigurableServiceConnection implements ServiceConnection {
			}

			// Identify the format and append it to the output format list of the service:
			// FITS
			if (f.equalsIgnoreCase(VALUE_FITS))
				outputFormats.add(new FITSFormat(this));
			// JSON
			if (f.equalsIgnoreCase(VALUE_JSON))
			else if (f.equalsIgnoreCase(VALUE_JSON))
				outputFormats.add(new JSONFormat(this));
			// HTML
			else if (f.equalsIgnoreCase(VALUE_HTML))
				outputFormats.add(new HTMLFormat(this));
			// TEXT
			else if (f.equalsIgnoreCase(VALUE_TEXT))
				outputFormats.add(new TextFormat(this));
			// CSV
			else if (f.equalsIgnoreCase(VALUE_CSV))
				outputFormats.add(new SVFormat(this, ",", true));
@@ -370,6 +414,18 @@ public final class ConfigurableServiceConnection implements ServiceConnection {
				}else
					throw new TAPException("Missing separator char/string for the SV output format: \"" + f + "\"!");
			}
			// VOTABLE
			else if (f.toLowerCase().startsWith(VALUE_VOTABLE) || f.toLowerCase().startsWith(VALUE_VOT)){
				// Parse the format:
				VOTableFormat votFormat = parseVOTableFormat(f);

				// Add the VOTable format:
				outputFormats.add(votFormat);

				// Determine whether the MIME type is the VOTable expected one:
				if (votFormat.getShortMimeType().equals("votable") || votFormat.getMimeType().equals("votable"))
					hasVotableFormat = true;
			}
			// custom OutputFormat
			else if (isClassPath(f)){
				Class<? extends OutputFormat> userOutputFormatClass = fetchClass(f, KEY_OUTPUT_FORMATS, OutputFormat.class);
@@ -387,6 +443,86 @@ public final class ConfigurableServiceConnection implements ServiceConnection {
			else
				throw new TAPException("Unknown output format: " + f);
		}

		// Add by default VOTable format if none is specified:
		if (!hasVotableFormat)
			outputFormats.add(new VOTableFormat(this));
	}

	private VOTableFormat parseVOTableFormat(final String propValue) throws TAPException{
		DataFormat serialization = null;
		VOTableVersion votVersion = null;
		String mimeType = null, shortMimeType = null;

		// Get the parameters, if any:
		int beginSep = propValue.indexOf('(');
		if (beginSep > 0){
			int endSep = propValue.indexOf(')');
			if (endSep <= beginSep)
				throw new TAPException("Wrong output format specification syntax in: \"" + propValue + "\"! A VOTable parameters list must end with ')'.");
			// split the parameters:
			String[] params = propValue.substring(beginSep + 1, endSep).split(",");
			if (params.length > 2)
				throw new TAPException("Wrong number of parameters for the output format VOTable: \"" + propValue + "\"! Only two parameters may be provided: serialization and version.");
			else if (params.length >= 1){
				// resolve the serialization format:
				params[0] = params[0].trim().toLowerCase();
				if (params[0].length() == 0 || params[0].equals("b") || params[0].equals("binary"))
					serialization = DataFormat.BINARY;
				else if (params[0].equals("b2") || params[0].equals("binary2"))
					serialization = DataFormat.BINARY2;
				else if (params[0].equals("td") || params[0].equals("tabledata"))
					serialization = DataFormat.TABLEDATA;
				else if (params[0].equals("fits"))
					serialization = DataFormat.FITS;
				else
					throw new TAPException("Unsupported VOTable serialization: \"" + params[0] + "\"! Accepted values: 'binary' (or 'b'), 'binary2' (or 'b2'), 'tabledata' (or 'td') and 'fits'.");
				// resolve the version:
				if (params.length == 2){
					params[1] = params[1].trim();
					if (params[1].equals("1.0") || params[1].equalsIgnoreCase("v1.0"))
						votVersion = VOTableVersion.V10;
					else if (params[1].equals("1.1") || params[1].equalsIgnoreCase("v1.1"))
						votVersion = VOTableVersion.V11;
					else if (params[1].equals("1.2") || params[1].equalsIgnoreCase("v1.2"))
						votVersion = VOTableVersion.V12;
					else if (params[1].equals("1.3") || params[1].equalsIgnoreCase("v1.3"))
						votVersion = VOTableVersion.V13;
					else
						throw new TAPException("Unsupported VOTable version: \"" + params[1] + "\"! Accepted values: '1.0' (or 'v1.0'), '1.1' (or 'v1.1'), '1.2' (or 'v1.2') and '1.3' (or 'v1.3').");
				}
			}
		}

		// Get the MIME type and its alias, if any:
		beginSep = propValue.indexOf(':');
		if (beginSep > 0){
			int endSep = propValue.indexOf(':', beginSep + 1);
			if (endSep < 0)
				endSep = propValue.length();
			// extract the MIME type, if any:
			mimeType = propValue.substring(beginSep + 1, endSep).trim();
			if (mimeType.length() == 0)
				mimeType = null;
			// extract the short MIME type, if any:
			if (endSep < propValue.length()){
				beginSep = endSep;
				endSep = propValue.indexOf(':', beginSep + 1);
				if (endSep >= 0)
					throw new TAPException("Wrong output format specification syntax in: \"" + propValue + "\"! After a MIME type and a short MIME type, no more information is expected.");
				else
					endSep = propValue.length();
				shortMimeType = propValue.substring(beginSep + 1, endSep).trim();
				if (shortMimeType.length() == 0)
					shortMimeType = null;
			}
		}

		// Create the VOTable format:
		VOTableFormat votFormat = new VOTableFormat(this, serialization, votVersion);
		votFormat.setMimeType(mimeType, shortMimeType);

		return votFormat;
	}

	private void initOutputLimits(final Properties tapConfig) throws TAPException{
+4 −2
Original line number Diff line number Diff line
@@ -23,7 +23,9 @@ import static tap.config.TAPConfiguration.DEFAULT_TAP_CONF_FILE;
import static tap.config.TAPConfiguration.KEY_HOME_PAGE;
import static tap.config.TAPConfiguration.KEY_HOME_PAGE_MIME_TYPE;
import static tap.config.TAPConfiguration.TAP_CONF_PARAMETER;
import static tap.config.TAPConfiguration.fetchClass;
import static tap.config.TAPConfiguration.getProperty;
import static tap.config.TAPConfiguration.isClassPath;

import java.io.File;
import java.io.FileInputStream;
@@ -114,11 +116,11 @@ public class ConfigurableTAPServlet extends HttpServlet {
		String propValue = getProperty(tapConf, KEY_HOME_PAGE);
		if (propValue != null){
			// If it is a class path, replace the current home page by an instance of this class:
			if (TAPConfiguration.isClassPath(propValue)){
			if (isClassPath(propValue)){
				Class<? extends HomePage> newHomePage = null;
				try{
					// ...fetch the class:
					newHomePage = TAPConfiguration.fetchClass(propValue, KEY_HOME_PAGE, HomePage.class);
					newHomePage = fetchClass(propValue, KEY_HOME_PAGE, HomePage.class);
					// ...get its constructor with TAP object:
					Constructor<? extends HomePage> constructor = newHomePage.getConstructor(TAP.class);
					// ...create a new instance and set it as new home page:
+6 −0
Original line number Diff line number Diff line
@@ -87,7 +87,13 @@ public final class TAPConfiguration {

	/* OUTPUT KEYS */
	public final static String KEY_OUTPUT_FORMATS = "output_add_formats";
	public final static String VALUE_ALL = "ALL";
	public final static String VALUE_VOTABLE = "votable";
	public final static String VALUE_VOT = "vot";
	public final static String VALUE_FITS = "fits";
	public final static String VALUE_JSON = "json";
	public final static String VALUE_HTML = "html";
	public final static String VALUE_TEXT = "text";
	public final static String VALUE_CSV = "csv";
	public final static String VALUE_TSV = "tsv";
	public final static String VALUE_SV = "sv";
+28 −20
Original line number Diff line number Diff line
@@ -113,6 +113,7 @@
						<li><u>a URL</u>: here, a redirection toward this URL will be made at each request on the home page</li>
						<li><u>a classpath</u>: the classpath of an extension of tap.resource.HomePage which must replace the default home page resource. This class MUST have at least one constructor with exactly one parameter not NULL of type tap.resource.TAP.</li>
					</ul>
					<p><em>By default, the default home page provided by the library is used.</em></p>
				</td>
				<td><ul><li>my_tap_homepage.jsp</li><li>jsp/my_tap_homepage.jsp</li><li>file:///home/foo/customHomePage.html</li><li>http://...</li><li>{aPackage.NewHomePage}</li></ul></td>
			</tr>
@@ -124,7 +125,7 @@
					<p>MIME type of the service home page.</p>
					<p>This property is used only if the specified "home_page" is a local file path (i.e. if "home_page=file://...").</p>
					<p>If no value is provided "text/html" will be set by default.</p>
					<p><i>Default: <code>text/html</code></i></p>
					<p><em>Default: <code>text/html</code></em></p>
				</td>
				<td><ul><li>text/html <em>(default)</em></li><li>text/plain</li><li>application/xml</li></ul></td>
			</tr>
@@ -229,7 +230,7 @@
				<td>
					<p><strong>Mandatory if</strong> the password is not already provided in jdbc_url</p>
					<p>Password used by db_username to access to the database.</p>
					<p><em><u>Note:</u> No password encryption can be done in this configuration file for the moment.</em></p>
					<p><em><u>Warning:</u> No password encryption can be done in this configuration file for the moment.</em></p>
				</td>
				<td></td>
			</tr>
@@ -254,7 +255,7 @@
				<td></td>
				<td>text</td>
				<td>
					<p><b>Mandatory if</b> the value of "metadata" is "xml".</p>
					<p><strong>Mandatory if</strong> the value of "metadata" is "xml".</p>
					<p>Local file path to the TableSet XML document. This XML must implement the schema TableSet defined by <a href="http://www.ivoa.net/xml/VODataService/v1.1">VODataService</a>.</p>
				</td>
				<td>/home/foo/my_metadata.xml</td>
@@ -286,7 +287,7 @@
				<td>
					<p>Tells whether a directory should be created for each user. If yes, the user directory will be named with the user ID. In this directory, there will be error files, job results
					and it may be the backup file of the user.</p>
					<p><em>The default value is: true.</em></p>
					<p><em>Default: <code>true</code></em></p>
				</td>
				<td><ul><li>true <i>(default)</i></li><li>false</li></ul></td>
			</tr>
@@ -296,7 +297,7 @@
				<td>boolean</td>
				<td>
					<p>Tells whether user directories must be grouped. If yes, directories are grouped by the first letter found in the user ID.</p>
					<p><em>The default value is: false.</em></p>
					<p><em>Default: <code>false</code></em></p>
				</td>
				<td><ul><li>true</li><li>false <i>(default)</i></li></ul></td>
			</tr>
@@ -322,7 +323,7 @@
					<p>The maximum period MUST be greater or equals to the default retention period. If this rule is not respected, the default retention period is set immediately
					 to the maximum retention period.</p>
					<p>A negative or null value means there is no restriction over the maximum retention period: the job results will be kept forever. Float values are not allowed.</p>
					<p><em>By default query results are kept forever: max_retention_period=0.</em></p></td>
					<p><em>Default: <code>max_retention_period=0</code> (results kept for ever)</em></p></td>
				<td>604800 <em>(1 week)</em></td>
			</tr>
			
@@ -334,7 +335,7 @@
				<td>
					<p>Frequency at which the UWS service (that's to say, all its users and jobs) must be backuped.</p>
					<p>Allowed values are: never (no backup will never be done), user_action (each time a user does a writing action, like creating or execution a job), a time (must be positive and not null) in milliseconds.</p>
					<p><em>By default, no backup is done, so: backup_frequency=never.</em></p>
					<p><em>Default: <code>backup_frequency=never</code> (no backup)</em></p>
				</td>
				<td><ul><li>never <em>(default)</em></li><li>user_action</li><li>3600000 <em>(1 hour)</em></li></ul></td>
			</tr>
@@ -345,7 +346,7 @@
				<td>
					<p>Tells whether the backup must be one file for every user, or one file for each user. This second option should be chosen if your TAP Service is organizing its files by user directories ; see the property <em>directory_per_user</em>.</p>
					<p>Allowed values are: user (one backup file for each user), whole (one file for all users ; may generates a big file).</p>
					<p><em>The default mode is: whole.</em></p>
					<p><em>Default: <code>whole</code></em></p>
				</td>
				<td><ul><li>whole <em>(default)</em></li><li>user</li></ul></td>
			</tr>
@@ -358,7 +359,7 @@
				<td>
					<p>Maximum number of asynchronous jobs that can run simultaneously.</p>
					<p>A negative or null value means there is no restriction over the number of running asynchronous jobs.</p>
					<p><em>By default, there is no restriction: max_async_jobs=0.</em></p>
					<p><em>Default: <code>max_async_jobs=0</code> (no restriction)</em></p>
				</td>
				<td><ul><li>0 <em>(default)</em></li><li>10</li></ul></td>
			</tr>
@@ -373,7 +374,7 @@
					<p>The default duration MUST be less or equals to the maximum execution duration. If this rule is not respected, the default execution duration is set immediately
					 to the maximum execution duration.</p>
					<p>A negative or null value means there is no restriction over the default execution duration: the execution could never end. Float values are not allowed.</p>
					<p><em>By default, there is no restriction: default_execution_duration=0.</em></p>
					<p><em>Default: <code>default_execution_duration=0</code> (no restriction)</em></p>
				</td>
				<td>600000 <em>(10 minutes)</em></td>
			</tr>
@@ -386,7 +387,7 @@
					<p>The maximum duration MUST be greater or equals to the default execution duration. If this rule is not respected, the default execution duration is set immediately
					 to the maximum execution duration.</p>
					<p>A negative or null value means there is no restriction over the maximum execution duration: the execution could never end. Float values are not allowed.</p>
					<p><em>By default, there is no restriction: max_execution_duration=0.</em></p>
					<p><em>Default: <code>max_execution_duration=0</code> (no restriction)</em></p>
				</td>
				<td>3600000 <em>(1 hour)</em></td>
			</tr>
@@ -397,11 +398,18 @@
				<td></td>
				<td>text</td>
				<td>
					<p>Comma separated list of output formats for query results, in addition to the VOTable.</p>
					<p>Allowed values are: json, csv, tsv, sv(<i>&lt;separator&gt;</i>)[:<i>mime_type</i>[:<i>mime_type_alias</i>]], or a path (within brackets: {...}) to a class implementing OutputFormat&lt;ResultSet&gt; and having at least one constructor with only a tap.ServiceConnection&lt;ResultSet&gt; parameter.</p>
					<p><i>Note: if no MIME type or MIME type alias is provided for the sv(...) format, defaults are: </i>text/plain<i> and </i>text<i>. So </i>sv([])<i> is equivalent to </i>sv([]):text/plain:text<i>.</i></p>
					<p>Comma separated list of output formats for query results.</p>
					<p>Allowed values are: votable (or 'vot'), fits, text, csv, tsv, json and html.</p>
					<p>The special value "ALL" will select all formats provided by the library.</p>
					<p>The VOTable format may be more detailed with the following syntax: (<code>serialization</code>,<code>version</code>):<code>mime_type</code>:<code>short_mime_type</code>.
					The MIME type part and the parameters part may be omitted (e.g. votable:application/xml:votable , votable(td,1.3)]).
					Empty string values are allowed for each values (e.g. votable():: , votable(td)::votable).</p>
					<p>It is also possible to define a custom Separated Value format, different from CSV and TSV, thanks to the following syntax: sv(<code>separator</code>):<code>mime_type</code>:<code>short_mime_type</code>.
					On the contrary to the VOTable syntax, the parameter (i.e. separator) MUST BE provided. The MIME type part may be omitted ; then the MIME type will be set by default to text/plain.</p>
					<p>There is finally a last possible value: a classpath to a class implementing OutputFormat and having at least one constructor with exactly one parameter of type tap.ServiceConnection.</p>
					<p><em>Default: <code>ALL</code></em></p>
				</td>
				<td><ul><li>json</li><li>csv</li><li>tsv</li><li>sv(|):text/psv:psv</li><li>sv([])</li><li>{apackage.FooOutputFormat}</li></ul></td>
				<td><ul><li>votable</li><li>vot</li><li>vot(td,1.2)::votable</li><li>json,html ,csv, text</li><li>sv(|):text/psv:psv</li><li>sv([])</li><li>{apackage.FooOutputFormat}</li></ul></td>
			</tr>
			<tr class="optional">
				<td class="done">output_default_limit</td>
@@ -412,7 +420,7 @@
					<p>This limit can be expressed in only one unit: rows.</p>
					<p>A negative or null value means there is no restriction over this limit. Float values are not allowed.</p>
					<p>Obviously this limit MUST be less or equal than output_max_limit.</p>
					<p><em>By default, there is no restriction: output_default_limit=0</em></p>
					<p><em>Default: <code>output_default_limit=0</code> (no restriction)</em></p>
				</td>
				<td><ul><li>0 <em>(default)</em></li><li>20</li><li>20r</li><li>20R</li></ul></td>
			</tr>
@@ -425,7 +433,7 @@
					<p>This limit can be expressed in only one unit: rows.</p>
					<p>A negative or null value means there is no restriction over this limit. Float values are not allowed.</p>
					<p>Obviously this limit MUST be greater or equal than output_default_limit.</p>
					<p><em>By default, there is no restriction: output_max_limit=0</em></p>
					<p><em>Default: <code>output_max_limit=0</code> (no restriction)</em></p>
				</td>
				<td><ul><li>0 <em>(default)</em></li><li>1000</li><li>10000r</li><li>10000R</li></ul></td>
			</tr>
@@ -454,7 +462,7 @@
					<p>A negative or null value means there is no restriction over this limit. Float values are not allowed.</p>
					<p><b>Warning!</b> Obviously this limit MUST be less or equal than upload_max_db_limit, and MUST be of the same type as it.
					If the chosen type is rows, this limit MUST also be strictly less than upload_max_file_size.</p>
					<p><i>By default, there is no restriction: upload_default_db_limit=0</i></p>
					<p><em>Default: <code>upload_default_db_limit=0</code> (no restriction)</em></p>
				</td>
				<td><ul><li>0 <em>(default)</em></li><li>20</li><li>20r</li><li>20R</li><li>200kB</li></ul></td>
			</tr>
@@ -470,7 +478,7 @@
					<p>A negative or null value means there is no restriction over this limit. Float values are not allowed.</p>
					<p><b>Warning!</b> Obviously this limit MUST be greater or equal than upload_default_db_limit, and MUST be of the same type as it.
					If the chosen type is rows, this limit MUST also be strictly less than upload_max_file_size.</p>
					<p><i>By default, there is no restriction: upload_max_db_limit=0</i></p>
					<p><em>Default: <code>upload_max_db_limit=0</code> (no restriction)</em></p>
				</td>
				<td><ul><li>0 <em>(default)</em></li><li>10000</li><li>10000r</li><li>10000R</li><li>1MB</li></ul></td>
			</tr>
@@ -484,7 +492,7 @@
					Here, unit is case sensitive. No other storage unit is allowed.</p>
					<p><b>Warning!</b> When the upload is enabled, there must be a maximum file size. Here, no "unlimited" value is possible ; 0 and any negative value are not allowed.</p>
					<p><b>Warning!</b> In function of the chosen upload_max_db_limit type, upload_max_file_size MUST be greater in order to figure out the file metadata part.</p>
					<p><i>By default, the maximum size is set to its maximum possible value: upload_max_file_size=2147483647B (~2GB)</i></p>
					<p><em>Default: <code>upload_max_file_size=2147483647B</code> (~2GB ; maximum possible value)</em></p>
				</td>
				<td><ul><li>2147483647B <em>(default)</em></li><li>2MB</li></ul></td>
			</tr>
+18 −4
Original line number Diff line number Diff line
@@ -257,11 +257,25 @@ max_execution_duration = 0
##########

# [OPTIONAL]
# Comma separated list of output formats for query results, in addition to the VOTable.
# Comma separated list of output formats for query results.
# 
# Allowed values: json, csv, tsv, sv(<separator>), or a path (within brackets: {...}) to a class implementing OutputFormat<ResultSet>
#                 and having at least one constructor with only a tap.ServiceConnection<ResultSet> parameter.
output_add_formats = 
# Allowed values are: votable (or 'vot'), fits, text, csv, tsv, json and html.
# 
# The special value "ALL" will select all formats provided by the library.
# 
# The VOTable format may be more detailed with the following syntax: (serialization,version):mime_type:short_mime_type.
# The MIME type part and the parameters part may be omitted (e.g. votable:application/xml:votable , votable(td,1.3)]).
# Empty string values are allowed for each values (e.g. votable():: , votable(td)::votable).
# 
# It is also possible to define a custom Separated Value format, different from CSV and TSV, thanks to the following syntax: sv(separator):mime_type:short_mime_type.
# On the contrary to the VOTable syntax, the parameter (i.e. separator) MUST BE provided.
# The MIME type part may be omitted ; then the MIME type will be set by default to text/plain.
# 
# There is finally a last possible value: a classpath to a class implementing OutputFormat and having at least one constructor with exactly one parameter of type
# tap.ServiceConnection.
# 
# Default: ALL
output_add_formats = ALL

# [OPTIONAL]
# Default limit for the result output.
Loading