2 * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
3 * Copyright (C) 2014 The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
11 * Jalview is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty
13 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
14 * PURPOSE. See the GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along with Jalview. If not, see <http://www.gnu.org/licenses/>.
17 * The Jalview Authors are detailed in the 'AUTHORS' file.
19 package jalview.datamodel;
21 import jalview.analysis.Rna;
22 import jalview.analysis.SecStrConsensus.SimpleBP;
24 import jalview.analysis.WUSSParseException;
26 import java.util.ArrayList;
27 import java.util.Enumeration;
28 import java.util.Hashtable;
30 import fr.orsay.lri.varna.models.rna.RNA;
38 public class AlignmentAnnotation
41 * If true, this annotations is calculated every edit, eg consensus, quality
42 * or conservation graphs
44 public boolean autoCalculated = false;
46 public String annotationId;
48 public SequenceI sequenceRef;
54 public String description;
57 public Annotation[] annotations;
61 public ArrayList<SimpleBP> bps=null;
63 * RNA secondary structure contact positions
65 public SequenceFeature[] _rnasecstr = null;
68 * position of annotation resulting in invalid WUSS parsing or -1
70 private long invalidrnastruc = -1;
73 * Updates the _rnasecstr field Determines the positions that base pair and
74 * the positions of helices based on secondary structure from a Stockholm file
78 private void _updateRnaSecStr(CharSequence RNAannot)
81 _rnasecstr = Rna.GetBasePairs(RNAannot);
82 bps = Rna.GetModeleBP(RNAannot);
85 catch (WUSSParseException px)
87 // DEBUG System.out.println(px);
88 invalidrnastruc=px.getProblemPos();
90 if (invalidrnastruc > -1)
94 Rna.HelixMap(_rnasecstr);
95 // setRNAStruc(RNAannot);
97 if (_rnasecstr != null && _rnasecstr.length > 0)
99 // show all the RNA secondary structure annotation symbols.
101 showAllColLabels = true;
102 scaleColLabel = true;
104 // System.out.println("featuregroup " + _rnasecstr[0].getFeatureGroup());
107 public java.util.Hashtable sequenceMapping;
110 public float graphMin;
113 public float graphMax;
116 * Score associated with label and description.
118 public double score = Double.NaN;
121 * flag indicating if annotation has a score.
123 public boolean hasScore = false;
125 public GraphLine threshold;
127 // Graphical hints and tips
129 /** Can this row be edited by the user ? */
130 public boolean editable = false;
132 /** Indicates if annotation has a graphical symbol track */
133 public boolean hasIcons; //
135 /** Indicates if annotation has a text character label */
136 public boolean hasText;
138 /** is the row visible */
139 public boolean visible = true;
141 public int graphGroup = -1;
143 /** Displayed height of row in pixels */
144 public int height = 0;
146 public int graph = 0;
148 public int graphHeight = 40;
150 public boolean padGaps = false;
152 public static final int NO_GRAPH = 0;
154 public static final int BAR_GRAPH = 1;
156 public static final int LINE_GRAPH = 2;
158 public boolean belowAlignment = true;
160 public SequenceGroup groupRef = null;
163 * display every column label, even if there is a row of identical labels
165 public boolean showAllColLabels = false;
168 * scale the column label to fit within the alignment column.
170 public boolean scaleColLabel = false;
173 * centre the column labels relative to the alignment column
175 public boolean centreColLabels = false;
177 private boolean isrna;
182 * @see java.lang.Object#finalize()
184 protected void finalize() throws Throwable
191 public static int getGraphValueFromString(String string)
193 if (string.equalsIgnoreCase("BAR_GRAPH"))
197 else if (string.equalsIgnoreCase("LINE_GRAPH"))
206 // JBPNote: what does this do ?
207 public void ConcenStru(CharSequence RNAannot) throws WUSSParseException
209 bps = Rna.GetModeleBP(RNAannot);
212 * Creates a new AlignmentAnnotation object.
215 * short label shown under sequence labels
217 * text displayed on mouseover
219 * set of positional annotation elements
221 public AlignmentAnnotation(String label, String description,
222 Annotation[] annotations)
227 this.description = description;
228 this.annotations = annotations;
230 validateRangeAndDisplay();
234 * Checks if annotation labels represent secondary structures
237 void areLabelsSecondaryStructure()
239 boolean nonSSLabel = false;
241 StringBuffer rnastring = new StringBuffer();
244 for (int i = 0; i < annotations.length; i++)
246 if (annotations[i] == null)
250 if (annotations[i].secondaryStructure == 'H'
251 || annotations[i].secondaryStructure == 'E')
256 // Check for RNA secondary structure
258 //System.out.println(annotations[i].secondaryStructure);
259 if (annotations[i].secondaryStructure == '('
260 || annotations[i].secondaryStructure == '['
261 || annotations[i].secondaryStructure == '<'
262 || annotations[i].secondaryStructure == '{'
263 || annotations[i].secondaryStructure == 'A'
264 || annotations[i].secondaryStructure == 'B'
265 || annotations[i].secondaryStructure == 'C'
266 || annotations[i].secondaryStructure == 'D'
267 || annotations[i].secondaryStructure == 'E'
268 || annotations[i].secondaryStructure == 'F'
269 || annotations[i].secondaryStructure == 'G'
270 || annotations[i].secondaryStructure == 'H'
271 || annotations[i].secondaryStructure == 'I'
272 || annotations[i].secondaryStructure == 'J'
273 || annotations[i].secondaryStructure == 'K'
274 || annotations[i].secondaryStructure == 'L'
275 || annotations[i].secondaryStructure == 'M'
276 || annotations[i].secondaryStructure == 'N'
277 || annotations[i].secondaryStructure == 'O'
278 || annotations[i].secondaryStructure == 'P'
279 || annotations[i].secondaryStructure == 'Q'
280 || annotations[i].secondaryStructure == 'R'
281 || annotations[i].secondaryStructure == 'S'
282 || annotations[i].secondaryStructure == 'T'
283 || annotations[i].secondaryStructure == 'U'
284 || annotations[i].secondaryStructure == 'V'
285 || annotations[i].secondaryStructure == 'W'
286 || annotations[i].secondaryStructure == 'X'
287 || annotations[i].secondaryStructure == 'Y'
288 || annotations[i].secondaryStructure == 'Z')
295 // System.out.println("displaychar " + annotations[i].displayCharacter);
297 if (annotations[i].displayCharacter == null
298 || annotations[i].displayCharacter.length() == 0)
300 rnastring.append('.');
303 if (annotations[i].displayCharacter.length() == 1)
305 firstChar = annotations[i].displayCharacter.charAt(0);
306 // check to see if it looks like a sequence or is secondary structure
308 if (annotations[i].secondaryStructure != ' '
311 // Uncomment to only catch case where
312 // displayCharacter==secondary
314 // to correctly redisplay SS annotation imported from Stockholm,
315 // exported to JalviewXML and read back in again.
317 // annotations[i].displayCharacter.charAt(0)==annotations[i].secondaryStructure
320 && firstChar != '�' // JBPNote should explicitly express as unicode number to avoid source code translation problems
352 && firstChar < jalview.schemes.ResidueProperties.aaIndex.length)
354 if (jalview.schemes.ResidueProperties.aaIndex[firstChar] < 23) // TODO:
367 rnastring.append(annotations[i].displayCharacter.charAt(1));
370 if (annotations[i].displayCharacter.length() > 0)
379 for (int j = 0; j < annotations.length; j++)
381 if (annotations[j] != null
382 && annotations[j].secondaryStructure != ' ')
384 annotations[j].displayCharacter = String
385 .valueOf(annotations[j].secondaryStructure);
386 annotations[j].secondaryStructure = ' ';
395 _updateRnaSecStr(new AnnotCharSequence());
399 annotationId = this.hashCode() + "";
403 * flyweight access to positions in the alignment annotation row for RNA
409 private class AnnotCharSequence implements CharSequence
415 public AnnotCharSequence()
417 this(0, annotations.length);
420 public AnnotCharSequence(int start, int end)
427 public CharSequence subSequence(int start, int end)
429 return new AnnotCharSequence(offset + start, offset + end);
439 public char charAt(int index)
442 return ((index + offset < 0) || (index + offset) >= max
443 || annotations[index + offset] == null || (dc = annotations[index
444 + offset].displayCharacter.trim()).length() < 1) ? '.' : dc
448 public String toString()
450 char[] string = new char[max - offset];
451 int mx = annotations.length;
453 for (int i = offset; i < mx; i++)
456 string[i] = (annotations[i] == null || (dc = annotations[i].displayCharacter
457 .trim()).length() < 1) ? '.' : dc.charAt(0);
459 return new String(string);
463 private long _lastrnaannot = -1;
465 public String getRNAStruc()
469 String rnastruc = new AnnotCharSequence().toString();
470 if (_lastrnaannot != rnastruc.hashCode())
472 // ensure rna structure contacts are up to date
473 _lastrnaannot = rnastruc.hashCode();
474 _updateRnaSecStr(rnastruc);
482 * Creates a new AlignmentAnnotation object.
497 public AlignmentAnnotation(String label, String description,
498 Annotation[] annotations, float min, float max, int graphType)
500 // graphs are not editable
501 editable = graphType == 0;
504 this.description = description;
505 this.annotations = annotations;
509 validateRangeAndDisplay();
513 * checks graphMin and graphMax, secondary structure symbols, sets graphType
514 * appropriately, sets null labels to the empty string if appropriate.
516 public void validateRangeAndDisplay()
519 if (annotations == null)
521 visible = false; // try to prevent renderer from displaying.
522 return; // this is a non-annotation row annotation - ie a sequence score.
525 int graphType = graph;
526 float min = graphMin;
527 float max = graphMax;
528 boolean drawValues = true;
533 for (int i = 0; i < annotations.length; i++)
535 if (annotations[i] == null)
540 if (drawValues && annotations[i].displayCharacter != null
541 && annotations[i].displayCharacter.length() > 1)
546 if (annotations[i].value > max)
548 max = annotations[i].value;
551 if (annotations[i].value < min)
553 min = annotations[i].value;
555 if (_linecolour == null && annotations[i].colour != null)
557 _linecolour = annotations[i].colour;
560 // ensure zero is origin for min/max ranges on only one side of zero
577 areLabelsSecondaryStructure();
579 if (!drawValues && graphType != NO_GRAPH)
581 for (int i = 0; i < annotations.length; i++)
583 if (annotations[i] != null)
585 annotations[i].displayCharacter = "X";
592 * Copy constructor creates a new independent annotation row with the same
593 * associated sequenceRef
597 public AlignmentAnnotation(AlignmentAnnotation annotation)
599 this.label = new String(annotation.label);
600 if (annotation.description != null)
601 this.description = new String(annotation.description);
602 this.graphMin = annotation.graphMin;
603 this.graphMax = annotation.graphMax;
604 this.graph = annotation.graph;
605 this.graphHeight = annotation.graphHeight;
606 this.graphGroup = annotation.graphGroup;
607 this.groupRef = annotation.groupRef;
608 this.editable = annotation.editable;
609 this.autoCalculated = annotation.autoCalculated;
610 this.hasIcons = annotation.hasIcons;
611 this.hasText = annotation.hasText;
612 this.height = annotation.height;
613 this.label = annotation.label;
614 this.padGaps = annotation.padGaps;
615 this.visible = annotation.visible;
616 this.centreColLabels = annotation.centreColLabels;
617 this.scaleColLabel = annotation.scaleColLabel;
618 this.showAllColLabels = annotation.showAllColLabels;
619 this.calcId = annotation.calcId;
620 if (this.hasScore = annotation.hasScore)
622 this.score = annotation.score;
624 if (annotation.threshold != null)
626 threshold = new GraphLine(annotation.threshold);
628 if (annotation.annotations != null)
630 Annotation[] ann = annotation.annotations;
631 this.annotations = new Annotation[ann.length];
632 for (int i = 0; i < ann.length; i++)
636 annotations[i] = new Annotation(ann[i]);
637 if (_linecolour != null)
639 _linecolour = annotations[i].colour;
644 if (annotation.sequenceRef != null)
646 this.sequenceRef = annotation.sequenceRef;
647 if (annotation.sequenceMapping != null)
650 sequenceMapping = new Hashtable();
651 Enumeration pos = annotation.sequenceMapping.keys();
652 while (pos.hasMoreElements())
654 // could optimise this!
655 p = (Integer) pos.nextElement();
656 Annotation a = (Annotation) annotation.sequenceMapping.get(p);
661 for (int i = 0; i < ann.length; i++)
665 sequenceMapping.put(p, annotations[i]);
672 this.sequenceMapping = null;
676 // TODO: check if we need to do this: JAL-952
677 // if (this.isrna=annotation.isrna)
679 // _rnasecstr=new SequenceFeature[annotation._rnasecstr];
681 validateRangeAndDisplay(); // construct hashcodes, etc.
685 * clip the annotation to the columns given by startRes and endRes (inclusive)
686 * and prune any existing sequenceMapping to just those columns.
691 public void restrict(int startRes, int endRes)
693 if (annotations == null)
700 if (startRes >= annotations.length)
701 startRes = annotations.length - 1;
702 if (endRes >= annotations.length)
703 endRes = annotations.length - 1;
704 if (annotations == null)
706 Annotation[] temp = new Annotation[endRes - startRes + 1];
707 if (startRes < annotations.length)
709 System.arraycopy(annotations, startRes, temp, 0, endRes - startRes
712 if (sequenceRef != null)
714 // Clip the mapping, if it exists.
715 int spos = sequenceRef.findPosition(startRes);
716 int epos = sequenceRef.findPosition(endRes);
717 if (sequenceMapping != null)
719 Hashtable newmapping = new Hashtable();
720 Enumeration e = sequenceMapping.keys();
721 while (e.hasMoreElements())
723 Integer pos = (Integer) e.nextElement();
724 if (pos.intValue() >= spos && pos.intValue() <= epos)
726 newmapping.put(pos, sequenceMapping.get(pos));
729 sequenceMapping.clear();
730 sequenceMapping = newmapping;
737 * set the annotation row to be at least length Annotations
740 * minimum number of columns required in the annotation row
741 * @return false if the annotation row is greater than length
743 public boolean padAnnotation(int length)
745 if (annotations == null)
747 return true; // annotation row is correct - null == not visible and
750 if (annotations.length < length)
752 Annotation[] na = new Annotation[length];
753 System.arraycopy(annotations, 0, na, 0, annotations.length);
757 return annotations.length > length;
764 * @return DOCUMENT ME!
766 public String toString()
768 StringBuffer buffer = new StringBuffer();
770 for (int i = 0; i < annotations.length; i++)
772 if (annotations[i] != null)
776 buffer.append(annotations[i].value);
780 buffer.append(annotations[i].secondaryStructure);
784 buffer.append(annotations[i].displayCharacter);
790 // TODO: remove disgusting hack for 'special' treatment of consensus line.
791 if (label.indexOf("Consensus") == 0)
795 for (int i = 0; i < annotations.length; i++)
797 if (annotations[i] != null)
799 buffer.append(annotations[i].description);
806 return buffer.toString();
809 public void setThreshold(GraphLine line)
814 public GraphLine getThreshold()
820 * Attach the annotation to seqRef, starting from startRes position. If
821 * alreadyMapped is true then the indices of the annotation[] array are
822 * sequence positions rather than alignment column positions.
826 * @param alreadyMapped
828 public void createSequenceMapping(SequenceI seqRef, int startRes,
829 boolean alreadyMapped)
836 sequenceRef = seqRef;
837 if (annotations == null)
841 sequenceMapping = new java.util.Hashtable();
845 for (int i = 0; i < annotations.length; i++)
847 if (annotations[i] != null)
851 seqPos = seqRef.findPosition(i);
855 seqPos = i + startRes;
858 sequenceMapping.put(new Integer(seqPos), annotations[i]);
864 public void adjustForAlignment()
866 if (sequenceRef == null)
869 if (annotations == null)
874 int a = 0, aSize = sequenceRef.getLength();
883 Annotation[] temp = new Annotation[aSize];
886 for (a = sequenceRef.getStart(); a <= sequenceRef.getEnd(); a++)
888 index = new Integer(a);
889 if (sequenceMapping.containsKey(index))
891 position = sequenceRef.findIndex(a) - 1;
893 temp[position] = (Annotation) sequenceMapping.get(index);
901 * remove any null entries in annotation row and return the number of non-null
902 * annotation elements.
906 public int compactAnnotationArray()
908 int i = 0, iSize = annotations.length;
911 if (annotations[i] == null)
914 System.arraycopy(annotations, i + 1, annotations, i, iSize - i
923 Annotation[] ann = annotations;
924 annotations = new Annotation[i];
925 System.arraycopy(ann, 0, annotations, 0, i);
931 * Associate this annotion with the aligned residues of a particular sequence.
932 * sequenceMapping will be updated in the following way: null sequenceI -
933 * existing mapping will be discarded but annotations left in mapped
934 * positions. valid sequenceI not equal to current sequenceRef: mapping is
935 * discarded and rebuilt assuming 1:1 correspondence TODO: overload with
936 * parameter to specify correspondence between current and new sequenceRef
940 public void setSequenceRef(SequenceI sequenceI)
942 if (sequenceI != null)
944 if (sequenceRef != null)
946 if (sequenceRef != sequenceI
947 && !sequenceRef.equals(sequenceI)
948 && sequenceRef.getDatasetSequence() != sequenceI
949 .getDatasetSequence())
951 // if sequenceRef isn't intersecting with sequenceI
952 // throw away old mapping and reconstruct.
954 if (sequenceMapping != null)
956 sequenceMapping = null;
957 // compactAnnotationArray();
959 createSequenceMapping(sequenceI, 1, true);
960 adjustForAlignment();
964 // Mapping carried over
965 sequenceRef = sequenceI;
971 createSequenceMapping(sequenceI, 1, true);
972 adjustForAlignment();
977 // throw away the mapping without compacting.
978 sequenceMapping = null;
986 public double getScore()
995 public void setScore(double score)
1003 * @return true if annotation has an associated score
1005 public boolean hasScore()
1007 return hasScore || !Double.isNaN(score);
1011 * Score only annotation
1014 * @param description
1017 public AlignmentAnnotation(String label, String description, double score)
1019 this(label, description, null);
1024 * copy constructor with edit based on the hidden columns marked in colSel
1026 * @param alignmentAnnotation
1029 public AlignmentAnnotation(AlignmentAnnotation alignmentAnnotation,
1030 ColumnSelection colSel)
1032 this(alignmentAnnotation);
1033 if (annotations == null)
1037 colSel.makeVisibleAnnotation(this);
1040 public void setPadGaps(boolean padgaps, char gapchar)
1042 this.padGaps = padgaps;
1046 for (int i = 0; i < annotations.length; i++)
1048 if (annotations[i] == null)
1049 annotations[i] = new Annotation(String.valueOf(gapchar), null,
1051 else if (annotations[i].displayCharacter == null
1052 || annotations[i].displayCharacter.equals(" "))
1053 annotations[i].displayCharacter = String.valueOf(gapchar);
1059 * format description string for display
1062 * @return Get the annotation description string optionally prefixed by
1063 * associated sequence name (if any)
1065 public String getDescription(boolean seqname)
1067 if (seqname && this.sequenceRef != null)
1069 int i = description.toLowerCase().indexOf("<html>");
1072 // move the html tag to before the sequence reference.
1073 return "<html>" + sequenceRef.getName() + " : "
1074 + description.substring(i + 6);
1076 return sequenceRef.getName() + " : " + description;
1081 public boolean isValidStruc()
1083 return invalidrnastruc == -1;
1086 public long getInvalidStrucPos()
1088 return invalidrnastruc;
1092 * machine readable ID string indicating what generated this annotation
1094 protected String calcId = "";
1097 * base colour for line graphs. If null, will be set automatically by
1098 * searching the alignment annotation
1100 public java.awt.Color _linecolour;
1102 public String getCalcId()
1107 public void setCalcId(String calcId)
1109 this.calcId = calcId;