package org.opencb.opencga.storage.core.variant.adaptors;
import org.opencb.commons.datastore.core.QueryOptions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
/**
* Created on 01/02/17.
*
* @author Jacobo Coll <jacobo167@gmail.com>
*/
public enum VariantField {
ID("id", "ids", "name"),
CHROMOSOME,
START,
END,
REFERENCE,
ALTERNATE,
LENGTH,
TYPE,
HGVS,
STUDIES("studies", "sourceEntries"),
STUDIES_SAMPLES_DATA(STUDIES, "studies.samplesData", "samples", "samplesData"),
STUDIES_FILES(STUDIES, "studies.files", "files"),
STUDIES_STATS(STUDIES, "studies.stats", "studies.cohortStats", "stats", "sourceEntries.stats"),
STUDIES_SECONDARY_ALTERNATES(STUDIES, "studies.secondaryAlternates"),
STUDIES_STUDY_ID(STUDIES, "studies.studyId"),
ANNOTATION("annotation"),
ANNOTATION_ANCESTRAL_ALLELE(ANNOTATION, "annotation.ancestralAllele"),
ANNOTATION_ID(ANNOTATION, "annotation.id"),
ANNOTATION_XREFS(ANNOTATION, "annotation.xrefs"),
ANNOTATION_HGVS(ANNOTATION, "annotation.hgvs"),
ANNOTATION_DISPLAY_CONSEQUENCE_TYPE(ANNOTATION, "annotation.displayConsequenceType"),
ANNOTATION_CONSEQUENCE_TYPES(ANNOTATION, "annotation.consequenceTypes"),
ANNOTATION_POPULATION_FREQUENCIES(ANNOTATION, "annotation.populationFrequencies"),
ANNOTATION_MINOR_ALLELE(ANNOTATION, "annotation.minorAllele"),
ANNOTATION_MINOR_ALLELE_FREQ(ANNOTATION, "annotation.minorAlleleFreq"),
ANNOTATION_CONSERVATION(ANNOTATION, "annotation.conservation"),
ANNOTATION_GENE_EXPRESSION(ANNOTATION, "annotation.geneExpression"),
ANNOTATION_GENE_TRAIT_ASSOCIATION(ANNOTATION, "annotation.geneTraitAssociation"),
ANNOTATION_GENE_DRUG_INTERACTION(ANNOTATION, "annotation.geneDrugInteraction"),
ANNOTATION_VARIANT_TRAIT_ASSOCIATION(ANNOTATION, "annotation.variantTraitAssociation"),
ANNOTATION_FUNCTIONAL_SCORE(ANNOTATION, "annotation.functionalScore"),
ANNOTATION_ADDITIONAL_ATTRIBUTES(ANNOTATION, "annotation.additionalAttributes");
private final List<String> names;
private final VariantField parent;
private final AtomicReference<List<VariantField>> children = new AtomicReference<>();
private static final AtomicReference<Map<String, VariantField>> NAMES_MAP = new AtomicReference<>();
VariantField(String... names) {
this(null, names);
}
VariantField(VariantField parent, String... names) {
this.parent = parent;
if (names.length == 0) {
this.names = Collections.singletonList(name().toLowerCase());
} else {
this.names = Collections.unmodifiableList(Arrays.asList(names));
}
}
private static Logger logger = LoggerFactory.getLogger(VariantField.class);
public String fieldName() {
return names.get(0);
}
@Override
public String toString() {
return fieldName();
}
public VariantField getParent() {
return parent;
}
public List<VariantField> getChildren() {
if (children.get() == null) {
ArrayList<VariantField> childrenList = new ArrayList<>();
for (VariantField variantField : VariantField.values()) {
if (variantField.getParent() == this) {
childrenList.add(variantField);
}
}
childrenList.trimToSize();
children.compareAndSet(null, Collections.unmodifiableList(childrenList));
}
return children.get();
}
public static VariantField get(String field) {
return getNamesMap().get(field);
}
public static Set<VariantField> getReturnedFields(QueryOptions options) {
return getReturnedFields(options, false);
}
/**
* Given a QueryOptions, reads the {@link QueryOptions#INCLUDE} and {@link QueryOptions#EXCLUDE} to determine
* which fields from Variant will be returned.
* In case of both fields present, {@link QueryOptions#INCLUDE} will take advantage over {@link QueryOptions#EXCLUDE}
*
* @param options Non null options
* @param prune Remove intermediate nodes some child is missing, or all children from a node if all are present
* @return List of fields to be returned.
*/
public static Set<VariantField> getReturnedFields(QueryOptions options, boolean prune) {
Set<VariantField> returnedFields;
List<String> includeList = options == null ? Collections.emptyList() : options.getAsStringList(QueryOptions.INCLUDE);
if (includeList != null && !includeList.isEmpty()) {
returnedFields = new HashSet<>();
for (String include : includeList) {
VariantField field = get(include);
if (field == null) {
throw VariantQueryException.unknownVariantField(QueryOptions.INCLUDE, include);
// continue;
}
if (field.getParent() != null) {
returnedFields.add(field.getParent());
}
returnedFields.add(field);
returnedFields.addAll(field.getChildren());
}
} else {
List<String> excludeList = options == null ? Collections.emptyList() : options.getAsStringList(QueryOptions.EXCLUDE);
returnedFields = new HashSet<>(Arrays.asList(values()));
if (excludeList != null && !excludeList.isEmpty()) {
for (String exclude : excludeList) {
VariantField field = get(exclude);
if (field == null) {
throw VariantQueryException.unknownVariantField(QueryOptions.EXCLUDE, exclude);
// continue;
}
returnedFields.remove(field);
returnedFields.removeAll(field.getChildren());
}
}
}
if (prune) {
return prune(returnedFields);
} else {
return returnedFields;
}
}
/**
* Remove intermediate nodes some child is missing, or all children from a node if all are present.
*
* @param returnedFields Set of non pruned fields
* @return Pruned set of fields
*/
public static Set<VariantField> prune(Set<VariantField> returnedFields) {
if (returnedFields.containsAll(VariantField.STUDIES.getChildren())) {
returnedFields.removeAll(VariantField.STUDIES.getChildren());
} else {
returnedFields.remove(VariantField.STUDIES);
}
if (returnedFields.containsAll(VariantField.ANNOTATION.getChildren())) {
returnedFields.removeAll(VariantField.ANNOTATION.getChildren());
} else {
returnedFields.remove(VariantField.ANNOTATION);
}
return returnedFields;
}
private static Map<String, VariantField> getNamesMap() {
if (NAMES_MAP.get() == null) {
Map<String, VariantField> map = new HashMap<>();
for (VariantField variantField : VariantField.values()) {
for (String name : variantField.names) {
map.put(name, variantField);
}
}
NAMES_MAP.compareAndSet(null, Collections.unmodifiableMap(map));
}
return NAMES_MAP.get();
}
}