Clover icon

Coverage Report

  1. Project Clover database Wed Dec 3 2025 17:03:17 GMT
  2. Package jalview.ws2.actions.annotation

File AnnotationTask.java

 

Coverage histogram

../../../../img/srcFileCovDistChart0.png
60% of files have more coverage

Code metrics

36
71
6
1
205
171
38
0.54
11.83
6
6.33

Classes

Class Line # Actions
AnnotationTask 27 71 38
0.00%
 

Contributing tests

No tests hitting this source file were found.

Source view

1    package jalview.ws2.actions.annotation;
2   
3    import java.io.IOException;
4    import java.util.ArrayList;
5    import java.util.HashMap;
6    import java.util.List;
7    import java.util.Map;
8   
9    import jalview.api.AlignViewportI;
10    import jalview.api.FeatureColourI;
11    import jalview.datamodel.AlignmentAnnotation;
12    import jalview.datamodel.AlignmentI;
13    import jalview.datamodel.AnnotatedCollectionI;
14    import jalview.datamodel.Annotation;
15    import jalview.datamodel.ContiguousI;
16    import jalview.datamodel.Mapping;
17    import jalview.datamodel.SequenceI;
18    import jalview.datamodel.features.FeatureMatcherSetI;
19    import jalview.util.MapList;
20    import jalview.ws.params.ArgumentI;
21    import jalview.ws2.actions.BaseTask;
22    import jalview.ws2.actions.ServiceInputInvalidException;
23    import jalview.ws2.api.Credentials;
24    import jalview.ws2.api.JobStatus;
25    import jalview.ws2.client.api.AnnotationWebServiceClientI;
26   
 
