diff --git a/src/tap/metadata/TAPMetadata.java b/src/tap/metadata/TAPMetadata.java index 01b5d901372a5484b0259d86773975cc119a421f..dfb9a7fa9009db58e787cc2828ea27fd4f47be0e 100644 --- a/src/tap/metadata/TAPMetadata.java +++ b/src/tap/metadata/TAPMetadata.java @@ -24,6 +24,7 @@ import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; @@ -38,7 +39,6 @@ import javax.servlet.http.HttpServletResponse; import adql.db.DBTable; import adql.db.DBType; import adql.db.DBType.DBDatatype; -import tap.db.JDBCConnection; import tap.metadata.TAPTable.TableType; import tap.resource.Capabilities; import tap.resource.TAPResource; @@ -52,18 +52,27 @@ import uws.UWSToolBox; * This list also corresponds to the TAP resource "/tables".</p> * * <p> - * Only schemas are stored in this object. So that's why only schemas can be added and removed - * from this class. However, {@link TAPSchema} objects are listing tables, whose the object - * representation is listing columns. So to add tables, you must first embed them in a schema. + * Only schemas are stored in this object. So that's why only schemas can be + * added and removed from this class. However, {@link TAPSchema} objects are + * listing tables, whose the object representation is listing columns. So to + * add tables, you must first embed them in a schema. * </p> * * <p> - * All metadata have two names: one to use in ADQL queries and the other to use when really querying - * the database. This is very useful to hide the real complexity of the database and propose - * a simpler view of the query-able data. It is particularly useful if a schema does not exist in the - * database but has been added in the TAP schema for more logical separation on the user point of view. - * In a such case, the schema would have an ADQL name but no DB name (NULL value ; which is possible only - * with {@link TAPSchema} objects). + * All metadata have two names: one to use in ADQL queries and the other to use + * when really querying the database. This is very useful to hide the real + * complexity of the database and propose a simpler view of the query-able + * data. It is particularly useful if a schema does not exist in the database + * but has been added in the TAP schema for more logical separation on the user + * point of view. In a such case, the schema would have an ADQL name but no DB + * name (NULL value ; which is possible only with {@link TAPSchema} objects). + * </p> + * + * <p> + * This class lets also detect the ObsCore and RegTAP data models, thanks to + * the functions {@link #getObsCoreTable()} and {@link #getRegTAPSchema()} + * which return resp. the ObsCore table and the RegTAP schema if matching the + * IVOA specification. * </p> * * @author Grégory Mantelet (CDS;ARI) @@ -401,11 +410,12 @@ public class TAPMetadata implements Iterable<TAPSchema>, VOSIResource, TAPResour * This function is case sensitive only on the schema name * (i.e. <code>ivoa</code>) which must be defined in full lower case. * The table name (i.e. <code>ObsCore</code>) will be found whatever - * the case it is written in. + * the case it is written in and whether it is prefixed or not. * </p> * * @return Description of the ObsCore table, - * or <code>NULL</code> if this table is not provided by this TAP service. + * or <code>NULL</code> if this table is not provided by this TAP + * service. * * @since 2.1 */ @@ -413,13 +423,51 @@ public class TAPMetadata implements Iterable<TAPSchema>, VOSIResource, TAPResour TAPSchema ivoaSchema = getSchema("ivoa"); if (ivoaSchema != null){ for(TAPTable t : ivoaSchema){ - if (t.getADQLName().equalsIgnoreCase("obscore")) + if (t.getADQLName().matches("(?i)(ivoa\\.)?obscore")) return t; } } return null; } + /** + * Get the description of the RegTAP schema, if it is defined. + * + * <p> + * This function is case sensitive only on the schema name + * (i.e. <code>rr</code>) which must be defined in full lower case. + * The tables name (e.g. <code>capability</code>) will be searched case + * sensitively as well. However, it does not matter if the table name is + * prefixed or not. + * </p> + * + * @return Description of the RegTAP schema, + * or <code>NULL</code> if RegTAP is not supported in this TAP + * service. + * + * @since 2.1 + */ + public TAPSchema getRegTAPSchema(){ + // Get the RegTAP schema if existing: + TAPSchema rrSchema = getSchema("rr"); + + // If there is one, check its content: + if (rrSchema != null){ + // search for all mandatory tables of RegTAP: + HashSet<String> matchedTables = new HashSet<String>(13); + for(TAPTable t : rrSchema){ + if (t.getADQLName().matches("(rr\\.)?(capability|interface|intf_param|relationship|res_date|res_detail|res_role|res_schema|res_subject|res_table|resource|table_column|validation)")) + matchedTables.add(t.getADQLName()); + } + // if all columns have been found, return the RegTAP schema: + if (matchedTables.size() == 13) + return rrSchema; + } + + // NULL, if no schema rr or if it does match the IVOA specification: + return null; + } + /** * Get the number of all tables contained in this TAP metadata set. * @@ -895,7 +943,7 @@ public class TAPMetadata implements Iterable<TAPSchema>, VOSIResource, TAPResour /** * Get the minimum definition of the table TAP_SCHEMA.coosys as expected by - * the library (see {@link JDBCConnection#getTAPSchema()}. + * the library (see {@link tap.db.JDBCConnection#getTAPSchema()}. * * @return The created definition of TAP_SCHEMA.coosys. * diff --git a/src/tap/resource/TAP.java b/src/tap/resource/TAP.java index e8fa13903ad6f9d88d473533f3cbf6b671c69a65..e4ab1448a5d5aaaf7dbe2557233c3aa5a6185bd1 100644 --- a/src/tap/resource/TAP.java +++ b/src/tap/resource/TAP.java @@ -39,6 +39,7 @@ import tap.error.DefaultTAPErrorWriter; import tap.formatter.OutputFormat; import tap.log.TAPLog; import tap.metadata.TAPMetadata; +import tap.metadata.TAPSchema; import tap.metadata.TAPTable; import uk.ac.starlink.votable.VOSerializer; import uws.UWSException; @@ -55,7 +56,7 @@ import uws.service.log.UWSLog.LogLevel; * <p>At its creation it is creating and configuring the other resources in function of the given description of the TAP service.</p> * * @author Grégory Mantelet (CDS;ARI) - * @version 2.1 (03/2017) + * @version 2.1 (09/2017) */ public class TAP implements VOSIResource { @@ -634,6 +635,7 @@ public class TAP implements VOSIResource { * <p>Currently, only the following DMs are natively supported:</p> * <ul> * <li>Obscore (1.0 and PR-1.1)</li> + * <li>RegTAP (1.0)</li> * </ul> * * <p> @@ -653,7 +655,10 @@ public class TAP implements VOSIResource { * @since 2.1 */ protected void appendDataModels(final StringBuffer xml, final String linePrefix){ + // ObsCore: appendObsCoreDM(xml, linePrefix); + // RegTAP: + appendRegTAPDM(xml, linePrefix); } /** @@ -676,8 +681,8 @@ public class TAP implements VOSIResource { * If not, the Obscore table will be declared as Obscore 1.0. * </p> * - * @param xml The <code>/capabilities</code> in-progress content in which - * Obscore-DM should be declared if found. + * @param xml The <code>/capabilities</code> in-progress content + * in which Obscore-DM should be declared if found. * @param linePrefix Tabulations/Spaces that should prefix all lines * (for human readability). * @@ -714,6 +719,41 @@ public class TAP implements VOSIResource { } } + /** + * <p>Append the RegTAP DM declaration in the given {@link StringBuffer} + * if a schema <code>rr</code> can be found in <code>TAP_SCHEMA</code> + * with all its required tables.</p> + * + * <p> + * This function has no effect if the schema <code>rr</code> or its + * mandatory children tables can not be found. The research is done + * case sensitively by {@link TAPMetadata#getRegTAPSchema()}. + * </p> + * + * <p> + * If there is a valid schema <code>rr</code>, this function + * detects automatically which version of RegTAP is implemented. For the + * moment only one is supported: RegTAP-1.0. + * </p> + * + * @param xml The <code>/capabilities</code> in-progress content + * in which RegTAP-DM should be declared if found. + * @param linePrefix Tabulations/Spaces that should prefix all lines + * (for human readability). + * + * @see TAPMetadata#getRegTAPTable() + * + * @since 2.1 + */ + protected void appendRegTAPDM(final StringBuffer xml, final String linePrefix){ + // Try to get the RegTAP schema definition: + TAPSchema regtap = service.getTAPMetadata().getRegTAPSchema(); + + // If there is one, determine the supported DM version and declare it: + if (regtap != null) + xml.append(linePrefix + "<dataModel ivo-id=\"ivo://ivoa.net/std/RegTAP#1.0\">Registry 1.0</dataModel>\n"); + } + /* ************************************* */ /* MANAGEMENT OF THIS RESOURCE'S CONTENT */ /* ************************************* */