JAL-2349 fix up tests for Java 11 compatibility and require sequence associated mappa...
[jalview.git] / src / jalview / ws / datamodel / alphafold / MappableContactMatrix.java
1 package jalview.ws.datamodel.alphafold;
2
3 import java.util.ArrayList;
4
5 import jalview.datamodel.ContactListI;
6 import jalview.datamodel.ContactListImpl;
7 import jalview.datamodel.ContactListProviderI;
8 import jalview.datamodel.Mapping;
9 import jalview.datamodel.SequenceI;
10 import jalview.util.MapList;
11 import jalview.ws.datamodel.MappableContactMatrixI;
12
13 public abstract class MappableContactMatrix<T extends MappableContactMatrix<T>> implements MappableContactMatrixI
14 {
15   SequenceI refSeq = null;
16   MapList toSeq = null;
17
18   /**
19    * the length that refSeq is expected to be (excluding gaps, of course)
20    */
21   int length;
22
23
24   @Override
25   public boolean hasReferenceSeq()
26   {
27     return (refSeq != null);
28   }
29
30   @Override
31   public SequenceI getReferenceSeq()
32   {
33     return refSeq;
34   }
35   @Override
36   public MapList getMapFor(SequenceI mapSeq)
37   {
38     if (refSeq!=null)
39     {
40       while (mapSeq!=refSeq && mapSeq.getDatasetSequence()!=null)
41       {
42         mapSeq = mapSeq.getDatasetSequence();
43       }
44       if (mapSeq!=refSeq)
45       {
46         return null;
47       }
48     } else {
49       if (mapSeq!=null) {
50         // our MapList does not concern this seq
51         return null;
52       }
53     }
54     
55     return toSeq;
56   }
57
58   /**
59    * set the reference sequence and construct the mapping between the start-end positions of given sequence and row/columns of contact matrix
60    * @param _refSeq
61    */
62   public void setRefSeq(SequenceI _refSeq)
63   {
64     refSeq = _refSeq;
65     while (refSeq.getDatasetSequence() != null)
66     {
67       refSeq = refSeq.getDatasetSequence();
68     }
69     length = _refSeq.getEnd() - _refSeq.getStart() + 1;
70 //    if (length!=refSeq.getLength() || _refSeq.getStart()!=1)
71     {
72       toSeq = new MapList(new int[] { _refSeq.getStart(), _refSeq.getEnd()}, new int[] { 0,length-1}, 1,1);
73     }
74   }
75
76   public T liftOver(SequenceI newRefSeq, Mapping sp2sq)
77   {
78     if (sp2sq.getMappedWidth() != sp2sq.getWidth())
79     {
80       // TODO: employ getWord/MappedWord to transfer annotation between cDNA and
81       // Protein reference frames
82       throw new Error(
83               "liftOver currently not implemented for transfer of annotation between different types of seqeunce");
84     }
85     boolean mapIsTo = (sp2sq != null) ? (sp2sq.getTo() == refSeq) : false;
86
87     /**
88      * map from matrix to toSeq's coordinate frame
89      */
90     int[] refMap = toSeq.locateInFrom(0, length - 1);
91     ArrayList<Integer> newFromMap = new ArrayList<Integer>();
92     int last = -1;
93     for (int i = 0; i < refMap.length; i += 2)
94     {
95       /*
96        * for each contiguous range in toSeq, locate corresponding range in sequence mapped to toSeq by sp2sq
97        */
98       int[] sp2map = mapIsTo
99               ? sp2sq.getMap().locateInFrom(refMap[i], refMap[i + 1])
100               : sp2sq.getMap().locateInTo(refMap[i], refMap[i + 1]);
101       if (sp2map == null)
102       {
103         continue;
104       }
105
106       for (int spm = 0; spm < sp2map.length; spm += 2)
107       {
108
109         if (last > -1)
110         {
111           if (sp2map[spm] != last + 1)
112           {
113             newFromMap.add(sp2map[spm]);
114           }
115           else
116           {
117             newFromMap.remove(newFromMap.size() - 1);
118           }
119         }
120         else
121         {
122           newFromMap.add(sp2map[spm]);
123         }
124         last = sp2map[spm + 1];
125         newFromMap.add(last);
126       }
127     }
128     if ((newFromMap.size() % 2) != 0)
129     {
130       // should have had an even number of int ranges!
131       throw new Error("PAEMatrix liftover failed.");
132     }
133     int fromIntMap[] = new int[newFromMap.size()];
134     int ipos = 0;
135     for (Integer i : newFromMap)
136     {
137       fromIntMap[ipos++] = i;
138     }
139     MapList newFromMapList = new MapList(fromIntMap,
140             new int[]
141             { 0, length - 1 }, 1, 1);
142
143     T newCM = newMappableContactMatrix(newRefSeq, newFromMapList);
144     return newCM;
145   }
146
147   protected abstract T  newMappableContactMatrix(SequenceI newRefSeq,
148           MapList newFromMapList);
149   @Override
150   public ContactListI getMappableContactList(final SequenceI localFrame,
151           final int column)
152   {
153     final int _column;
154     final int _lcolumn;
155     if (localFrame==null)
156     {
157       throw new Error("Unimplemented when no local sequence given.");
158     }
159     // return a ContactListI for column
160     // column is index into localFrame
161     // 1. map column to corresponding column in matrix
162
163     _lcolumn=localFrame.findPosition(column);
164     
165     if (toSeq != null)
166     {
167       SequenceI lf = localFrame, uf = refSeq;
168
169       // just look for dataset sequences and check they are the same.
170       // in future we could use DBRefMappings/whatever.
171       while (lf.getDatasetSequence() != null
172               || uf.getDatasetSequence() != null)
173       {
174         if (lf.getDatasetSequence() != null)
175         {
176           lf = lf.getDatasetSequence();
177         }
178         if (uf.getDatasetSequence() != null)
179         {
180           uf = uf.getDatasetSequence();
181         }
182       }
183       if (lf != uf)
184       {
185         // could try harder to find a mapping
186         throw new Error("This Matrix associated with '" + refSeq.getName()
187                 + "' is not mappable for the given localFrame sequence. ("
188                 + localFrame.getName() + ")");
189       }
190       // check the mapping to see if localFrame _lcolumn exists
191       int[] word = toSeq.locateInTo(_lcolumn, _lcolumn);
192       if (word == null)
193       {
194         return null;
195       }
196       _column = word[0];
197     }
198     else
199     {
200       // no mapping 
201       _column = _lcolumn;
202     }
203
204     // TODO - remove ? this may be a redundant check 
205     if (_column < 0 || ((toSeq != null && _column > toSeq.getToHighest())
206             || (toSeq == null && getHeight() <= _column)))
207     {
208       return null;
209     }
210
211     // 2. resolve ranges in matrix corresponding to range in localFrame
212     final int[] matrixRange = toSeq == null
213             ? new int[]
214             { localFrame.getStart(), localFrame.getEnd() }
215             : toSeq.locateInTo(localFrame.getStart(), localFrame.getEnd());
216
217     int h = 0;
218     for (int p = 0; p < matrixRange.length; p += 2)
219     {
220       h += 1+Math.abs(matrixRange[p + 1] - matrixRange[p]);
221     }
222     final int rangeHeight = h;
223     // 3. Construct ContactListImpl instance for just those segments.
224     
225     return new ContactListImpl(new ContactListProviderI()
226     {
227       
228       public int getColumn()
229       {
230         return column;
231       }
232       @Override
233       public int getPosition()
234       {
235         return _column;
236       }
237       
238       @Override
239       public int getContactHeight()
240       {
241         return rangeHeight;
242       }
243       
244       @Override
245       public double getContactAt(int mcolumn)
246       {
247         if (mcolumn<0 || mcolumn>=rangeHeight)
248         {
249           return -1;
250         }
251         return getElementAt(_column, locateInRange(mcolumn));
252         
253         // this code maps from mcolumn to localFrame - but that isn't what's needed
254 //        int loccolumn = localFrame.findPosition(mcolumn);
255 //        int[] lcolumn=(toSeq==null) ? new int[] {mcolumn} : toSeq.locateInTo(loccolumn,loccolumn);
256 //        if (lcolumn==null || lcolumn[0] < 0 || lcolumn[0] >= rangeHeight)
257 //        {
258 //          return -1;
259 //        }
260 //        return getElementAt(_column,lcolumn[0]);
261       }
262       /**
263        * @return the mcolumn'th position in the matrixRange window on the matrix
264        */
265       private int locateInRange(int mcolumn)
266       {
267
268         int h=0,p=0;
269         while (h < mcolumn && p+2 < matrixRange.length) 
270         {
271           h += 1+Math.abs(matrixRange[p + 1] - matrixRange[p]);
272           p+=2;
273         } 
274         return matrixRange[p]+mcolumn-h;
275       }
276       
277       @Override
278       public int[] getMappedPositionsFor(int cStart, int cEnd)
279       {
280         if (!hasReferenceSeq())
281         {
282           return ContactListProviderI.super.getMappedPositionsFor(cStart, cEnd);
283         }
284         // map into segment of matrix being shown
285         int realCstart = locateInRange(cStart);
286         int realCend = locateInRange(cEnd);
287         
288         // TODO account for discontinuities in the mapping
289
290         int[] mappedPositions = toSeq.locateInFrom(realCstart,realCend);
291         if (mappedPositions!=null) {
292           int s=-1,e=-1; 
293           for (int p=0;p<mappedPositions.length;p++)
294           {
295             if (s==-1 && mappedPositions[p]>=localFrame.getStart())
296             {
297               s=p; // remember first position within local frame
298             }
299             if (e==-1 || mappedPositions[p]<=localFrame.getEnd())
300             {
301               // update end pointer
302               e=p;
303               // compute local map
304               mappedPositions[p] = localFrame.findIndex(mappedPositions[p]);
305             }
306           }
307         }
308         return mappedPositions;
309       }
310     });
311   }
312
313   /**
314    * get a specific element of the contact matrix in its data-local coordinates
315    * rather than the mapped frame. Implementations are allowed to throw RunTimeExceptions if _column/i are out of bounds
316    * 
317    * @param _column
318    * @param i
319    * @return
320    */
321   protected abstract double getElementAt(int _column, int i);
322 }