package org.nextprot.api.core.service.impl;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
import com.google.common.collect.Sets;
import org.nextprot.api.commons.constants.IdentifierOffset;
import org.nextprot.api.commons.constants.Xref2Annotation;
import org.nextprot.api.core.dao.DbXrefDao;
import org.nextprot.api.core.domain.DbXref;
import org.nextprot.api.core.domain.DbXref.DbXrefProperty;
import org.nextprot.api.core.domain.Isoform;
import org.nextprot.api.core.domain.PublicationDbXref;
import org.nextprot.api.core.domain.annotation.Annotation;
import org.nextprot.api.core.domain.annotation.AnnotationEvidence;
import org.nextprot.api.core.domain.annotation.AnnotationIsoformSpecificity;
import org.nextprot.api.core.service.AntibodyResourceIdsService;
import org.nextprot.api.core.service.DbXrefService;
import org.nextprot.api.core.service.IsoformService;
import org.nextprot.api.core.service.PeptideNamesService;
import org.nextprot.api.core.utils.dbxref.conv.DbXrefConverter;
import org.nextprot.api.core.utils.dbxref.conv.EnsemblXrefPropertyConverter;
import org.nextprot.api.core.utils.dbxref.resolver.XRefDatabase;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.stream.Collectors;
@Lazy
@Service
public class DbXrefServiceImpl implements DbXrefService {
private static final Set<String> HIDDEN_PROPERTY_NAME_SET = Sets.newHashSet("match status", "organism ID", "organism name");
@Autowired private DbXrefDao dbXRefDao;
@Autowired private PeptideNamesService peptideNamesService;
@Autowired private AntibodyResourceIdsService antibodyResourceIdsService;
@Autowired private IsoformService isoService;
@Override
public List<DbXref> findDbXRefByPublicationId(Long publicationId) {
return this.dbXRefDao.findDbXRefsByPublicationId(publicationId);
}
@Override
public List<PublicationDbXref> findDbXRefByPublicationIds(List<Long> publicationIds) {
return dbXRefDao.findDbXRefByPublicationIds(publicationIds);
}
@Override
public List<DbXref> findDbXRefByIds(List<Long> resourceIds) {
return dbXRefDao.findDbXRefByIds(resourceIds);
}
private List<Annotation> convertXrefsIntoAnnotations(List<DbXref> xrefs, String entryName) {
List<Isoform> isoforms = isoService.findIsoformsByEntryName(entryName);
List<Annotation> xrefAnnotations = new ArrayList<>();
for (DbXref xref : xrefs) {
xrefAnnotations.add(convertXrefIntoAnnotation(xref, entryName, isoforms));
}
return xrefAnnotations;
}
private Annotation convertXrefIntoAnnotation(DbXref xref, String entryName, List<Isoform> isoforms) {
Preconditions.checkNotNull(xref.getProperties());
Annotation annotation = new Annotation();
annotation.setAnnotationId(xref.getDbXrefId() + IdentifierOffset.XREF_ANNOTATION_OFFSET);
Xref2Annotation xam = Xref2Annotation.getByDatabaseName(xref.getDatabaseName());
annotation.setCategory(xam.getAnnotCat());
annotation.setDescription(xref.getPropertyValue(xam.getXrefPropName())); // copy of some xref property
annotation.setQualityQualifier(xam.getQualityQualifier());
annotation.setCvTermName(null);
annotation.setCvTermAccessionCode(null);
annotation.setSynonym(null);
annotation.setUniqueName("AN_" + entryName.substring(3) + "_XR_" + String.valueOf(xref.getDbXrefId()));
annotation.setParentXref(xref);
annotation.setEvidences(Collections.singletonList(newAnnotationEvidence(annotation)));
annotation.addTargetingIsoforms(newAnnotationIsoformSpecificityList(isoforms, annotation));
return annotation;
}
private AnnotationEvidence newAnnotationEvidence(Annotation xrefAnnotation) {
AnnotationEvidence evidence = new AnnotationEvidence();
DbXref pxref = xrefAnnotation.getParentXref();
evidence.setAnnotationId(xrefAnnotation.getAnnotationId());
Xref2Annotation xam = Xref2Annotation.getByDatabaseName(pxref.getDatabaseName());
evidence.setEvidenceId(xrefAnnotation.getAnnotationId() + IdentifierOffset.XREF_ANNOTATION_EVIDENCE_OFFSET);
evidence.setAssignedBy(xam.getSrcName());
evidence.setResourceId(pxref.getDbXrefId());
evidence.setResourceAccession(pxref.getAccession());
evidence.setResourceDb(pxref.getDatabaseName());
evidence.setResourceAssociationType("evidence");
evidence.setResourceType("database");
evidence.setEvidenceCodeOntology(xam.getEcoOntology());
evidence.setNegativeEvidence(false);
evidence.setExperimentalContextId(null);
evidence.setResourceDescription(null);
evidence.setProperties(new ArrayList<>());
evidence.setQualifierType(xam.getQualifierType());
evidence.setQualityQualifier(xam.getQualityQualifier());
evidence.setAssignmentMethod(xam.getAssignmentMethod());
evidence.setEvidenceCodeAC(xam.getEcoAC());
evidence.setEvidenceCodeName(xam.getEcoName());
return evidence;
}
// build isoform specificity from isoforms and annotations and link them to annotations
private List<AnnotationIsoformSpecificity> newAnnotationIsoformSpecificityList(List<Isoform> isoforms, Annotation xrefAnnotation) {
List<AnnotationIsoformSpecificity> isospecs = new ArrayList<>();
for (Isoform iso: isoforms) {
AnnotationIsoformSpecificity isospec = new AnnotationIsoformSpecificity();
isospec.setAnnotationId(xrefAnnotation.getAnnotationId());
isospec.setFirstPosition(null); //According to PAM on Tuesday th 16th in the presence of Pascale and Frederic Nikitin
isospec.setLastPosition(null);
isospec.setIsoformAccession(iso.getIsoformAccession());
isospec.setSpecificity("UNKNOWN");
isospecs.add(isospec);
}
return isospecs;
}
@Override
/** Convert dbxrefs of type XrefAnnotationMapping into annotation for the given entry */
public List<Annotation> findDbXrefsAsAnnotationsByEntry(String entryName) {
List<DbXref> xrefsToConvert = findDbXrefsConvertibleIntoAnnotationByEntry(entryName);
if(!xrefsToConvert.isEmpty())
attachPropertiesToXrefs(xrefsToConvert, entryName, true);
List<Annotation> xrefAnnotations = convertXrefsIntoAnnotations(xrefsToConvert, entryName);
return new ImmutableList.Builder<Annotation>().addAll(xrefAnnotations).build();
}
/**
* Find dbxrefs convertible into Annotations (of type XrefAnnotationMapping)
* @param uniqueName the entry name
* @return a list of DbXref convertible to Annotation
*/
private List<DbXref> findDbXrefsConvertibleIntoAnnotationByEntry(String uniqueName) {
return dbXRefDao.findDbXrefsAsAnnotByMaster(uniqueName);
}
@Override
@Cacheable("xrefs")
public List<DbXref> findDbXrefsByMaster(String entryName) {
// build a comparator for the tree set: order by database name, accession, case insensitive
Comparator<DbXref> comparator = (a, b) -> {
int cmp1 = a.getDatabaseName().toUpperCase().compareTo(b.getDatabaseName().toUpperCase());
if (cmp1!=0) return cmp1;
return a.getAccession().toUpperCase().compareTo(b.getAccession().toUpperCase());
};
// now merge xrefs associated to the entry by annot, interact, mappings, etc. in the tree set
Set<DbXref> xrefs = new TreeSet<>(comparator);
addPeptideXrefs(entryName, xrefs);
addAntibodyXrefs(entryName, xrefs);
xrefs.addAll(this.dbXRefDao.findEntryAnnotationsEvidenceXrefs(entryName));
xrefs.addAll(this.dbXRefDao.findEntryAttachedXrefs(entryName));
xrefs.addAll(this.dbXRefDao.findEntryIdentifierXrefs(entryName));
xrefs.addAll(this.dbXRefDao.findEntryInteractionXrefs(entryName)); // xrefs of interactions evidences
xrefs.addAll(this.dbXRefDao.findEntryInteractionInteractantsXrefs(entryName)); // xrefs of xeno interactants
// turn the set into a list to match the signature expected elsewhere
List<DbXref> xrefList = new ArrayList<>(xrefs);
// get and attach the properties to the xrefs
if (! xrefList.isEmpty()) {
attachPropertiesToXrefs(xrefList, entryName, false);
}
//returns a immutable list when the result is cacheable (this prevents modifying the cache, since the cache returns a reference) copy on read and copy on write is too much time consuming
return new ImmutableList.Builder<DbXref>().addAll(xrefList).build();
}
private void addPeptideXrefs(String entryName, Set<DbXref> xrefs) {
List<String> names = peptideNamesService.findAllPeptideNamesByMasterId(entryName);
xrefs.addAll(!names.isEmpty() ? dbXRefDao.findPeptideXrefs(names) : new HashSet<>());
}
private void addAntibodyXrefs(String entryName, Set<DbXref> xrefs) {
List<Long> ids = antibodyResourceIdsService.findAllAntibodyIdsByMasterId(entryName);
xrefs.addAll(!ids.isEmpty() ? dbXRefDao.findAntibodyXrefs(ids) : new HashSet<>());
}
private void attachPropertiesToXrefs(List<DbXref> xrefs, String uniqueName, boolean fetchXrefAnnotationMappingProperties) {
List<Long> xrefIds = xrefs.stream().map(DbXref::getDbXrefId).collect(Collectors.toList());
List<DbXrefProperty> shownProperties = dbXRefDao.findDbXrefsProperties(xrefIds).stream()
.filter(p -> !HIDDEN_PROPERTY_NAME_SET.contains(p.getName()))
.collect(Collectors.toList());
Multimap<Long, DbXrefProperty> propsMap = Multimaps.index(shownProperties, DbXrefProperty::getDbXrefId);
Map<Long, List<DbXrefProperty>> ensemblPropertiesMap = getDbXrefEnsemblInfos(uniqueName, xrefs);
for (DbXref xref : xrefs) {
if (!fetchXrefAnnotationMappingProperties)
xref.setProperties((!Xref2Annotation.hasName(xref.getDatabaseName())) ? new ArrayList<>(propsMap.get(xref.getDbXrefId())) : new ArrayList<>());
else
xref.setProperties(new ArrayList<>(propsMap.get(xref.getDbXrefId())));
/*if (xref.getLinkUrl().contains("%u")) {
xref.setResolvedUrl(new DbXrefURLResolverDelegate().resolveWithAccession(xref, uniqueName));
}*/
if (ensemblPropertiesMap.containsKey(xref.getDbXrefId())) {
xref.addProperties(ensemblPropertiesMap.get(xref.getDbXrefId()));
}
}
xrefs.addAll(createMissingDbXrefs(xrefs));
}
private Map<Long, List<DbXrefProperty>> getDbXrefEnsemblInfos(String uniqueName, List<DbXref> xrefs) {
List<Long> ensemblRefIds = xrefs.stream().filter(xref -> xref.getAccession().startsWith("ENST")).map(DbXref::getDbXrefId).collect(Collectors.toList());
List<DbXref.EnsemblInfos> ensemblXRefInfosList = dbXRefDao.findDbXrefEnsemblInfos(uniqueName, ensemblRefIds);
EnsemblXrefPropertyConverter converter = EnsemblXrefPropertyConverter.getInstance();
Map<Long, List<DbXrefProperty>> map = new HashMap<>();
for (DbXref.EnsemblInfos infos : ensemblXRefInfosList) {
map.put(infos.getTranscriptXrefId(), converter.convert(infos));
}
return map;
}
/**
* Create dynamically missing xrefs from specific properties
*
* @param xrefs a list of xrefs
* @return the new created list
*/
private List<DbXref> createMissingDbXrefs(List<DbXref> xrefs) {
List<DbXref> newXrefs = new ArrayList<>();
for (DbXref xref : xrefs) {
if (XRefDatabase.REF_SEQ.getName().equals(xref.getDatabaseName()) ||
XRefDatabase.EMBL.getName().equals(xref.getDatabaseName())) {
newXrefs.addAll(DbXrefConverter.getInstance().convert(xref));
}
}
return newXrefs;
}
@Override
public List<DbXref> findDbXrefByAccession(String accession) {
return dbXRefDao.findDbXrefByAccession(accession);
}
@Override
public List<DbXref> findAllDbXrefs() {
return dbXRefDao.findAllDbXrefs();
}
@Override
public List<DbXref> findDbXRefByResourceId(Long resourceId) {
return dbXRefDao.findDbXrefByResourceId(resourceId);
}
@Override
public List<Long> getAllDbXrefsIds() {
return dbXRefDao.getAllDbXrefsIds();
}
}