JAL-674 JAL-961 properties to stash additional information for an alignment annotatio...
[jalview.git] / src / jalview / datamodel / AlignmentAnnotation.java
index 1376b11..78f64ac 100755 (executable)
@@ -1,33 +1,35 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.0b1)
+ * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
  * Copyright (C) 2014 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
  * Jalview is free software: you can redistribute it and/or
  * modify it under the terms of the GNU General Public License 
- * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
  *  
  * Jalview is distributed in the hope that it will be useful, but 
  * WITHOUT ANY WARRANTY; without even the implied warranty 
  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
  * PURPOSE.  See the GNU General Public License for more details.
  * 
- * You should have received a copy of the GNU General Public License along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
  * The Jalview Authors are detailed in the 'AUTHORS' file.
  */
 package jalview.datamodel;
 
 import jalview.analysis.Rna;
 import jalview.analysis.SecStrConsensus.SimpleBP;
-
 import jalview.analysis.WUSSParseException;
 
 import java.util.ArrayList;
 import java.util.Enumeration;
+import java.util.HashMap;
 import java.util.Hashtable;
-
-import fr.orsay.lri.varna.models.rna.RNA;
+import java.util.Map;
+import java.util.Map.Entry;
 
 /**
  * DOCUMENT ME!
@@ -55,10 +57,9 @@ public class AlignmentAnnotation
 
   /** DOCUMENT ME!! */
   public Annotation[] annotations;
-  
-  
 
-  public ArrayList<SimpleBP> bps=null;
+  public ArrayList<SimpleBP> bps = null;
+
   /**
    * RNA secondary structure contact positions
    */
@@ -77,15 +78,15 @@ public class AlignmentAnnotation
    */
   private void _updateRnaSecStr(CharSequence RNAannot)
   {
-    try {
-    _rnasecstr = Rna.GetBasePairs(RNAannot);
-    bps = Rna.GetModeleBP(RNAannot);
-    invalidrnastruc=-1;
-    }
-    catch (WUSSParseException px)
+    try
+    {
+      _rnasecstr = Rna.GetBasePairs(RNAannot);
+      bps = Rna.GetModeleBP(RNAannot);
+      invalidrnastruc = -1;
+    } catch (WUSSParseException px)
     {
       // DEBUG System.out.println(px);
-      invalidrnastruc=px.getProblemPos();
+      invalidrnastruc = px.getProblemPos();
     }
     if (invalidrnastruc > -1)
     {
@@ -104,7 +105,7 @@ public class AlignmentAnnotation
     // System.out.println("featuregroup " + _rnasecstr[0].getFeatureGroup());
   }
 
-  public java.util.Hashtable sequenceMapping;
+  public java.util.Hashtable<Integer, Annotation> sequenceMapping;
 
   /** DOCUMENT ME!! */
   public float graphMin;
@@ -203,11 +204,13 @@ public class AlignmentAnnotation
       return NO_GRAPH;
     }
   }
-    // JBPNote: what does this do ?
+
+  // JBPNote: what does this do ?
   public void ConcenStru(CharSequence RNAannot) throws WUSSParseException
   {
-         bps = Rna.GetModeleBP(RNAannot);
+    bps = Rna.GetModeleBP(RNAannot);
   }