27    public class AnnotationTask
28    extends BaseTask<AnnotationJob, AnnotationResult>
29    {
30    private AnnotationWebServiceClientI client;
31   
32    private final AnnotationAction action;
33   
34    private final AlignmentI alignment;
35   
36    private final AnnotatedCollectionI selectionGroup;
37   
 
38  0 toggle public AnnotationTask(AnnotationWebServiceClientI client,
39    AnnotationAction action, List<ArgumentI> args,
40    Credentials credentials, AlignViewportI viewport)
41    {
42  0 super(client, args, credentials);
43  0 this.client = client;
44  0 this.action = action;
45  0 this.alignment = viewport.getAlignment();
46  0 this.selectionGroup = viewport.getSelectionGroup();
47    }
48   
49    /**
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.
53    *
54    * @return list of annotation jobs
55    * @throws ServiceInputInvalidException
56    * input data is not valid
57    */
 
58  0 toggle @Override
59    public List<AnnotationJob> prepareJobs()
60    throws ServiceInputInvalidException
61    {
62  0 if (alignment == null || alignment.getWidth() <= 0
63    || alignment.getSequences() == null)
64  0 throw new ServiceInputInvalidException(
65    "Alignment does not contain sequences");
66  0 if (alignment.isNucleotide() && !action.doAllowNucleotide())
67  0 throw new ServiceInputInvalidException(action.getFullName()
68    + " does not allow nucleotide sequences");
69  0 if (!alignment.isNucleotide() && !action.doAllowProtein())
70  0 throw new ServiceInputInvalidException(
71    action.getFullName() + " does not allow protein sequences");
72  0 boolean bySequence = !action.isAlignmentAnalysis();
73  0 AnnotatedCollectionI inputSeqs = bySequence ? selectionGroup : null;
74  0 if (inputSeqs == null || inputSeqs.getWidth() <= 0
75    || inputSeqs.getSequences() == null
76    || inputSeqs.getSequences().size() < 1)
77  0 inputSeqs = alignment;
78  0 boolean submitGaps = action.isAlignmentAnalysis();
79  0 boolean requireAligned = action.getRequireAlignedSequences();
80  0 boolean filterSymbols = action.getFilterSymbols();
81  0 int minSize = action.getMinSequences();
82  0 AnnotationJob job = AnnotationJob.create(inputSeqs, bySequence,
83    submitGaps, requireAligned, filterSymbols, minSize);
84  0 if (!job.isInputValid())
85    {
86  0 job.setStatus(JobStatus.INVALID);
87  0 throw new ServiceInputInvalidException(
88    "Annotation job has invalid input");
89    }
90  0 job.setStatus(JobStatus.READY);
91  0 return List.of(job);
92    }
93   
 
94  0 toggle @Override
95    protected AnnotationResult collectResult(List<AnnotationJob> jobs)
96    throws IOException
97    {
98  0 final Map<String, FeatureColourI> featureColours = new HashMap<>();
99  0 final Map<String, FeatureMatcherSetI> featureFilters = new HashMap<>();
100  0 var job = jobs.get(0);
101  0 List<AlignmentAnnotation> returnedAnnot = client.attachAnnotations(
102    job.getServerJob(), job.getInputSequences(), featureColours,
103    featureFilters);
104    /* TODO
105    * copy over each annotation row returned and also defined on each
106    * sequence, excluding regions not annotated due to gapMap/column
107    * visibility */
108   
109  0 udpateCalcId(returnedAnnot);
110  0 for (AlignmentAnnotation ala : returnedAnnot)
111    {
112  0 SequenceI seq = (ala.sequenceRef == null) ? null
113    : job.seqNames.get(ala.sequenceRef.getName());
114  0 if (job.gapMap != null && job.gapMap.length > 0)
115  0 ala.annotations = createGappedAnnotations(ala.annotations,
116    job.gapMap);
117  0 if (seq != null)
118    {
119  0 int startRes = seq.findPosition(job.regionStart);
120  0 ala.createSequenceMapping(seq, startRes, false);
121    }
122    }
123   
124  0 boolean hasFeatures = false;
125  0 for (SequenceI sq : job.getInputSequences())
126    {
127  0 if (!sq.getFeatures().hasFeatures()
128    && (sq.getDBRefs() == null || sq.getDBRefs().isEmpty()))
129  0 continue;
130  0 hasFeatures = true;
131  0 SequenceI seq = job.seqNames.get(sq.getName());
132  0 SequenceI datasetSeq = seq.getRootDatasetSequence();
133  0 List<ContiguousI> sourceRange = findContiguousRanges(seq,
134    job.gapMap, job.regionStart, job.regionEnd);
135  0 int[] sourceStartEnd = ContiguousI.toStartEndArray(sourceRange);
136  0 Mapping mp = new Mapping(
137    new MapList(
138    sourceStartEnd,
139    new int[] { datasetSeq.getStart(), datasetSeq.getEnd() },
140    1, 1));
141  0 datasetSeq.transferAnnotation(sq, mp);
142    }
143   
144  0 return new AnnotationResult(returnedAnnot, hasFeatures, featureColours,
145    featureFilters);
146    }
147   
148    /**
149    * Updates calcId on provided annotations if not already set.
150    */
 
151  0 toggle public void udpateCalcId(Iterable<AlignmentAnnotation> annotations)
152    {
153  0 for (var annotation : annotations)
154    {
155  0 if (annotation.getCalcId() == null
156    || annotation.getCalcId().isEmpty())
157    {
158  0 annotation.setCalcId(action.getFullName());
159    }
160  0 annotation.autoCalculated = action.isAlignmentAnalysis()
161    && action.getWebService().isInteractive();
162    }
163    }
164   
165    // TODO: review and test
166    // may produce wrong output if annotations longer than gapMap
 
167  0 toggle private Annotation[] createGappedAnnotations(Annotation[] annotations,
168    boolean[] gapMap)
169    {
170  0 var size = Math.max(annotations.length, gapMap.length);
171  0 Annotation[] gappedAnnotations = new Annotation[size];
172  0 for (int p = 0, ap = 0; ap < size; ap++)
173    {
174  0 if (ap < gapMap.length && !gapMap[ap])
175    {
176  0 gappedAnnotations[ap] = new Annotation("", "", ' ', Float.NaN);
177    }
178  0 else if (p < annotations.length)
179    {
180  0 gappedAnnotations[ap] = annotations[p++];
181    }
182    }
183  0 return gappedAnnotations;
184    }
185   
186    // TODO: review ant test!!!
 
187  0 toggle private List<ContiguousI> findContiguousRanges(SequenceI seq,
188    boolean[] gapMap, int start, int end)
189    {
190  0 if (gapMap == null || gapMap.length < end)
191  0 return List.of(seq.findPositions(start + 1, end + 1));
192  0 List<ContiguousI> ranges = new ArrayList<>();
193  0 int lastcol = start, col = start;
194  0 do
195    {
196  0 if (col == end || !gapMap[col])
197    {
198  0 if (lastcol < col)
199  0 ranges.add(seq.findPositions(lastcol, col));
200  0 lastcol = col + 1;
201    }
202  0 } while (++col <= end);
203  0 return ranges;
204    }
205    }