/*
* Copyright [1999-2015] Wellcome Trust Sanger Institute and the EMBL-European Bioinformatics Institute
* Copyright [2016-2017] EMBL-European Bioinformatics Institute
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Copyright (C) 2011 EBI, GRL
*
* This library 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 2.1 of the License, or (at your option) any later version.
*
* This library 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 this library; if not, write to the Free
* Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.ensembl.healthcheck.testcase.variation;
import static org.ensembl.healthcheck.util.CollectionUtils.createLinkedHashSet;
import static org.ensembl.healthcheck.util.DBUtils.getShortDatabaseName;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.ensembl.healthcheck.DatabaseRegistryEntry;
import org.ensembl.healthcheck.DatabaseType;
import org.ensembl.healthcheck.ReportManager;
import org.ensembl.healthcheck.Species;
import org.ensembl.healthcheck.Team;
import org.ensembl.healthcheck.testcase.generic.AbstractCompareSchema;
/**
* Extension of the {@link AbstractCompareSchema} class which brings
* schema comparison to variation schemas. This also contains logic
* for the enforcement of the existence of tables and logic to optionally
* ignore certain missing tables. These are indicated by the methods
* @{link {@link #requiredTables()} and @{link {@link #notRequiredTables()}.
*/
public class CompareVariationSchema extends AbstractCompareSchema {
private Map<Species,Set<String>> nr;
private Map<Species,Set<String>> r;
@Override
public void types() {
addAppliesToType(DatabaseType.VARIATION);
}
@Override
protected void addResponsible() {
setTeamResponsible(Team.VARIATION);
}
/**
* We want to continue check the schema even if we know we are missing
* tables.
*/
@Override
protected boolean skipCheckingIfTablesAreUnequal() {
return false;
}
/**
* All tests are applicable
*/
@Override
protected void addTestTypes() {
Set<TestTypes> types = EnumSet.allOf(TestTypes.class);
getTestTypes().addAll(types);
}
@Override
protected String getDefinitionFileKey() {
return "variation_schema.file";
}
@Override
protected String getMasterSchemaKey() {
return "master.variation_schema";
}
/**
* These are tables which could be defined in the master schema
* but can be missing from the target schema
*/
protected Map<Species,Set<String>> notRequiredTables() {
if(nr == null) {
nr = new HashMap<Species, Set<String>>();
//Uncomment to bring in a table which applies to all species
// nr.put(Species.UNKNOWN, createLinkedHashSet(""));
nr.put(Species.HOMO_SAPIENS, createLinkedHashSet("tmp_sample_genotype_single_bp"));
nr.put(Species.PAN_TROGLODYTES, createLinkedHashSet("tmp_sample_genotype_single_bp"));
}
return nr;
}
/**
* Set of tables which MUST be in the target schema. One would assume that
* they could be missing from the master schema
*/
protected Map<Species,Set<String>> requiredTables() {
if(r == null) {
r = new HashMap<Species, Set<String>>();
r.put(Species.UNKNOWN, createLinkedHashSet("subsnp_map"));
//r.put(Species.MUS_MUSCULUS, createLinkedHashSet("strain_gtype_poly"));
//r.put(Species.RATTUS_NORVEGICUS, createLinkedHashSet("strain_gtype_poly"));
}
return r;
}
/**
* Set of tables which may be in the target schema to aid Mart build.
* They will be missing from the master schema.
*/
protected Map<Species,Set<String>> martTables() {
if(r == null) {
r = new HashMap<Species, Set<String>>();
r.put(Species.UNKNOWN, createLinkedHashSet("MTMP_transcript_variation"));
}
return r;
}
/**
* Override of the test method which makes sure we only test those tables
* which are required by the schema. If a table appears in the list
* specified by {@link #notRequiredTables()}.
*/
@Override
protected boolean compareTable(Connection master,
DatabaseRegistryEntry targetDbre, String table) throws SQLException {
Species species = targetDbre.getSpecies();
Set<String> notRequired = getSets(notRequiredTables(), species);
Set<String> required = getSets(requiredTables(), species);
Set<String> martTables = getSets(martTables(), species);
if(notRequired.contains(table) || required.contains(table) || martTables.contains(table)) {
return true;
}
return super.compareTable(master, targetDbre, table);
}
/**
* Re-implementation of the compare tables in schema method used to
* detect if two schemas are equal to each other. This is due to variation
* specific logic which means that schemas can differ but this is still
* valid.
*
* The logic is the same as variation's original version
*
* @param master The master connection
* @param targetDbre The registry entry for the given target schema
* @param ignoreBackupTables Ignored option
* @param directionFlag Ignored option
*/
@Override
public boolean compareTableEquality(Connection master,
DatabaseRegistryEntry targetDbre, boolean ignoreBackupTables,
int directionFlag) {
boolean result = true;
Connection target = targetDbre.getConnection();
String targetName = getDbNameForMsg(target);
String masterName = getDbNameForMsg(master);
Set<String> targetTables = createLinkedHashSet(getTableNames(target));
Set<String> masterTables = createLinkedHashSet(getTableNames(master));
Species species = targetDbre.getSpecies();
Set<String> notRequired = getSets(notRequiredTables(), species);
Set<String> required = getSets(requiredTables(), species);
//Check that tables that are in the master are in the target & skip those
//in the notRequired list
for(String masterTable: masterTables) {
if(notRequired.contains(masterTable)) {
String msg = String.format("Table `%s` is in the list of 'notRequiredTables()' exceptions for %s. Skipping",
masterTable, species.getAlias());
ReportManager.info(this, target, msg);
continue;
}
if(! targetTables.contains(masterTable)) {
String msg = String.format("Table `%s` exists in `%s` but not in `%s`",
masterTable, masterName, targetName);
ReportManager.problem(this, target, msg);
result = false;
}
}
//Check that tables in the target are in the master & it was a required table
for(String targetTable: targetTables) {
if(! masterTables.contains(targetTable) && ! required.contains(targetTable)) {
String msg = String.format("Table `%s` exists in `%s` but not in `%s`",
targetTable, targetName, masterName);
ReportManager.problem(this, target, msg);
result = false;
}
}
//Finally check for the required tables
for(String table: required) {
if(! targetTables.contains(table)) {
String msg = String.format("Table `%s` does not exist in `%s`",
table, targetName);
ReportManager.problem(this, target, msg);
result = false;
}
}
return result;
}
/**
* Returns a set which represents the tables which apply to all species
* (those who are UNKNOWN) and to the given species.
*/
private Set<String> getSets(Map<Species, Set<String>> input, Species species) {
Set<String> output = createLinkedHashSet();
if(input.containsKey(Species.UNKNOWN)) {
output.addAll(input.get(Species.UNKNOWN));
}
if(input.containsKey(species)) {
output.addAll(input.get(species));
}
return output;
}
}