+
   /**
    * Creates a new AlignmentAnnotation object.
    * 
@@ -255,37 +258,39 @@ public class AlignmentAnnotation
       else
       // Check for RNA secondary structure
       {
-         //System.out.println(annotations[i].secondaryStructure);
+        // System.out.println(annotations[i].secondaryStructure);
+        // TODO: 2.8.2 should this ss symbol validation check be a function in
+        // RNA/ResidueProperties ?
         if (annotations[i].secondaryStructure == '('
-                       || annotations[i].secondaryStructure == '['
-                       || annotations[i].secondaryStructure == '<'
-                       || annotations[i].secondaryStructure == '{'
-                       || annotations[i].secondaryStructure == 'A'
-                       || annotations[i].secondaryStructure == 'B'
-                       || annotations[i].secondaryStructure == 'C'
-                       || annotations[i].secondaryStructure == 'D'
-                       || annotations[i].secondaryStructure == 'E'
-                       || annotations[i].secondaryStructure == 'F'
-                       || annotations[i].secondaryStructure == 'G'
-                       || annotations[i].secondaryStructure == 'H'
-                       || annotations[i].secondaryStructure == 'I'
-                       || annotations[i].secondaryStructure == 'J'
-                       || annotations[i].secondaryStructure == 'K'
-                       || annotations[i].secondaryStructure == 'L'
-                       || annotations[i].secondaryStructure == 'M'
-                       || annotations[i].secondaryStructure == 'N'
-                       || annotations[i].secondaryStructure == 'O'
-                       || annotations[i].secondaryStructure == 'P'
-                       || annotations[i].secondaryStructure == 'Q'
-                       || annotations[i].secondaryStructure == 'R'
-                       || annotations[i].secondaryStructure == 'S'
-                       || annotations[i].secondaryStructure == 'T'
-                       || annotations[i].secondaryStructure == 'U'
-                       || annotations[i].secondaryStructure == 'V'
-                       || annotations[i].secondaryStructure == 'W'
-                       || annotations[i].secondaryStructure == 'X'
-                       || annotations[i].secondaryStructure == 'Y'
-                       || annotations[i].secondaryStructure == 'Z')
+                || annotations[i].secondaryStructure == '['
+                || annotations[i].secondaryStructure == '<'
+                || annotations[i].secondaryStructure == '{'
+                || annotations[i].secondaryStructure == 'A'
+                || annotations[i].secondaryStructure == 'B'
+                || annotations[i].secondaryStructure == 'C'
+                || annotations[i].secondaryStructure == 'D'
+                || annotations[i].secondaryStructure == 'E'
+                || annotations[i].secondaryStructure == 'F'
+                || annotations[i].secondaryStructure == 'G'
+                || annotations[i].secondaryStructure == 'H'
+                || annotations[i].secondaryStructure == 'I'
+                || annotations[i].secondaryStructure == 'J'
+                || annotations[i].secondaryStructure == 'K'
+                || annotations[i].secondaryStructure == 'L'
+                || annotations[i].secondaryStructure == 'M'
+                || annotations[i].secondaryStructure == 'N'
+                || annotations[i].secondaryStructure == 'O'
+                || annotations[i].secondaryStructure == 'P'
+                || annotations[i].secondaryStructure == 'Q'
+                || annotations[i].secondaryStructure == 'R'
+                || annotations[i].secondaryStructure == 'S'
+                || annotations[i].secondaryStructure == 'T'
+                || annotations[i].secondaryStructure == 'U'
+                || annotations[i].secondaryStructure == 'V'
+                || annotations[i].secondaryStructure == 'W'
+                || annotations[i].secondaryStructure == 'X'
+                || annotations[i].secondaryStructure == 'Y'
+                || annotations[i].secondaryStructure == 'Z')
         {
           hasIcons |= true;
           isrna |= true;
@@ -317,7 +322,7 @@ public class AlignmentAnnotation
                 // annotations[i].displayCharacter.charAt(0)==annotations[i].secondaryStructure
                 firstChar != ' '
                 && firstChar != '$'
-                && firstChar != 'ยต' // JBPNote should explicitly express as unicode number to avoid source code translation problems
+                && firstChar != 0xCE
                 && firstChar != '('
                 && firstChar != '['
                 && firstChar != '>'
@@ -598,7 +603,9 @@ public class AlignmentAnnotation
   {
     this.label = new String(annotation.label);
     if (annotation.description != null)
+    {
       this.description = new String(annotation.description);
+    }
     this.graphMin = annotation.graphMin;
     this.graphMax = annotation.graphMax;
     this.graph = annotation.graph;
@@ -653,7 +660,7 @@ public class AlignmentAnnotation
           {
             // could optimise this!
             p = (Integer) pos.nextElement();
-            Annotation a = (Annotation) annotation.sequenceMapping.get(p);
+            Annotation a = annotation.sequenceMapping.get(p);
             if (a == null)
             {
               continue;
@@ -696,13 +703,21 @@ public class AlignmentAnnotation
       return;
     }
     if (startRes < 0)
+    {
       startRes = 0;
+    }
     if (startRes >= annotations.length)
+    {
       startRes = annotations.length - 1;
+    }
     if (endRes >= annotations.length)
+    {
       endRes = annotations.length - 1;
+    }
     if (annotations == null)
+    {
       return;
+    }
     Annotation[] temp = new Annotation[endRes - startRes + 1];
     if (startRes < annotations.length)
     {
@@ -864,7 +879,9 @@ public class AlignmentAnnotation
   public void adjustForAlignment()
   {
     if (sequenceRef == null)
+    {
       return;
+    }
 
     if (annotations == null)
     {
@@ -890,7 +907,7 @@ public class AlignmentAnnotation
       {
         position = sequenceRef.findIndex(a) - 1;
 
-        temp[position] = (Annotation) sequenceMapping.get(index);
+        temp[position] = sequenceMapping.get(index);
       }
     }
 
@@ -911,8 +928,10 @@ public class AlignmentAnnotation
       if (annotations[i] == null)
       {
         if (i + 1 < iSize)
+        {
           System.arraycopy(annotations, i + 1, annotations, i, iSize - i
                   - 1);
+        }
         iSize--;
       }
       else
@@ -943,10 +962,14 @@ public class AlignmentAnnotation
     {
       if (sequenceRef != null)
       {
+        boolean rIsDs=sequenceRef.getDatasetSequence()==null,tIsDs=sequenceI.getDatasetSequence()==null;
         if (sequenceRef != sequenceI
-                && !sequenceRef.equals(sequenceI)
-                && sequenceRef.getDatasetSequence() != sequenceI
+                && (rIsDs && !tIsDs && sequenceRef != sequenceI
+                        .getDatasetSequence())
+                && (!rIsDs && tIsDs && sequenceRef.getDatasetSequence() != sequenceI)
+                && (!rIsDs && !tIsDs && sequenceRef.getDatasetSequence() != sequenceI
                         .getDatasetSequence())
+                && !sequenceRef.equals(sequenceI))
         {
           // if sequenceRef isn't intersecting with sequenceI
           // throw away old mapping and reconstruct.
@@ -1046,11 +1069,15 @@ public class AlignmentAnnotation
       for (int i = 0; i < annotations.length; i++)
       {
         if (annotations[i] == null)
+        {
           annotations[i] = new Annotation(String.valueOf(gapchar), null,
-                  ' ', 0f,null);
+                  ' ', 0f, null);
+        }
         else if (annotations[i].displayCharacter == null
                 || annotations[i].displayCharacter.equals(" "))
+        {
           annotations[i].displayCharacter = String.valueOf(gapchar);
+        }
       }
     }
   }
@@ -1094,6 +1121,11 @@ public class AlignmentAnnotation
   protected String calcId = "";
 
   /**
+   * properties associated with the calcId
+   */
+  protected Map<String, String> properties = new HashMap<String, String>();
+
+  /**
    * base colour for line graphs. If null, will be set automatically by
    * searching the alignment annotation
    */
