package org.nextprot.api.isoform.mapper.service.impl;
import com.google.common.base.Strings;
import org.nextprot.api.commons.bio.variation.prot.seqchange.SequenceChange;
import org.nextprot.api.commons.bio.variation.prot.SequenceVariation;
import org.nextprot.api.commons.exception.NextProtException;
import org.nextprot.api.commons.service.MasterIdentifierService;
import org.nextprot.api.core.domain.Entry;
import org.nextprot.api.core.domain.Isoform;
import org.nextprot.api.core.service.EntryBuilderService;
import org.nextprot.api.core.service.MasterIsoformMappingService;
import org.nextprot.api.core.service.fluent.EntryConfig;
import org.nextprot.api.core.utils.IsoformUtils;
import org.nextprot.api.core.utils.seqmap.IsoformSequencePositionMapper;
import org.nextprot.api.isoform.mapper.domain.FeatureQueryException;
import org.nextprot.api.isoform.mapper.domain.SequenceFeature;
import org.nextprot.api.isoform.mapper.domain.SingleFeatureQuery;
import org.nextprot.api.isoform.mapper.domain.impl.BaseFeatureQueryResult;
import org.nextprot.api.isoform.mapper.domain.impl.FeatureQueryFailureImpl;
import org.nextprot.api.isoform.mapper.domain.impl.SequenceFeatureBase;
import org.nextprot.api.isoform.mapper.domain.impl.SingleFeatureQuerySuccessImpl;
import org.nextprot.api.isoform.mapper.domain.impl.exception.EntryAccessionNotFoundForGeneException;
import org.nextprot.api.isoform.mapper.domain.impl.exception.MultipleEntryAccessionForGeneException;
import org.nextprot.api.isoform.mapper.service.IsoformMappingService;
import org.nextprot.api.isoform.mapper.service.SequenceFeatureValidator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.text.ParseException;
import java.util.Set;
/**
* Specs: https://issues.isb-sib.ch/browse/BIOEDITOR-397
*/
@Service
public class IsoformMappingServiceImpl implements IsoformMappingService {
@Autowired
public MasterIsoformMappingService masterIsoformMappingService;
@Autowired
private EntryBuilderService entryBuilderService;
@Autowired
private MasterIdentifierService masterIdentifierService;
@Override
public BaseFeatureQueryResult validateFeature(SingleFeatureQuery query) {
try {
SequenceFeature sequenceFeature = SequenceFeatureBase.newFeature(query);
if (Strings.isNullOrEmpty(query.getAccession()))
query.setAccession(findAccessionFromGeneName(query, sequenceFeature.getGeneName()));
Entry entry = entryBuilderService.build(EntryConfig.newConfig(query.getAccession())
.withTargetIsoforms().withOverview());
SequenceFeatureValidator validator = new SequenceFeatureValidator(entry, query);
return validator.validate(sequenceFeature);
} catch (FeatureQueryException e) {
return new FeatureQueryFailureImpl(e);
}
}
@Override
public BaseFeatureQueryResult propagateFeature(SingleFeatureQuery query) {
BaseFeatureQueryResult results = validateFeature(query);
if (results.isSuccess()) {
try {
propagate((SingleFeatureQuerySuccessImpl) results);
} catch (ParseException e) {
throw new NextProtException(e.getMessage());
}
}
return results;
}
// TODO: refactor this method, it is too complex (probably a propagator object with strategy pattern for the mapping)
private void propagate(SingleFeatureQuerySuccessImpl successResults) throws ParseException {
SingleFeatureQuery query = successResults.getQuery();
query.setPropagableFeature(true);
SequenceFeature isoFeature = successResults.getIsoformSequenceFeature();
Isoform featureIsoform = isoFeature.getIsoform(successResults.getEntry());
SequenceVariation variation = isoFeature.getProteinVariation();
OriginalAminoAcids originalAminoAcids = getOriginalAminoAcids(featureIsoform.getSequence(), variation);
// propagate the feature to other isoforms
for (Isoform otherIsoform : IsoformUtils.getOtherIsoforms(successResults.getEntry(), featureIsoform.getUniqueName())) {
Integer firstIsoPos = IsoformSequencePositionMapper.getProjectedPosition(featureIsoform,
originalAminoAcids.getFirstAAPos(), otherIsoform);
Integer lastIsoPos = firstIsoPos;
if (firstIsoPos != null) {
if (IsoformSequencePositionMapper.checkAminoAcidsFromPosition(otherIsoform, firstIsoPos, originalAminoAcids.getAas())
&& variation.getVaryingSequence().isMultipleAminoAcids()) {
lastIsoPos = IsoformSequencePositionMapper.getProjectedPosition(featureIsoform, originalAminoAcids.getLastAAPos(), otherIsoform);
}
if (lastIsoPos != null) {
if (originalAminoAcids.isExtensionTerminal())
successResults.addMappedFeature(otherIsoform, firstIsoPos + 1, lastIsoPos + 1);
else
successResults.addMappedFeature(otherIsoform, firstIsoPos, lastIsoPos);
}
else {
successResults.addUnmappedFeature(otherIsoform);
}
}
else {
successResults.addUnmappedFeature(otherIsoform);
}
}
}
private OriginalAminoAcids getOriginalAminoAcids(String sequence, SequenceVariation variation) {
SequenceChange.Type variationType = variation.getSequenceChange().getType();
int firstPos = variation.getVaryingSequence().getFirstAminoAcidPos();
int lastPos = variation.getVaryingSequence().getLastAminoAcidPos();
boolean isTerminalExtension = false;
if (variationType == SequenceChange.Type.EXTENSION_TERM) {
firstPos = sequence.length();
lastPos = sequence.length();
isTerminalExtension = true;
}
return new OriginalAminoAcids(sequence, firstPos, lastPos, isTerminalExtension);
}
/**
* Find entry accession from geneName
*/
private String findAccessionFromGeneName(SingleFeatureQuery query, String geneName) throws FeatureQueryException {
Set<String> accessions = masterIdentifierService.findEntryAccessionByGeneName(geneName, false);
if (accessions.isEmpty()) {
throw new EntryAccessionNotFoundForGeneException(query, geneName);
} else if (accessions.size() > 1) {
throw new MultipleEntryAccessionForGeneException(query, geneName, accessions);
}
// found one single entry accession
return accessions.iterator().next();
}
private static class OriginalAminoAcids {
private final String aas;
private final int firstAAPos;
private final int lastAAPos;
private final boolean isExtensionTerminal;
public OriginalAminoAcids(String sequence, int firstAAPos, int lastAAPos, boolean isExtensionTerminal) {
this.aas = sequence.substring(firstAAPos-1, lastAAPos);
this.firstAAPos = firstAAPos;
this.lastAAPos = lastAAPos;
this.isExtensionTerminal = isExtensionTerminal;
}
public String getAas() {
return aas;
}
public int getFirstAAPos() {
return firstAAPos;
}
public int getLastAAPos() {
return lastAAPos;
}
public boolean isExtensionTerminal() {
return isExtensionTerminal;
}
}
}