23e462b9f890910a61e9c12836ee920ee399ab97
[jalview.git] / src / jalview / ws2 / actions / annotation / AnnotationJob.java
1 package jalview.ws2.actions.annotation;
2
3 import java.util.ArrayList;
4 import java.util.BitSet;
5 import java.util.Collections;
6 import java.util.HashMap;
7 import java.util.List;
8 import java.util.Map;
9
10 import jalview.analysis.AlignSeq;
11 import jalview.analysis.SeqsetUtils;
12 import jalview.api.FeatureColourI;
13 import jalview.datamodel.AlignmentAnnotation;
14 import jalview.datamodel.AlignmentI;
15 import jalview.datamodel.AnnotatedCollectionI;
16 import jalview.datamodel.Sequence;
17 import jalview.datamodel.SequenceI;
18 import jalview.datamodel.features.FeatureMatcherSetI;
19 import jalview.schemes.ResidueProperties;
20 import jalview.util.Comparison;
21 import jalview.ws2.actions.BaseJob;
22
23 public class AnnotationJob extends BaseJob
24 {
25   final boolean[] gapMap;
26
27   final Map<String, SequenceI> seqNames;
28
29   final int start, end;
30   
31   final int minSize;
32
33   List<AlignmentAnnotation> returnedAnnotations = Collections.emptyList();
34   
35   Map<String, FeatureColourI> featureColours = Collections.emptyMap();
36   
37   Map<String, FeatureMatcherSetI> featureFilters = Collections.emptyMap();
38   
39
40   public AnnotationJob(List<SequenceI> inputSeqs, boolean[] gapMap,
41       Map<String, SequenceI> seqNames, int start, int end, int minSize)
42   {
43     super(inputSeqs);
44     this.gapMap = gapMap;
45     this.seqNames = seqNames;
46     this.start = start;
47     this.end = end;
48     this.minSize = minSize;
49   }
50
51   @Override
52   public boolean isInputValid()
53   {
54     int nvalid = 0;
55     for (SequenceI sq : getInputSequences())
56       if (sq.getStart() <= sq.getEnd())
57         nvalid++;
58     return nvalid >= minSize;
59   }
60
61   public static AnnotationJob create(AnnotatedCollectionI inputSeqs, 
62       boolean bySequence, boolean submitGaps, boolean requireAligned, 
63       boolean filterNonStandardResidues, int minSize)
64   {
65     List<SequenceI> seqs = new ArrayList<>();
66     int minlen = 10;
67     int ln = -1;
68     Map<String, SequenceI> seqNames = bySequence ? new HashMap<>() : null;
69     BitSet gapMap = new BitSet();
70     int gapMapSize = 0;
71     int start = inputSeqs.getStartRes();
72     int end = inputSeqs.getEndRes();
73     // TODO: URGENT! unify with JPred / MSA code to handle hidden regions
74     // correctly
75     // TODO: push attributes into WsJob instance (so they can be safely
76     // persisted/restored
77     for (SequenceI sq : inputSeqs.getSequences())
78     {
79       int sqlen;
80       if (bySequence)
81         sqlen = sq.findPosition(end + 1) - sq.findPosition(start + 1);
82       else
83         sqlen = sq.getEnd() - sq.getStart();
84       if (sqlen >= minlen)
85       {
86         String newName = SeqsetUtils.unique_name(seqs.size() + 1);
87         if (seqNames != null)
88           seqNames.put(newName, sq);
89         Sequence seq;
90         if (submitGaps)
91         {
92           seq = new Sequence(newName, sq.getSequenceAsString());
93           gapMapSize = Math.max(gapMapSize, seq.getLength());
94           for (int pos : sq.gapMap())
95           {
96             char sqchr = sq.getCharAt(pos);
97             boolean include = !filterNonStandardResidues;
98             include |= sq.isProtein() ? ResidueProperties.aaIndex[sqchr] < 20
99                 : ResidueProperties.nucleotideIndex[sqchr] < 5;
100             if (include)
101               gapMap.set(pos);
102           }
103         }
104         else
105         {
106           // TODO: add ability to exclude hidden regions
107           seq = new Sequence(newName, AlignSeq.extractGaps(Comparison.GapChars,
108               sq.getSequenceAsString(start, end + 1)));
109           // for annotation need to also record map to sequence start/end
110           // position in range
111           // then transfer back to original sequence on return.
112         }
113         seqs.add(seq);
114         ln = Math.max(ln, seq.getLength());
115       }
116     }
117
118     if (requireAligned && submitGaps)
119     {
120       int realWidth = gapMap.cardinality();
121       for (int i = 0; i < seqs.size(); i++)
122       {
123         SequenceI sq = seqs.get(i);
124         char[] padded = new char[realWidth];
125         char[] original = sq.getSequence();
126         for (int op = 0, pp = 0; pp < realWidth; op++)
127         {
128           if (gapMap.get(op))
129           {
130             if (original.length > op)
131               padded[pp++] = original[op];
132             else
133               padded[pp++] = '-';
134           }
135         }
136         seqs.set(i, new Sequence(sq.getName(), padded));
137       }
138     }
139     boolean[] gapMapArray = new boolean[gapMapSize];
140     for (int i = 0; i < gapMapSize; i++)
141       gapMapArray[i] = gapMap.get(i);
142     return new AnnotationJob(seqs, gapMapArray, seqNames, start, end, minSize);
143   }
144 }