Check values not null in copy constructor
[jalview.git] / src / jalview / datamodel / AlignmentAnnotation.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer
3  * Copyright (C) 2007 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
18  */
19 package jalview.datamodel;
20
21 import java.util.Enumeration;
22 import java.util.Hashtable;
23 import java.util.Vector;
24
25 /**
26  * DOCUMENT ME!
27  *
28  * @author $author$
29  * @version $Revision$
30  */
31 public class AlignmentAnnotation
32 {
33   /** If true, this annotations is calculated every edit,
34    * eg consensus, quality or conservation graphs */
35   public boolean autoCalculated = false;
36
37   public String annotationId;
38
39   public SequenceI sequenceRef;
40
41   /** DOCUMENT ME!! */
42   public String label;
43
44   /** DOCUMENT ME!! */
45   public String description;
46
47   /** DOCUMENT ME!! */
48   public Annotation[] annotations;
49
50   public java.util.Hashtable sequenceMapping;
51
52   /** DOCUMENT ME!! */
53   public float graphMin;
54
55   /** DOCUMENT ME!! */
56   public float graphMax;
57
58   public GraphLine threshold;
59
60   // Graphical hints and tips
61
62   /** DOCUMENT ME!! */
63   public boolean editable = false;
64
65   /** DOCUMENT ME!! */
66   public boolean hasIcons; //
67
68   /** DOCUMENT ME!! */
69   public boolean hasText;
70
71   /** DOCUMENT ME!! */
72   public boolean visible = true;
73
74   public int graphGroup = -1;
75
76   /** DOCUMENT ME!! */
77   public int height = 0;
78
79   public int graph = 0;
80
81   public int graphHeight = 40;
82
83   public static final int NO_GRAPH = 0;
84
85   public static final int BAR_GRAPH = 1;
86
87   public static final int LINE_GRAPH = 2;
88
89   public static int getGraphValueFromString(String string)
90   {
91     if (string.equalsIgnoreCase("BAR_GRAPH"))
92     {
93       return BAR_GRAPH;
94     }
95     else if (string.equalsIgnoreCase("LINE_GRAPH"))
96     {
97       return LINE_GRAPH;
98     }
99     else
100     {
101       return NO_GRAPH;
102     }
103   }
104
105   /**
106    * Creates a new AlignmentAnnotation object.
107    *
108    * @param label DOCUMENT ME!
109    * @param description DOCUMENT ME!
110    * @param annotations DOCUMENT ME!
111    */
112   public AlignmentAnnotation(String label, String description,
113                              Annotation[] annotations)
114   {
115     // always editable?
116     editable = true;
117     this.label = label;
118     this.description = description;
119     this.annotations = annotations;
120
121     areLabelsSecondaryStructure();
122   }
123
124   void areLabelsSecondaryStructure()
125   {
126     boolean nonSSLabel = false;
127     for (int i = 0; i < annotations.length; i++)
128     {
129       if (annotations[i] == null)
130       {
131         continue;
132       }
133
134       if (annotations[i].secondaryStructure == 'H' ||
135           annotations[i].secondaryStructure == 'E')
136       {
137         hasIcons = true;
138       }
139
140       if (annotations[i].displayCharacter.length() == 1
141           && !annotations[i].displayCharacter.equals("H")
142           && !annotations[i].displayCharacter.equals("E")
143           && !annotations[i].displayCharacter.equals("-")
144           && !annotations[i].displayCharacter.equals("."))
145       {
146         if (jalview.schemes.ResidueProperties.aaIndex
147             [annotations[i].displayCharacter.charAt(0)] < 23)
148         {
149           nonSSLabel = true;
150         }
151       }
152
153       if (annotations[i].displayCharacter.length() > 0)
154       {
155         hasText = true;
156       }
157     }
158
159     if (nonSSLabel)
160     {
161       hasIcons = false;
162       for (int j = 0; j < annotations.length; j++)
163       {
164         if (annotations[j] != null && annotations[j].secondaryStructure != ' ')
165         {
166           annotations[j].displayCharacter
167               = String.valueOf(annotations[j].secondaryStructure);
168           annotations[j].secondaryStructure = ' ';
169         }
170
171       }
172
173     }
174
175     annotationId = this.hashCode() + "";
176   }
177   /**
178    * Creates a new AlignmentAnnotation object.
179    *
180    * @param label DOCUMENT ME!
181    * @param description DOCUMENT ME!
182    * @param annotations DOCUMENT ME!
183    * @param min DOCUMENT ME!
184    * @param max DOCUMENT ME!
185    * @param winLength DOCUMENT ME!
186    */
187   public AlignmentAnnotation(String label, String description,
188                              Annotation[] annotations, float min, float max,
189                              int graphType)
190   {
191     // graphs are not editable
192     editable = graphType==0;
193
194     this.label = label;
195     this.description = description;
196     this.annotations = annotations;
197     graph = graphType;
198     graphMin = min;
199     graphMax = max;
200     validateRangeAndDisplay();
201   }
202   /**
203    * checks graphMin and graphMax,
204    * secondary structure symbols,
205    * sets graphType appropriately,
206    * sets null labels to the empty string
207    * if appropriate.
208    */
209   private void validateRangeAndDisplay() {
210     int graphType = graph;
211     float min = graphMin;
212     float max = graphMax;
213     boolean drawValues = true;
214
215     if (min == max)
216     {
217       min = 999999999;
218       for (int i = 0; i < annotations.length; i++)
219       {
220         if (annotations[i] == null)
221         {
222           continue;
223         }
224
225         if (drawValues && annotations[i].displayCharacter.length() > 1)
226         {
227           drawValues = false;
228         }
229
230         if (annotations[i].value > max)
231         {
232           max = annotations[i].value;
233         }
234
235         if (annotations[i].value < min)
236         {
237           min = annotations[i].value;
238         }
239       }
240     }
241
242     graphMin = min;
243     graphMax = max;
244
245     areLabelsSecondaryStructure();
246
247     if (!drawValues && graphType != NO_GRAPH)
248     {
249       for (int i = 0; i < annotations.length; i++)
250       {
251         if (annotations[i] != null)
252         {
253           annotations[i].displayCharacter = "";
254         }
255       }
256     }
257   }
258
259   /**
260    * Copy constructor
261    * creates a new independent annotation row with the same associated sequenceRef
262    * @param annotation
263    */
264   public AlignmentAnnotation(AlignmentAnnotation annotation)
265   {
266     this.label = new String(annotation.label);
267     if (annotation.description != null)
268       this.description = new String(annotation.description);
269     this.graphMin = annotation.graphMin;
270     this.graphMax = annotation.graphMax;
271     this.graph = annotation.graph;
272     this.graphHeight = annotation.graphHeight;
273     this.graphGroup = annotation.graphGroup;
274     this.editable = annotation.editable;
275     this.autoCalculated = annotation.autoCalculated;
276     this.hasIcons = annotation.hasIcons;
277     this.hasText = annotation.hasText;
278     this.height = annotation.height;
279     this.label = annotation.label;
280     if (threshold!=null) {
281       threshold = new GraphLine(annotation.threshold);
282     }
283     if (annotation.annotations!=null) {
284       Vector anvec = new Vector();
285       Annotation[] ann = annotation.annotations;
286       this.annotations = new Annotation[ann.length];
287       for (int i=0; i<ann.length; i++) {
288         annotations[i] = new Annotation(ann[i]);
289         anvec.addElement(ann[i]); // for lookup if sequenceMapping exists.
290       };
291       if (annotation.sequenceRef!=null) {
292         this.sequenceRef = annotation.sequenceRef;
293         if (annotation.sequenceMapping!=null)
294         {
295           sequenceMapping = new Hashtable();
296           Enumeration pos=annotation.sequenceMapping.keys();
297           while (pos.hasMoreElements()) {
298             Integer p = (Integer) pos.nextElement();
299             Annotation a = (Annotation) sequenceMapping.get(p);
300             sequenceMapping.put(p, annotations[anvec.indexOf(a)]);
301           }
302           anvec.removeAllElements();
303         } else {
304           this.sequenceMapping = null;
305         }
306       }
307     }
308     validateRangeAndDisplay(); // construct hashcodes, etc.
309   }
310
311   /**
312    * DOCUMENT ME!
313    *
314    * @return DOCUMENT ME!
315    */
316   public String toString()
317   {
318     StringBuffer buffer = new StringBuffer();
319
320     for (int i = 0; i < annotations.length; i++)
321     {
322       if (annotations[i] != null)
323       {
324         if (graph != 0)
325         {
326           buffer.append(annotations[i].value);
327         }
328         else if (hasIcons)
329         {
330           buffer.append(annotations[i].secondaryStructure);
331         }
332         else
333         {
334           buffer.append(annotations[i].displayCharacter);
335         }
336       }
337
338       buffer.append(", ");
339     }
340
341     if (label.equals("Consensus"))
342     {
343       buffer.append("\n");
344
345       for (int i = 0; i < annotations.length; i++)
346       {
347         if (annotations[i] != null)
348         {
349           buffer.append(annotations[i].description);
350         }
351
352         buffer.append(", ");
353       }
354     }
355
356     return buffer.toString();
357   }
358
359   public void setThreshold(GraphLine line)
360   {
361     threshold = line;
362   }
363
364   public GraphLine getThreshold()
365   {
366     return threshold;
367   }
368
369   /**
370    * Attach the annotation to seqRef, starting from startRes position. If alreadyMapped is true then the indices of the annotation[] array are sequence positions rather than alignment column positions.
371    * @param seqRef
372    * @param startRes
373    * @param alreadyMapped
374    */
375   public void createSequenceMapping(SequenceI seqRef,
376                                     int startRes,
377                                     boolean alreadyMapped)
378   {
379
380     if (seqRef == null)
381     {
382       return;
383     }
384
385     sequenceMapping = new java.util.Hashtable();
386
387     sequenceRef = seqRef;
388     int seqPos;
389
390     for (int i = 0; i < annotations.length; i++)
391     {
392       if (annotations[i] != null)
393       {
394         if (alreadyMapped)
395         {
396           seqPos = seqRef.findPosition(i);
397         }
398         else
399         {
400           seqPos = i + startRes;
401         }
402
403         sequenceMapping.put(new Integer(seqPos), annotations[i]);
404       }
405     }
406
407   }
408
409   public void adjustForAlignment()
410   {
411     if (sequenceRef==null)
412       return;
413
414     int a = 0, aSize = sequenceRef.getLength();
415
416     if (aSize == 0)
417     {
418       //Its been deleted
419       return;
420     }
421
422     int position;
423     Annotation[] temp = new Annotation[aSize];
424     Integer index;
425
426     for (a = sequenceRef.getStart(); a <= sequenceRef.getEnd(); a++)
427     {
428       index = new Integer(a);
429       if (sequenceMapping.containsKey(index))
430       {
431         position = sequenceRef.findIndex(a) - 1;
432
433         temp[position] = (Annotation) sequenceMapping.get(index);
434       }
435     }
436
437     annotations = temp;
438   }
439   /**
440    * remove any null entries in annotation row and return the
441    * number of non-null annotation elements.
442    * @return
443    */
444   private int compactAnnotationArray() {
445     int j=0;
446     for (int i=0;i<annotations.length; i++) {
447       if (annotations[i]!=null && j!=i) {
448         annotations[j++] = annotations[i];
449       }
450     }
451     Annotation[] ann = annotations;
452     annotations = new Annotation[j];
453     System.arraycopy(ann, 0, annotations, 0, j);
454     ann = null;
455     return j;
456   }
457
458   /**
459    * Associate this annotion with the aligned residues of a particular sequence.
460    * sequenceMapping will be updated in the following way:
461    *   null sequenceI - existing mapping will be discarded but annotations left in mapped positions.
462    *   valid sequenceI not equal to current sequenceRef: mapping is discarded and rebuilt assuming 1:1 correspondence
463    *   TODO: overload with parameter to specify correspondence between current and new sequenceRef
464    * @param sequenceI
465    */
466   public void setSequenceRef(SequenceI sequenceI)
467   {
468     if (sequenceI!=null) {
469       if (sequenceRef!=null) {
470         if (sequenceRef!=sequenceI && !sequenceRef.equals(sequenceI)) {
471           // throw away old mapping and reconstruct.
472           sequenceRef=null;
473           if (sequenceMapping!=null)
474           {
475             sequenceMapping=null;
476             // compactAnnotationArray();
477           }
478           createSequenceMapping(sequenceI, 1,true);
479           adjustForAlignment();
480         } else {
481           // Mapping carried over
482           sequenceRef = sequenceI;
483         }
484       } else {
485         // No mapping exists
486         createSequenceMapping(sequenceI, 1, true);
487         adjustForAlignment();
488       }
489     } else {
490       // throw away the mapping without compacting.
491       sequenceMapping=null;
492       sequenceRef = null;
493     }
494   }
495 }