@@ -1108,4 +1140,143 @@ public class AlignmentAnnotation
   {
     this.calcId = calcId;
   }
+
+  public boolean isRNA()
+  {
+    return isrna;
+  }
+
+  /**
+   * transfer annotation to the given sequence using the given mapping from the
+   * current positions or an existing sequence mapping
+   * 
+   * @param sq
+   * @param sp2sq
+   *          map involving sq as To or From
+   */
+  public void liftOver(SequenceI sq, Mapping sp2sq)
+  {
+    if (sp2sq.getMappedWidth() != sp2sq.getWidth())
+    {
+      // TODO: employ getWord/MappedWord to transfer annotation between cDNA and Protein reference frames
+      throw new Error("liftOver currently not implemented for transfer of annotation between different types of seqeunce");
+    }
+    boolean mapIsTo = (sp2sq != null) ? (sp2sq.getTo() == sq || sp2sq
+            .getTo() == sq.getDatasetSequence()) : false;
+
+    // TODO build a better annotation element map and get rid of annotations[]
+    Hashtable<Integer, Annotation> mapForsq = new Hashtable();
+    if (sequenceMapping != null)
+    {
+      if (sp2sq != null)
+      {
+        for (Entry<Integer, Annotation> ie : sequenceMapping.entrySet())
+        {
+          Integer mpos = Integer.valueOf(mapIsTo ? sp2sq
+                  .getMappedPosition(ie.getKey()) : sp2sq.getPosition(ie
+                  .getKey()));
+          if (mpos >= sq.getStart() && mpos <= sq.getEnd())
+          {
+            mapForsq.put(mpos, ie.getValue());
+          }
+        }
+        sequenceMapping = mapForsq;
+        sequenceRef = sq;
+        adjustForAlignment();
+      }
+      else
+      {
+        // trim positions
+      }
+    }
+  }
+
+  /**
+   * like liftOver but more general.
+   * 
+   * Takes an array of int pairs that will be used to update the internal
+   * sequenceMapping and so shuffle the annotated positions
+   * 
+   * @param newref
+   *          - new sequence reference for the annotation row - if null,
+   *          sequenceRef is left unchanged
+   * @param mapping
+   *          array of ints containing corresponding positions
+   * @param from
+   *          - column for current coordinate system (-1 for index+1)
+   * @param to
+   *          - column for destination coordinate system (-1 for index+1)
+   * @param idxoffset
+   *          - offset added to index when referencing either coordinate system
+   * @note no checks are made as to whether from and/or to are sensible
+   * @note caller should add the remapped annotation to newref if they have not
+   *       already
+   */
+  public void remap(SequenceI newref, int[][] mapping, int from, int to,
+          int idxoffset)
+  {
+    if (mapping != null)
+    {
+      Hashtable<Integer, Annotation> old = sequenceMapping, remap = new Hashtable<Integer, Annotation>();
+      int index = -1;
+      for (int mp[] : mapping)
+      {
+        if (index++ < 0)
+        {
+          continue;
+        }
+        Annotation ann = null;
+        if (from == -1)
+        {
+          ann = sequenceMapping.get(Integer.valueOf(idxoffset + index));
+        }
+        else
+        {
+          if (mp != null && mp.length > from)
+          {
+            ann = sequenceMapping.get(Integer.valueOf(mp[from]));
+          }
+        }
+        if (ann != null)
+        {
+          if (to == -1)
+          {
+            remap.put(Integer.valueOf(idxoffset + index), ann);
+          }
+          else
+          {
+            if (to > -1 && to < mp.length)
+            {
+              remap.put(Integer.valueOf(mp[to]), ann);
+            }
+          }
+        }
+      }
+      sequenceMapping = remap;
+      old.clear();
+      if (newref != null)
+      {
+        sequenceRef = newref;
+      }
+      adjustForAlignment();
+    }
+  }
+
+  public Object getProperty(String property)
+  {
+    if (properties == null)
+    {
+      return null;
+    }
+    return properties.get(property);
+  }
+
+  public void setProperty(String property, String value)
+  {
+    if (properties==null)
+    {
+      properties = new HashMap<String,String>();
+    }
+    properties.put(property, value);
+  }
 }