1 package jalview.ws2.actions.annotation;
3 import java.io.IOException;
4 import java.util.ArrayList;
5 import java.util.HashMap;
9 import jalview.analysis.AlignmentAnnotationUtils;
10 import jalview.api.AlignViewportI;
11 import jalview.api.FeatureColourI;
12 import jalview.datamodel.AlignmentAnnotation;
13 import jalview.datamodel.AlignmentI;
14 import jalview.datamodel.AnnotatedCollectionI;
15 import jalview.datamodel.Annotation;
16 import jalview.datamodel.ContiguousI;
17 import jalview.datamodel.Mapping;
18 import jalview.datamodel.SequenceI;
19 import jalview.datamodel.features.FeatureMatcherSetI;
20 import jalview.util.MapList;
21 import jalview.ws.params.ArgumentI;
22 import jalview.ws2.actions.BaseTask;
23 import jalview.ws2.actions.ServiceInputInvalidException;
24 import jalview.ws2.api.Credentials;
25 import jalview.ws2.api.JobStatus;
26 import jalview.ws2.client.api.AnnotationWebServiceClientI;
28 public class AnnotationTask extends BaseTask<AnnotationJob, AnnotationResult>
30 private AnnotationWebServiceClientI client;
32 private final AnnotationAction action;
34 private final AlignmentI alignment;
36 private final AnnotatedCollectionI selectionGroup;
38 public AnnotationTask(AnnotationWebServiceClientI client,
39 AnnotationAction action, List<ArgumentI> args, Credentials credentials,
40 AlignViewportI viewport)
42 super(client, args, credentials);
45 this.alignment = viewport.getAlignment();
46 this.selectionGroup = viewport.getSelectionGroup();
50 * Create and return a list of annotation jobs from the current state of the
51 * viewport. Returned job are not started by this method and should be stored
52 * in a field and started separately.
54 * @return list of annotation jobs
55 * @throws ServiceInputInvalidException
56 * input data is not valid
59 public List<AnnotationJob> prepareJobs() throws ServiceInputInvalidException
61 if (alignment == null || alignment.getWidth() <= 0 ||
62 alignment.getSequences() == null)
63 throw new ServiceInputInvalidException("Alignment does not contain sequences");
64 if (alignment.isNucleotide() && !action.doAllowNucleotide())
65 throw new ServiceInputInvalidException(
66 action.getFullName() + " does not allow nucleotide sequences");
67 if (!alignment.isNucleotide() && !action.doAllowProtein())
68 throw new ServiceInputInvalidException(
69 action.getFullName() + " does not allow protein sequences");
70 boolean bySequence = !action.isAlignmentAnalysis();
71 AnnotatedCollectionI inputSeqs = bySequence ? selectionGroup : null;
72 if (inputSeqs == null || inputSeqs.getWidth() <= 0 ||
73 inputSeqs.getSequences() == null || inputSeqs.getSequences().size() < 1)
74 inputSeqs = alignment;
75 boolean submitGaps = action.isAlignmentAnalysis();
76 boolean requireAligned = action.getRequireAlignedSequences();
77 boolean filterSymbols = action.getFilterSymbols();
78 int minSize = action.getMinSequences();
79 AnnotationJob job = AnnotationJob.create(inputSeqs, bySequence,
80 submitGaps, requireAligned, filterSymbols, minSize);
81 if (!job.isInputValid())
83 job.setStatus(JobStatus.INVALID);
84 throw new ServiceInputInvalidException("Annotation job has invalid input");
86 job.setStatus(JobStatus.READY);
91 protected AnnotationResult collectResult(List<AnnotationJob> jobs) throws IOException
93 final Map<String, FeatureColourI> featureColours = new HashMap<>();
94 final Map<String, FeatureMatcherSetI> featureFilters = new HashMap<>();
95 var job = jobs.get(0);
96 List<AlignmentAnnotation> returnedAnnot = client.attachAnnotations(
97 job.getServerJob(), job.getInputSequences(), featureColours,
100 * copy over each annotation row returned and also defined on each
101 * sequence, excluding regions not annotated due to gapMap/column
104 udpateCalcId(returnedAnnot);
105 for (AlignmentAnnotation ala : returnedAnnot)
107 SequenceI aseq = null;
108 if (ala.sequenceRef != null)
110 SequenceI seq = job.seqNames.get(ala.sequenceRef.getName());
111 aseq = seq.getRootDatasetSequence();
113 ala.sequenceRef = aseq;
114 Annotation[] gappedAnnots = createGappedAnnotations(ala.annotations, job.start, job.gapMap);
115 ala.annotations = gappedAnnots;
118 boolean hasFeatures = false;
119 for (SequenceI sq : job.getInputSequences())
121 if (!sq.getFeatures().hasFeatures() && (sq.getDBRefs() == null || sq.getDBRefs().isEmpty()))
124 SequenceI seq = job.seqNames.get(sq.getName());
125 SequenceI datasetSeq = seq.getRootDatasetSequence();
126 List<ContiguousI> sourceRange = findContiguousRanges(datasetSeq, job.gapMap, job.start, job.end);
127 int[] sourceStartEnd = ContiguousI.toStartEndArray(sourceRange);
128 Mapping mp = new Mapping(new MapList(
129 sourceStartEnd, new int[]
130 { datasetSeq.getStart(), datasetSeq.getEnd() }, 1, 1));
131 datasetSeq.transferAnnotation(sq, mp);
134 return new AnnotationResult(returnedAnnot, hasFeatures, featureColours, featureFilters);
138 * Updates calcId on provided annotations if not already set.
140 public void udpateCalcId(Iterable<AlignmentAnnotation> annotations)
142 for (var annotation : annotations)
144 if (annotation.getCalcId() == null || annotation.getCalcId().isEmpty())
146 annotation.setCalcId(action.getFullName());
148 annotation.autoCalculated = action.isAlignmentAnalysis() &&
149 action.getWebService().isInteractive();
153 private Annotation[] createGappedAnnotations(Annotation[] annotations, int start, boolean[] gapMap)
155 var size = Math.max(alignment.getWidth(), gapMap.length);
156 Annotation[] gappedAnnotations = new Annotation[size];
157 for (int p = 0, ap = start; ap < size; ap++)
159 if (gapMap != null && gapMap.length > ap && !gapMap[ap])
161 gappedAnnotations[ap] = new Annotation("", "", ' ', Float.NaN);
163 else if (p < annotations.length)
165 gappedAnnotations[ap] = annotations[p++];
168 return gappedAnnotations;
171 private List<ContiguousI> findContiguousRanges(SequenceI seq, boolean[] gapMap, int start, int end)
173 if (gapMap == null || gapMap.length < end)
174 return List.of(seq.findPositions(start, end));
175 List<ContiguousI> ranges = new ArrayList<>();
176 int lastcol = start, col = start;
179 if (col == end || !gapMap[col])
182 ranges.add(seq.findPositions(lastcol, col));
185 } while (++col <= end);