Merge branch 'develop' into bug/JAL-1841rnaSecStr
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Mon, 29 Aug 2016 08:01:06 +0000 (09:01 +0100)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Mon, 29 Aug 2016 08:01:06 +0000 (09:01 +0100)
13 files changed:
resources/lang/Messages.properties
resources/lang/Messages_es.properties
src/jalview/analysis/Rna.java
src/jalview/analysis/StructureFrequency.java
src/jalview/appletgui/AnnotationPanel.java
src/jalview/datamodel/AlignmentAnnotation.java
src/jalview/gui/AnnotationPanel.java
src/jalview/io/RnamlFile.java
src/jalview/io/StockholmFile.java
src/jalview/renderer/AnnotationRenderer.java
src/jalview/schemes/ResidueProperties.java
test/jalview/analysis/RnaTest.java
test/jalview/datamodel/AlignmentAnnotationTests.java

index 54181fe..b01464a 100644 (file)
@@ -389,7 +389,7 @@ label.ignore_unmatched_dropped_files = Ignore unmatched dropped files?
 label.view_name_original = Original
 label.enter_view_name = Enter View Name
 label.enter_label = Enter label
-label.enter_label_for_the_structure = Enter a label for the structure?
+label.enter_label_for_the_structure = Enter a label for the structure
 label.pdb_entry_is_already_displayed = {0} is already displayed.\nDo you want to re-use this viewer ?
 label.map_sequences_to_visible_window = Map Sequences to Visible Window: {0}
 label.add_pdbentry_to_view = Do you want to add {0} to the view called\n{1}\n
index 45941c2..87538be 100644 (file)
@@ -355,9 +355,9 @@ label.automatically_associate_pdb_files_with_sequences_same_name = Quieres asoci
 label.automatically_associate_pdb_files_by_name = Asociar los ficheros PDB por nombre automáticamente
 label.ignore_unmatched_dropped_files_info = Quieres <em>ignorar</em> los {0} ficheros cuyos nombres no coincidan con ningún IDs de las secuencias ?
 label.ignore_unmatched_dropped_files = Ignorar los ficheros sin coincidencias?
-label.enter_view_name = Introducir nombre visible (¿?)
+label.enter_view_name = Introduzca un nombre para la vista
 label.enter_label = Introducir etiqueta
-label.enter_label_for_the_structure = Introducir una etiqueta para la estructura?
+label.enter_label_for_the_structure = Introducir una etiqueta para la estructura
 label.pdb_entry_is_already_displayed = {0} Ya est\u00E1 mostrado.\nQuieres volver a usar este visor?
 label.map_sequences_to_visible_window = Mapa de secuencias en ventana visible: {0}
 label.add_pdbentry_to_view = Quieres a\u00F1adir {0} a la vista llamada\n{1}\n
index e752126..f497f0e 100644 (file)
@@ -31,82 +31,82 @@ import jalview.datamodel.SequenceFeature;
 import jalview.util.MessageManager;
 
 import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
 import java.util.Hashtable;
+import java.util.List;
 import java.util.Stack;
 import java.util.Vector;
 
 public class Rna
 {
 
-  static Hashtable<Integer, Integer> pairHash = new Hashtable();
-
-  private static final Character[] openingPars = { '(', '[', '{', '<', 'A',
-      'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
-      'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' };
-
-  private static final Character[] closingPars = { ')', ']', '}', '>', 'a',
-      'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
-      'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' };
-
-  private static HashSet<Character> openingParsSet = new HashSet<Character>(
-          Arrays.asList(openingPars));
-
-  private static HashSet<Character> closingParsSet = new HashSet<Character>(
-          Arrays.asList(closingPars));
-
-  private static Hashtable<Character, Character> closingToOpening = new Hashtable<Character, Character>()
-  // Initializing final data structure
-  {
-    private static final long serialVersionUID = 1L;
-    {
-      for (int i = 0; i < openingPars.length; i++)
-      {
-        // System.out.println(closingPars[i] + "->" + openingPars[i]);
-        put(closingPars[i], openingPars[i]);
-      }
-    }
-  };
-
-  private static boolean isOpeningParenthesis(char c)
+  /**
+   * Answers true if the character is a valid open pair rna secondary structure
+   * symbol. Currently accepts A-Z, ([{<
+   * 
+   * @param c
+   * @return
+   */
+  public static boolean isOpeningParenthesis(char c)
   {
-    return openingParsSet.contains(c);
+    return ('A' <= c && c <= 'Z' || c == '(' || c == '[' || c == '{' || c == '<');
   }
 
-  private static boolean isClosingParenthesis(char c)
+  /**
+   * Answers true if the character is a valid close pair rna secondary structure
+   * symbol. Currently accepts a-z, )]}>
+   * 
+   * @param c
+   * @return
+   */
+  public static boolean isClosingParenthesis(char c)
   {
-    return closingParsSet.contains(c);
+    return ('a' <= c && c <= 'z' || c == ')' || c == ']' || c == '}' || c == '>');
   }
 
-  private static char matchingOpeningParenthesis(char closingParenthesis)
-          throws WUSSParseException
+  /**
+   * Returns the matching open pair symbol for the given closing symbol.
+   * Currently returns A-Z for a-z, or ([{< for )]}>, or the input symbol if it
+   * is not a valid closing symbol.
+   * 
+   * @param c
+   * @return
+   */
+  public static char getMatchingOpeningParenthesis(char c)
   {
-    if (!isClosingParenthesis(closingParenthesis))
+    if ('a' <= c && c <= 'z')
     {
-      throw new WUSSParseException(
-              MessageManager.formatMessage(
-                      "exception.querying_matching_opening_parenthesis_for_non_closing_parenthesis",
-                      new String[] { new StringBuffer(closingParenthesis)
-                              .toString() }), -1);
+      return (char) (c + 'A' - 'a');
+    }
+    switch (c)
+    {
+    case ')':
+      return '(';
+    case ']':
+      return '[';
+    case '}':
+      return '{';
+    case '>':
+      return '<';
+    default:
+      return c;
     }
-
-    return closingToOpening.get(closingParenthesis);
   }
 
   /**
    * Based off of RALEE code ralee-get-base-pairs. Keeps track of open bracket
    * positions in "stack" vector. When a close bracket is reached, pair this
-   * with the last element in the "stack" vector and store in "pairs" vector.
-   * Remove last element in the "stack" vector. Continue in this manner until
-   * the whole string is processed.
+   * with the last matching element in the "stack" vector and store in "pairs"
+   * vector. Remove last element in the "stack" vector. Continue in this manner
+   * until the whole string is processed. Parse errors are thrown as exceptions
+   * wrapping the error location - position of the first unmatched closing
+   * bracket, or string length if there is an unmatched opening bracket.
    * 
    * @param line
    *          Secondary structure line of an RNA Stockholm file
-   * @return Array of SequenceFeature; type = RNA helix, begin is open base
-   *         pair, end is close base pair
+   * @return
+   * @throw {@link WUSSParseException}
    */
-  public static Vector<SimpleBP> GetSimpleBPs(CharSequence line)
+  public static Vector<SimpleBP> getSimpleBPs(CharSequence line)
           throws WUSSParseException
   {
     Hashtable<Character, Stack<Integer>> stacks = new Hashtable<Character, Stack<Integer>>();
@@ -128,13 +128,13 @@ public class Rna
       else if (isClosingParenthesis(base))
       {
 
-        char opening = matchingOpeningParenthesis(base);
+        char opening = getMatchingOpeningParenthesis(base);
 
         if (!stacks.containsKey(opening))
         {
           throw new WUSSParseException(MessageManager.formatMessage(
                   "exception.mismatched_unseen_closing_char",
-                  new String[] { new StringBuffer(base).toString() }), i);
+                  new String[] { String.valueOf(base) }), i);
         }
 
         Stack<Integer> stack = stacks.get(opening);
@@ -143,7 +143,7 @@ public class Rna
           // error whilst parsing i'th position. pass back
           throw new WUSSParseException(MessageManager.formatMessage(
                   "exception.mismatched_closing_char",
-                  new String[] { new StringBuffer(base).toString() }), i);
+                  new String[] { String.valueOf(base) }), i);
         }
         int temp = stack.pop();
 
@@ -156,33 +156,36 @@ public class Rna
       Stack<Integer> stack = stacks.get(opening);
       if (!stack.empty())
       {
+        /*
+         * we have an unmatched opening bracket; report error as at
+         * i (length of input string)
+         */
         throw new WUSSParseException(MessageManager.formatMessage(
                 "exception.mismatched_opening_char",
-                new String[] { new StringBuffer(opening).toString(),
-                    Integer.valueOf(stack.pop()).toString() }), i);
+                new String[] { String.valueOf(opening),
+                    String.valueOf(stack.pop()) }), i);
       }
     }
     return pairs;
   }
 
-  public static SequenceFeature[] GetBasePairs(CharSequence line)
+  public static SequenceFeature[] getBasePairs(List<SimpleBP> bps)
           throws WUSSParseException
   {
-    Vector<SimpleBP> bps = GetSimpleBPs(line);
     SequenceFeature[] outPairs = new SequenceFeature[bps.size()];
     for (int p = 0; p < bps.size(); p++)
     {
-      SimpleBP bp = bps.elementAt(p);
+      SimpleBP bp = bps.get(p);
       outPairs[p] = new SequenceFeature("RNA helix", "", "", bp.getBP5(),
               bp.getBP3(), "");
     }
     return outPairs;
   }
 
-  public static ArrayList<SimpleBP> GetModeleBP(CharSequence line)
+  public static List<SimpleBP> getModeleBP(CharSequence line)
           throws WUSSParseException
   {
-    Vector<SimpleBP> bps = GetSimpleBPs(line);
+    Vector<SimpleBP> bps = getSimpleBPs(line);
     return new ArrayList<SimpleBP>(bps);
   }
 
@@ -220,8 +223,8 @@ public class Rna
     int close; // Position of a close bracket under review
     int j; // Counter
 
-    Hashtable helices = new Hashtable(); // Keep track of helix number for each
-                                         // position
+    Hashtable<Integer, Integer> helices = new Hashtable<Integer, Integer>();
+    // Keep track of helix number for each position
 
     // Go through each base pair and assign positions a helix
     for (i = 0; i < pairs.length; i++)
@@ -255,7 +258,7 @@ public class Rna
         if ((popen < lastopen) && (popen > open))
         {
           if (helices.containsValue(popen)
-                  && (((Integer) helices.get(popen)) == helix))
+                  && ((helices.get(popen)) == helix))
           {
             continue;
           }
@@ -281,4 +284,126 @@ public class Rna
 
     }
   }
+
+  /**
+   * Answers true if the character is a recognised symbol for RNA secondary
+   * structure. Currently accepts a-z, A-Z, ()[]{}<>.
+   * 
+   * @param c
+   * @return
+   */
+  public static boolean isRnaSecondaryStructureSymbol(char c)
+  {
+    return isOpeningParenthesis(c) || isClosingParenthesis(c);
+  }
+
+  /**
+   * Translates a string to RNA secondary structure representation. Returns the
+   * string with any non-SS characters changed to spaces. Accepted characters
+   * are a-z, A-Z, and (){}[]<> brackets.
+   * 
+   * @param ssString
+   * @return
+   */
+  public static String getRNASecStrucState(String ssString)
+  {
+    if (ssString == null)
+    {
+      return null;
+    }
+    StringBuilder result = new StringBuilder(ssString.length());
+    for (int i = 0; i < ssString.length(); i++)
+    {
+      char c = ssString.charAt(i);
+      result.append(isRnaSecondaryStructureSymbol(c) ? c : " ");
+    }
+    return result.toString();
+  }
+
+  /**
+   * Answers true if the base-pair is either a canonical (A-T/U, C-G) or a
+   * wobble (G-T/U) pair (either way round), else false
+   * 
+   * @param first
+   * @param second
+   * @return
+   */
+  public static boolean isCanonicalOrWobblePair(char first, char second)
+  {
+    if (first > 'Z')
+    {
+      first -= 32;
+    }
+    if (second > 'Z')
+    {
+      second -= 32;
+    }
+  
+    switch (first)
+    {
+    case 'A':
+      switch (second)
+      {
+      case 'T':
+      case 'U':
+        return true;
+      }
+      break;
+    case 'C':
+      switch (second)
+      {
+      case 'G':
+        return true;
+      }
+      break;
+    case 'T':
+    case 'U':
+      switch (second)
+      {
+      case 'A':
+      case 'G':
+        return true;
+      }
+      break;
+    case 'G':
+      switch (second)
+      {
+      case 'C':
+      case 'T':
+      case 'U':
+        return true;
+      }
+      break;
+    }
+    return false;
+  }
+
+  /**
+   * Returns the matching close pair symbol for the given opening symbol.
+   * Currently returns a-z for A-Z, or )]}> for ([{<, or the input symbol if it
+   * is not a valid opening symbol.
+   * 
+   * @param c
+   * @return
+   */
+  public static char getMatchingClosingParenthesis(char c)
+  {
+    if ('A' <= c && c <= 'Z')
+    {
+      return (char) (c + 'a' - 'A');
+    }
+    switch (c)
+    {
+    case '(':
+      return ')';
+    case '[':
+      return ']';
+    case '{':
+      return '}';
+    case '<':
+      return '>';
+    default:
+      return c;
+    }
+  }
 }
index 88387dd..479c856 100644 (file)
@@ -24,6 +24,7 @@ import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.Annotation;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
+import jalview.util.Comparison;
 import jalview.util.Format;
 
 import java.util.ArrayList;
@@ -103,23 +104,23 @@ public class StructureFrequency
 
     SequenceFeature[] rna = rnaStruc._rnasecstr;
     char c, s, cEnd;
-    int count = 0, nonGap = 0, i, bpEnd = -1, j, jSize = sequences.length;
+    int bpEnd = -1;
+    int jSize = sequences.length;
     int[] values;
     int[][] pairs;
     float percentage;
-    boolean wooble = true;
-    for (i = start; i < end; i++) // foreach column
+
+    for (int i = start; i < end; i++) // foreach column
     {
-      residueHash = new Hashtable();
+      int canonicalOrWobblePairCount = 0;
+      int otherPairCount = 0;
       maxResidue = "-";
       values = new int[255];
       pairs = new int[255][255];
       bpEnd = -1;
-      // System.out.println("s="+struc[i]);
       if (i < struc.length)
       {
         s = struc[i];
-
       }
       else
       {
@@ -130,7 +131,7 @@ public class StructureFrequency
         s = '-';
       }
 
-      if (s != '(' && s != '[')
+      if (!Rna.isOpeningParenthesis(s))
       {
         if (s == '-')
         {
@@ -139,12 +140,11 @@ public class StructureFrequency
       }
       else
       {
-
         bpEnd = findPair(rna, i);
 
         if (bpEnd > -1)
         {
-          for (j = 0; j < jSize; j++) // foreach row
+          for (int j = 0; j < jSize; j++) // foreach row
           {
             if (sequences[j] == null)
             {
@@ -152,45 +152,46 @@ public class StructureFrequency
                       .println("WARNING: Consensus skipping null sequence - possible race condition.");
               continue;
             }
-            c = sequences[j].getCharAt(i);
-            // System.out.println("c="+c);
 
-            // standard representation for gaps in sequence and structure
-            if (c == '.' || c == ' ')
-            {
-              c = '-';
-            }
+            c = sequences[j].getCharAt(i);
+            cEnd = sequences[j].getCharAt(bpEnd);
 
-            if (c == '-')
+            if (Comparison.isGap(c) || Comparison.isGap(cEnd))
             {
               values['-']++;
               continue;
             }
-            cEnd = sequences[j].getCharAt(bpEnd);
 
-            // System.out.println("pairs ="+c+","+cEnd);
-            if (checkBpType(c, cEnd) == true)
+            /*
+             * ensure upper-case for counting purposes
+             */
+            if ('a' <= c && 'z' >= c)
+            {
+              c += 'A' - 'a';
+            }
+            if ('a' <= cEnd && 'z' >= cEnd)
+            {
+              cEnd += 'A' - 'a';
+            }
+            if (Rna.isCanonicalOrWobblePair(c, cEnd))
             {
-              values['(']++; // H means it's a helix (structured)
+              values['(']++;
               maxResidue = "(";
-              wooble = true;
-              // System.out.println("It's a pair wc");
-
+              canonicalOrWobblePairCount++;
             }
-            if (checkBpType(c, cEnd) == false)
+            else
             {
-              wooble = false;
-              values['[']++; // H means it's a helix (structured)
+              values['[']++;
               maxResidue = "[";
-
+              otherPairCount++;
             }
             pairs[c][cEnd]++;
-
           }
         }
         // nonGap++;
       }
-      // UPDATE this for new values
+
+      residueHash = new Hashtable();
       if (profile)
       {
         // TODO 1-dim array with jsize in [0], nongapped in [1]; or Pojo
@@ -199,13 +200,21 @@ public class StructureFrequency
 
         residueHash.put(PAIRPROFILE, pairs);
       }
-      if (wooble == true)
-      {
-        count = values['('];
-      }
-      if (wooble == false)
+
+      /*
+       * the count is the number of valid pairs (as a percentage, determines
+       * the relative size of the profile logo)
+       */
+      int count = canonicalOrWobblePairCount;
+
+      /*
+       * currently displaying as '(' if most pairs are valid, or as
+       * '[' if there are more invalid than valid pairs 
+       */
+      if (!maxResidue.equals("-"))
       {
-        count = values['['];
+        maxResidue = canonicalOrWobblePairCount >= otherPairCount ? "("
+                : "[";
       }
       residueHash.put(MAXCOUNT, new Integer(count));
       residueHash.put(MAXRESIDUE, maxResidue);
@@ -225,17 +234,9 @@ public class StructureFrequency
         values[']'] = values['['];
         values['('] = 0;
         values['['] = 0;
+        maxResidue = maxResidue.equals("(") ? ")" : "]";
+
         residueHash = new Hashtable();
-        if (wooble == true)
-        {
-          // System.out.println(maxResidue+","+wooble);
-          maxResidue = ")";
-        }
-        if (wooble == false)
-        {
-          // System.out.println(maxResidue+","+wooble);
-          maxResidue = "]";
-        }
         if (profile)
         {
           residueHash.put(PROFILE, new int[][] { values,
@@ -251,80 +252,8 @@ public class StructureFrequency
         residueHash.put(PID_GAPS, new Float(percentage));
 
         result[bpEnd] = residueHash;
-
-      }
-    }
-  }
-
-  /**
-   * Method to check if a base-pair is a canonical or a wobble bp
-   * 
-   * @param up
-   *          5' base
-   * @param down
-   *          3' base
-   * @return True if it is a canonical/wobble bp
-   */
-  public static boolean checkBpType(char up, char down)
-  {
-    if (up > 'Z')
-    {
-      up -= 32;
-    }
-    if (down > 'Z')
-    {
-      down -= 32;
-    }
-
-    switch (up)
-    {
-    case 'A':
-      switch (down)
-      {
-      case 'T':
-        return true;
-      case 'U':
-        return true;
-      }
-      break;
-    case 'C':
-      switch (down)
-      {
-      case 'G':
-        return true;
-      }
-      break;
-    case 'T':
-      switch (down)
-      {
-      case 'A':
-        return true;
-      case 'G':
-        return true;
-      }
-      break;
-    case 'G':
-      switch (down)
-      {
-      case 'C':
-        return true;
-      case 'T':
-        return true;
-      case 'U':
-        return true;
-      }
-      break;
-    case 'U':
-      switch (down)
-      {
-      case 'A':
-        return true;
-      case 'G':
-        return true;
       }
-      break;
     }
-    return false;
   }
 
   /**
@@ -534,7 +463,7 @@ public class StructureFrequency
       for (String j : test)
       {
         System.out.println(i + "-" + j + ": "
-                + StructureFrequency.checkBpType(i.charAt(0), j.charAt(0)));
+                + Rna.isCanonicalOrWobblePair(i.charAt(0), j.charAt(0)));
       }
     }
   }
index e5475f8..6012c1a 100755 (executable)
@@ -28,6 +28,7 @@ import jalview.renderer.AwtRenderPanelI;
 import jalview.schemes.ResidueProperties;
 import jalview.util.Comparison;
 import jalview.util.MessageManager;
+import jalview.util.Platform;
 
 import java.awt.Color;
 import java.awt.Dimension;
@@ -101,7 +102,8 @@ public class AnnotationPanel extends Panel implements AwtRenderPanelI,
 
   public AnnotationPanel(AlignmentPanel ap)
   {
-    MAC = new jalview.util.Platform().isAMac();
+    new jalview.util.Platform();
+    MAC = Platform.isAMac();
     this.ap = ap;
     av = ap.av;
     setLayout(null);
@@ -242,7 +244,8 @@ public class AnnotationPanel extends Panel implements AwtRenderPanelI,
       else if (evt.getActionCommand().equals(STEM))
       {
         type = 'S';
-        symbol = "\u03C3";
+        int column = av.getColumnSelection().getSelectedRanges().get(0)[0];
+        symbol = aa[activeRow].getDefaultRnaHelixSymbol(column);
       }
 
       if (!aa[activeRow].hasIcons)
@@ -347,7 +350,8 @@ public class AnnotationPanel extends Panel implements AwtRenderPanelI,
     if ((evt.getModifiers() & InputEvent.BUTTON3_MASK) == InputEvent.BUTTON3_MASK
             && activeRow != -1)
     {
-      if (av.getColumnSelection() == null)
+      if (av.getColumnSelection() == null
+              || av.getColumnSelection().isEmpty())
       {
         return;
       }
@@ -355,10 +359,8 @@ public class AnnotationPanel extends Panel implements AwtRenderPanelI,
       PopupMenu pop = new PopupMenu(
               MessageManager.getString("label.structure_type"));
       MenuItem item;
-      /*
-       * Just display the needed structure options
-       */
-      if (av.getAlignment().isNucleotide() == true)
+
+      if (av.getAlignment().isNucleotide())
       {
         item = new MenuItem(STEM);
         item.addActionListener(this);
index 7990a5c..2a89fa1 100755 (executable)
@@ -24,11 +24,11 @@ import jalview.analysis.Rna;
 import jalview.analysis.SecStrConsensus.SimpleBP;
 import jalview.analysis.WUSSParseException;
 
-import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 
@@ -79,7 +79,7 @@ public class AlignmentAnnotation
   /** Array of annotations placed in the current coordinate system */
   public Annotation[] annotations;
 
-  public ArrayList<SimpleBP> bps = null;
+  public List<SimpleBP> bps = null;
 
   /**
    * RNA secondary structure contact positions
@@ -102,8 +102,8 @@ public class AlignmentAnnotation
   {
     try
     {
-      _rnasecstr = Rna.GetBasePairs(RNAannot);
-      bps = Rna.GetModeleBP(RNAannot);
+      bps = Rna.getModeleBP(RNAannot);
+      _rnasecstr = Rna.getBasePairs(bps);
       invalidrnastruc = -1;
     } catch (WUSSParseException px)
     {
@@ -272,7 +272,7 @@ public class AlignmentAnnotation
   // JBPNote: what does this do ?
   public void ConcenStru(CharSequence RNAannot) throws WUSSParseException
   {
-    bps = Rna.GetModeleBP(RNAannot);
+    bps = Rna.getModeleBP(RNAannot);
   }
 
   /**
@@ -485,7 +485,7 @@ public class AlignmentAnnotation
       this(0, annotations.length);
     }
 
-    public AnnotCharSequence(int start, int end)
+    AnnotCharSequence(int start, int end)
     {
       offset = start;
       max = end;
@@ -593,6 +593,7 @@ public class AlignmentAnnotation
     if (annotations == null)
     {
       visible = false; // try to prevent renderer from displaying.
+      invalidrnastruc = -1;
       return; // this is a non-annotation row annotation - ie a sequence score.
     }
 
@@ -1411,6 +1412,77 @@ public class AlignmentAnnotation
     this.annotationId = ANNOTATION_ID_PREFIX + Long.toString(nextId());
   }
 
+  /**
+   * Returns the match for the last unmatched opening RNA helix pair symbol
+   * preceding the given column, or '(' if nothing found to match.
+   * 
+   * @param column
+   * @return
+   */
+  public String getDefaultRnaHelixSymbol(int column)
+  {
+    String result = "(";
+    if (annotations == null)
+    {
+      return result;
+    }
+
+    /*
+     * for each preceding column, if it contains an open bracket, 
+     * count whether it is still unmatched at column, if so return its pair
+     * (likely faster than the fancy alternative using stacks)
+     */
+    for (int col = column - 1; col >= 0; col--)
+    {
+      Annotation annotation = annotations[col];
+      if (annotation == null)
+      {
+        continue;
+      }
+      String displayed = annotation.displayCharacter;
+      if (displayed == null || displayed.length() != 1)
+      {
+        continue;
+      }
+      char symbol = displayed.charAt(0);
+      if (!Rna.isOpeningParenthesis(symbol))
+      {
+        continue;
+      }
+
+      /*
+       * found an opening bracket symbol
+       * count (closing-opening) symbols of this type that follow it,
+       * up to and excluding the target column; if the count is less
+       * than 1, the opening bracket is unmatched, so return its match
+       */
+      String closer = String.valueOf(Rna
+              .getMatchingClosingParenthesis(symbol));
+      String opener = String.valueOf(symbol);
+      int count = 0;
+      for (int j = col + 1; j < column; j++)
+      {
+        if (annotations[j] != null)
+        {
+          String s = annotations[j].displayCharacter;
+          if (closer.equals(s))
+          {
+            count++;
+          }
+          else if (opener.equals(s))
+          {
+            count--;
+          }
+        }
+      }
+      if (count < 1)
+      {
+        return closer;
+      }
+    }
+    return result;
+  }
+
   protected static synchronized long nextId()
   {
     return counter++;
index 3c9a13e..6a621ff 100755 (executable)
@@ -290,7 +290,8 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
       aa[activeRow].annotations = anot;
     }
 
-    if (evt.getActionCommand().equals(REMOVE))
+    String action = evt.getActionCommand();
+    if (action.equals(REMOVE))
     {
       for (int index : av.getColumnSelection().getSelected())
       {
@@ -300,7 +301,7 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
         }
       }
     }
-    else if (evt.getActionCommand().equals(LABEL))
+    else if (action.equals(LABEL))
     {
       String exMesg = collectAnnotVals(anot, LABEL);
       String label = JOptionPane.showInputDialog(this,
@@ -325,10 +326,7 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
 
         if (anot[index] == null)
         {
-          anot[index] = new Annotation(label, "", ' ', 0); // TODO: verify that
-          // null exceptions
-          // aren't raised
-          // elsewhere.
+          anot[index] = new Annotation(label, "", ' ', 0);
         }
         else
         {
@@ -336,7 +334,7 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
         }
       }
     }
-    else if (evt.getActionCommand().equals(COLOUR))
+    else if (action.equals(COLOUR))
     {
       Color col = JColorChooser.showDialog(this,
               MessageManager.getString("label.select_foreground_colour"),
@@ -361,23 +359,24 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
     // HELIX, SHEET or STEM
     {
       char type = 0;
-      String symbol = "\u03B1";
+      String symbol = "\u03B1"; // alpha
 
-      if (evt.getActionCommand().equals(HELIX))
+      if (action.equals(HELIX))
       {
         type = 'H';
       }
-      else if (evt.getActionCommand().equals(SHEET))
+      else if (action.equals(SHEET))
       {
         type = 'E';
-        symbol = "\u03B2";
+        symbol = "\u03B2"; // beta
       }
 
       // Added by LML to color stems
-      else if (evt.getActionCommand().equals(STEM))
+      else if (action.equals(STEM))
       {
         type = 'S';
-        symbol = "\u03C3";
+        int column = av.getColumnSelection().getSelectedRanges().get(0)[0];
+        symbol = aa[activeRow].getDefaultRnaHelixSymbol(column);
       }
 
       if (!aa[activeRow].hasIcons)
@@ -396,7 +395,7 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
       if ((label.length() > 0) && !aa[activeRow].hasText)
       {
         aa[activeRow].hasText = true;
-        if (evt.getActionCommand().equals(STEM))
+        if (action.equals(STEM))
         {
           aa[activeRow].showAllColLabels = true;
         }
@@ -534,7 +533,8 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
 
     if (evt.isPopupTrigger() && activeRow != -1)
     {
-      if (av.getColumnSelection() == null)
+      if (av.getColumnSelection() == null
+              || av.getColumnSelection().isEmpty())
       {
         return;
       }
@@ -545,7 +545,7 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
       /*
        * Just display the needed structure options
        */
-      if (av.getAlignment().isNucleotide() == true)
+      if (av.getAlignment().isNucleotide())
       {
         item = new JMenuItem(STEM);
         item.addActionListener(this);
@@ -574,11 +574,6 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
       return;
     }
 
-    if (aa == null)
-    {
-      return;
-    }
-
     ap.getScalePanel().mousePressed(evt);
 
   }
index 0941a6f..f48f825 100644 (file)
@@ -20,7 +20,7 @@
  */
 package jalview.io;
 
-import jalview.analysis.SecStrConsensus.SimpleBP;
+import jalview.analysis.Rna;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.Annotation;
 import jalview.datamodel.Sequence;
@@ -32,6 +32,7 @@ import java.io.FileNotFoundException;
 import java.io.FileReader;
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.List;
 
 import com.stevesoft.pat.Regex;
 
@@ -79,6 +80,7 @@ public class RnamlFile extends AlignFile
    * 
    * @see jalview.io.AlignFile#parse()
    */
+  @Override
   public void parse() throws IOException
   {
     if (System.getProperty("java.version").indexOf("1.6") > -1
@@ -134,10 +136,10 @@ public class RnamlFile extends AlignFile
 
     result = RNAFactory.loadSecStrRNAML(getReader());
 
-    ArrayList<ArrayList> allarray = new ArrayList();
-    ArrayList<ArrayList<SimpleBP>> BP = new ArrayList();
-    ArrayList strucinarray = new ArrayList();
-    SequenceI[] seqs = new SequenceI[result.size()];
+    // ArrayList<ArrayList> allarray = new ArrayList();
+    // ArrayList<ArrayList<SimpleBP>> BP = new ArrayList();
+    // ArrayList strucinarray = new ArrayList();
+    SequenceI[] sqs = new SequenceI[result.size()];
 
     for (int i = 0; i < result.size(); i++)
     {
@@ -157,9 +159,9 @@ public class RnamlFile extends AlignFile
           id += "." + i;
         }
       }
-      seqs[i] = new Sequence(id, seq, begin, end);
+      sqs[i] = new Sequence(id, seq, begin, end);
 
-      seqs[i].setEnd(seqs[i].findPosition(seqs[i].getLength()));
+      sqs[i].setEnd(sqs[i].findPosition(sqs[i].getLength()));
       String[] annot = new String[rna.length()];
       Annotation[] ann = new Annotation[rna.length()];
 
@@ -170,9 +172,8 @@ public class RnamlFile extends AlignFile
       }
       for (int k = 0; k < rna.length(); k++)
       {
-        ann[k] = new Annotation(annot[k], "",
-                jalview.schemes.ResidueProperties.getRNASecStrucState(
-                        annot[k]).charAt(0), 0f);
+        ann[k] = new Annotation(annot[k], "", Rna.getRNASecStrucState(
+                annot[k]).charAt(0), 0f);
       }
 
       AlignmentAnnotation align = new AlignmentAnnotation(
@@ -181,17 +182,17 @@ public class RnamlFile extends AlignFile
                       + current.getID()
                       : "", ann);
 
-      seqs[i].addAlignmentAnnotation(align);
-      seqs[i].setRNA(result.get(i));
+      sqs[i].addAlignmentAnnotation(align);
+      sqs[i].setRNA(result.get(i));
 
-      allarray.add(strucinarray);
+      // allarray.add(strucinarray);
 
       annotations.addElement(align);
-      BP.add(align.bps);
+      // BP.add(align.bps);
 
     }
 
-    setSeqs(seqs);
+    setSeqs(sqs);
   }
 
   public static String print(SequenceI[] s)
@@ -199,13 +200,14 @@ public class RnamlFile extends AlignFile
     return "not yet implemented";
   }
 
+  @Override
   public String print()
   {
     System.out.print("print :");
     return print(getSeqsAsArray());
   }
 
-  public ArrayList getRNA()
+  public List<RNA> getRNA()
   {
     return result;
   }
index ab80ffa..bec7d82 100644 (file)
@@ -23,6 +23,7 @@
  */
 package jalview.io;
 
+import jalview.analysis.Rna;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.Annotation;
@@ -31,6 +32,7 @@ import jalview.datamodel.Mapping;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
+import jalview.schemes.ResidueProperties;
 import jalview.util.Format;
 import jalview.util.MessageManager;
 
@@ -72,8 +74,12 @@ import fr.orsay.lri.varna.models.rna.RNA;
  */
 public class StockholmFile extends AlignFile
 {
-  // static Logger logger = Logger.getLogger("jalview.io.StockholmFile");
-  protected ArrayList<RNA> result;
+  private static final Regex OPEN_PAREN = new Regex("(<|\\[)", "(");
+
+  private static final Regex CLOSE_PAREN = new Regex("(>|\\])", ")");
+
+  private static final Regex DETECT_BRACKETS = new Regex(
+          "(<|>|\\[|\\]|\\(|\\))");
 
   StringBuffer out; // output buffer
 
@@ -119,7 +125,7 @@ public class StockholmFile extends AlignFile
     fr = new FileReader(inFile);
 
     BufferedReader r = new BufferedReader(fr);
-    result = null;
+    List<RNA> result = null;
     try
     {
       result = RNAFactory.loadSecStrStockholm(r);
@@ -156,9 +162,8 @@ public class StockholmFile extends AlignFile
 
       for (int k = 0; k < rna.length(); k++)
       {
-        ann[k] = new Annotation(annot[k], "",
-                jalview.schemes.ResidueProperties.getRNASecStrucState(
-                        annot[k]).charAt(0), 0f);
+        ann[k] = new Annotation(annot[k], "", Rna.getRNASecStrucState(
+                annot[k]).charAt(0), 0f);
 
       }
       AlignmentAnnotation align = new AlignmentAnnotation("Sec. str.",
@@ -789,19 +794,13 @@ public class StockholmFile extends AlignFile
   }
 
   protected static AlignmentAnnotation parseAnnotationRow(
-          Vector annotation, String label, String annots)
+          Vector<AlignmentAnnotation> annotation, String label,
+          String annots)
   {
     String convert1, convert2 = null;
 
-    // Convert all bracket types to parentheses
-    Regex openparen = new Regex("(<|\\[)", "(");
-    Regex closeparen = new Regex("(>|\\])", ")");
-
-    // Detect if file is RNA by looking for bracket types
-    Regex detectbrackets = new Regex("(<|>|\\[|\\]|\\(|\\))");
-
-    convert1 = openparen.replaceAll(annots);
-    convert2 = closeparen.replaceAll(convert1);
+    convert1 = OPEN_PAREN.replaceAll(annots);
+    convert2 = CLOSE_PAREN.replaceAll(convert1);
     annots = convert2;
 
     String type = label;
@@ -828,15 +827,15 @@ public class StockholmFile extends AlignFile
       {
         // if (" .-_".indexOf(pos) == -1)
         {
-          if (detectbrackets.search(pos))
+          if (DETECT_BRACKETS.search(pos))
           {
-            ann.secondaryStructure = jalview.schemes.ResidueProperties
-                    .getRNASecStrucState(pos).charAt(0);
+            ann.secondaryStructure = Rna.getRNASecStrucState(
+                    pos).charAt(0);
           }
           else
           {
-            ann.secondaryStructure = jalview.schemes.ResidueProperties
-                    .getDssp3state(pos).charAt(0);
+            ann.secondaryStructure = ResidueProperties.getDssp3state(pos)
+                    .charAt(0);
           }
 
           if (ann.secondaryStructure == pos.charAt(0))
@@ -854,10 +853,10 @@ public class StockholmFile extends AlignFile
       els[i] = ann;
     }
     AlignmentAnnotation annot = null;
-    Enumeration e = annotation.elements();
+    Enumeration<AlignmentAnnotation> e = annotation.elements();
     while (e.hasMoreElements())
     {
-      annot = (AlignmentAnnotation) e.nextElement();
+      annot = e.nextElement();
       if (annot.label.equals(type))
       {
         break;
@@ -1121,6 +1120,7 @@ public class StockholmFile extends AlignFile
   }
 
   private static Hashtable typeIds = null;
+
   static
   {
     if (typeIds == null)
index 073ea92..94015de 100644 (file)
@@ -22,6 +22,7 @@ package jalview.renderer;
 
 import jalview.analysis.AAFrequency;
 import jalview.analysis.CodingUtils;
+import jalview.analysis.Rna;
 import jalview.analysis.StructureFrequency;
 import jalview.api.AlignViewportI;
 import jalview.datamodel.AlignmentAnnotation;
@@ -43,8 +44,6 @@ import java.awt.image.ImageObserver;
 import java.util.BitSet;
 import java.util.Hashtable;
 
-import com.stevesoft.pat.Regex;
-
 public class AnnotationRenderer
 {
   private static final int UPPER_TO_LOWER = 'a' - 'A'; // 32
@@ -75,7 +74,7 @@ public class AnnotationRenderer
     this.debugRedraw = debugRedraw;
   }
 
-  public void drawStemAnnot(Graphics g, Annotation[] row_annotations,
+  void drawStemAnnot(Graphics g, Annotation[] row_annotations,
           int lastSSX, int x, int y, int iconOffset, int startRes,
           int column, boolean validRes, boolean validEnd)
   {
@@ -83,7 +82,6 @@ public class AnnotationRenderer
     int sCol = (lastSSX / charWidth) + startRes;
     int x1 = lastSSX;
     int x2 = (x * charWidth);
-    Regex closeparen = new Regex("(\\))");
 
     char dc = (column == 0 || row_annotations[column - 1] == null) ? ' '
             : row_annotations[column - 1].secondaryStructure;
@@ -93,15 +91,17 @@ public class AnnotationRenderer
     boolean diffdownstream = !validRes || !validEnd
             || row_annotations[column] == null
             || dc != row_annotations[column].secondaryStructure;
-    // System.out.println("Column "+column+" diff up: "+diffupstream+" down:"+diffdownstream);
-    // If a closing base pair half of the stem, display a backward arrow
-    if (column > 0 && ResidueProperties.isCloseParenRNA(dc))
-    {
 
+    if (column > 0 && Rna.isClosingParenthesis(dc))
+    {
       if (diffupstream)
       // if (validRes && column>1 && row_annotations[column-2]!=null &&
       // dc.equals(row_annotations[column-2].displayCharacter))
       {
+        /*
+         * if new annotation with a closing base pair half of the stem, 
+         * display a backward arrow
+         */
         g.fillPolygon(new int[] { lastSSX + 5, lastSSX + 5, lastSSX },
                 new int[] { y + iconOffset, y + 14 + iconOffset,
                     y + 8 + iconOffset }, 3);
@@ -114,10 +114,13 @@ public class AnnotationRenderer
     }
     else
     {
-
       // display a forward arrow
       if (diffdownstream)
       {
+        /*
+         * if annotation ending with an opeing base pair half of the stem, 
+         * display a forward arrow
+         */
         g.fillPolygon(new int[] { x2 - 5, x2 - 5, x2 }, new int[] {
             y + iconOffset, y + 14 + iconOffset, y + 8 + iconOffset }, 3);
         x2 -= 5;
@@ -195,7 +198,7 @@ public class AnnotationRenderer
    */
   private boolean canClip = false;
 
-  public void drawNotCanonicalAnnot(Graphics g, Color nonCanColor,
+  void drawNotCanonicalAnnot(Graphics g, Color nonCanColor,
           Annotation[] row_annotations, int lastSSX, int x, int y,
           int iconOffset, int startRes, int column, boolean validRes,
           boolean validEnd)
@@ -206,7 +209,6 @@ public class AnnotationRenderer
     int sCol = (lastSSX / charWidth) + startRes;
     int x1 = lastSSX;
     int x2 = (x * charWidth);
-    Regex closeparen = new Regex("}|]|<|[a-z]");
 
     String dc = (column == 0 || row_annotations[column - 1] == null) ? ""
             : row_annotations[column - 1].displayCharacter;
@@ -218,8 +220,7 @@ public class AnnotationRenderer
             || !dc.equals(row_annotations[column].displayCharacter);
     // System.out.println("Column "+column+" diff up: "+diffupstream+" down:"+diffdownstream);
     // If a closing base pair half of the stem, display a backward arrow
-    if (column > 0 && closeparen.search(dc))// closeletter_b.search(dc)||closeletter_c.search(dc)||closeletter_d.search(dc)||closecrochet.search(dc))
-                                            // )
+    if (column > 0 && Rna.isClosingParenthesis(dc.charAt(0)))
     {
 
       if (diffupstream)
@@ -321,7 +322,7 @@ public class AnnotationRenderer
    * @param column
    * @return
    */
-  public int[] getProfileFor(AlignmentAnnotation aa, int column)
+  int[] getProfileFor(AlignmentAnnotation aa, int column)
   {
     // TODO : consider refactoring the global alignment calculation
     // properties/rendering attributes as a global 'alignment group' which holds
@@ -1071,7 +1072,7 @@ public class AnnotationRenderer
 
   private Color sdNOTCANONICAL_COLOUR;
 
-  public void drawGlyphLine(Graphics g, Annotation[] row, int lastSSX,
+  void drawGlyphLine(Graphics g, Annotation[] row, int lastSSX,
           int x, int y, int iconOffset, int startRes, int column,
           boolean validRes, boolean validEnd)
   {
@@ -1079,7 +1080,7 @@ public class AnnotationRenderer
     g.fillRect(lastSSX, y + 6 + iconOffset, (x * charWidth) - lastSSX, 2);
   }
 
-  public void drawSheetAnnot(Graphics g, Annotation[] row,
+  void drawSheetAnnot(Graphics g, Annotation[] row,
 
   int lastSSX, int x, int y, int iconOffset, int startRes, int column,
           boolean validRes, boolean validEnd)
@@ -1103,7 +1104,7 @@ public class AnnotationRenderer
 
   }
 
-  public void drawHelixAnnot(Graphics g, Annotation[] row, int lastSSX,
+  void drawHelixAnnot(Graphics g, Annotation[] row, int lastSSX,
           int x, int y, int iconOffset, int startRes, int column,
           boolean validRes, boolean validEnd)
   {
@@ -1163,7 +1164,7 @@ public class AnnotationRenderer
     g.fillRect(x1, y + 4 + iconOffset, x2 - x1, 8);
   }
 
-  public void drawLineGraph(Graphics g, AlignmentAnnotation _aa,
+  void drawLineGraph(Graphics g, AlignmentAnnotation _aa,
           Annotation[] aa_annotations, int sRes, int eRes, int y,
           float min, float max, int graphHeight)
   {
@@ -1256,7 +1257,7 @@ public class AnnotationRenderer
     }
   }
 
-  public void drawBarGraph(Graphics g, AlignmentAnnotation _aa,
+  void drawBarGraph(Graphics g, AlignmentAnnotation _aa,
           Annotation[] aa_annotations, int sRes, int eRes, float min,
           float max, int y, boolean renderHistogram, boolean renderProfile,
           boolean normaliseProfile)
@@ -1387,8 +1388,9 @@ public class AnnotationRenderer
             scl = htn * scale * profl[c++];
             lm = ofont.getLineMetrics(dc, 0, 1, g.getFontMetrics()
                     .getFontRenderContext());
-            g.setFont(ofont.deriveFont(AffineTransform.getScaleInstance(
-                    wdth, scl / lm.getAscent())));
+            Font font = ofont.deriveFont(AffineTransform.getScaleInstance(
+                    wdth, scl / lm.getAscent()));
+            g.setFont(font);
             lm = g.getFontMetrics().getLineMetrics(dc, 0, 1, g);
 
             // Debug - render boxes around characters
index 2aa24a1..d0d26b0 100755 (executable)
@@ -1603,87 +1603,6 @@ public class ResidueProperties
     return ss.toString();
   }
 
-  /**
-   * Used by getRNASecStrucState
-   * 
-   */
-  public static Hashtable<String, String> toRNAssState;
-
-  public static boolean RNAcloseParen[] = new boolean[255];
-  static
-  {
-    toRNAssState = new Hashtable<String, String>();
-    toRNAssState.put(")", "(");
-    toRNAssState.put("(", "(");
-    toRNAssState.put("]", "[");
-    toRNAssState.put("[", "[");
-    toRNAssState.put("{", "{");
-    toRNAssState.put("}", "{");
-    toRNAssState.put(">", ">");
-    toRNAssState.put("<", ">");
-    toRNAssState.put("A", "A");
-    toRNAssState.put("a", "A");
-    toRNAssState.put("B", "B");
-    toRNAssState.put("b", "B");
-    toRNAssState.put("C", "C");
-    toRNAssState.put("c", "C");
-    toRNAssState.put("D", "D");
-    toRNAssState.put("d", "D");
-    toRNAssState.put("E", "E");
-    toRNAssState.put("e", "E");
-    toRNAssState.put("F", "F");
-    toRNAssState.put("f", "F");
-    toRNAssState.put("G", "G");
-    toRNAssState.put("g", "G");
-    toRNAssState.put("H", "H");
-    toRNAssState.put("h", "H");
-    toRNAssState.put("I", "I");
-    toRNAssState.put("i", "I");
-    toRNAssState.put("J", "J");
-    toRNAssState.put("j", "J");
-    toRNAssState.put("K", "K");
-    toRNAssState.put("k", "K");
-    toRNAssState.put("L", "L");
-    toRNAssState.put("l", "L");
-    toRNAssState.put("M", "M");
-    toRNAssState.put("m", "M");
-    toRNAssState.put("N", "N");
-    toRNAssState.put("n", "N");
-    toRNAssState.put("O", "O");
-    toRNAssState.put("o", "O");
-    toRNAssState.put("P", "P");
-    toRNAssState.put("p", "P");
-    toRNAssState.put("Q", "Q");
-    toRNAssState.put("q", "Q");
-    toRNAssState.put("R", "R");
-    toRNAssState.put("r", "R");
-    toRNAssState.put("S", "S");
-    toRNAssState.put("s", "S");
-    toRNAssState.put("T", "T");
-    toRNAssState.put("t", "T");
-    toRNAssState.put("U", "U");
-    toRNAssState.put("u", "U");
-    toRNAssState.put("V", "V");
-    toRNAssState.put("v", "V");
-    toRNAssState.put("W", "W");
-    toRNAssState.put("w", "W");
-    toRNAssState.put("X", "X");
-    toRNAssState.put("x", "X");
-    toRNAssState.put("Y", "Y");
-    toRNAssState.put("y", "Y");
-    toRNAssState.put("Z", "Z");
-    toRNAssState.put("z", "Z");
-    for (int p = 0; p < RNAcloseParen.length; p++)
-    {
-      RNAcloseParen[p] = false;
-    }
-    for (String k : toRNAssState.keySet())
-    {
-      RNAcloseParen[k.charAt(0)] = k.charAt(0) != toRNAssState.get(k)
-              .charAt(0);
-    }
-  }
-
   static
   {
     modifications.put("MSE", "MET"); // Selenomethionine
@@ -3008,40 +2927,6 @@ public class ResidueProperties
     return canonical == null ? aa : canonical;
   }
 
-  /**
-   * translate to RNA secondary structure representation
-   * 
-   * @param ssstring
-   * @return ssstring as a RNA-state secondary structure assignment.
-   */
-  public static String getRNASecStrucState(String ssstring)
-  {
-    if (ssstring == null)
-    {
-      return null;
-    }
-    StringBuffer ss = new StringBuffer();
-    for (int i = 0; i < ssstring.length(); i++)
-    {
-      String ssc = ssstring.substring(i, i + 1);
-      if (toRNAssState.containsKey(ssc))
-      {
-        // valid ss character - so return it
-        ss.append(ssc); // (String) toRNAssState.get(ssc));
-      }
-      else
-      {
-        ss.append(" ");
-      }
-    }
-    return ss.toString();
-  }
-
-  public static boolean isCloseParenRNA(char dc)
-  {
-    return RNAcloseParen[dc];
-  }
-
   // main method generates perl representation of residue property hash
   // / cut here
   public static void main(String[] args)
index 5801437..95c37ac 100644 (file)
@@ -21,6 +21,9 @@
 package jalview.analysis;
 
 import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertFalse;
+import static org.testng.AssertJUnit.assertNull;
+import static org.testng.AssertJUnit.assertTrue;
 import static org.testng.AssertJUnit.fail;
 
 import jalview.analysis.SecStrConsensus.SimpleBP;
@@ -35,11 +38,12 @@ public class RnaTest
   public void testGetSimpleBPs() throws WUSSParseException
   {
     String rna = "([{})]"; // JAL-1081 example
-    Vector<SimpleBP> bps = Rna.GetSimpleBPs(rna);
+    Vector<SimpleBP> bps = Rna.getSimpleBPs(rna);
     assertEquals(3, bps.size());
 
     /*
      * the base pairs are added in the order in which the matching base is found
+     * (popping the stack of unmatched opening brackets)
      */
     assertEquals(2, bps.get(0).bp5); // {
     assertEquals(3, bps.get(0).bp3); // }
@@ -55,25 +59,170 @@ public class RnaTest
     String rna = "(([{})]";
     try
     {
-      Rna.GetSimpleBPs(rna);
+      Rna.getSimpleBPs(rna);
       fail("expected exception");
     } catch (WUSSParseException e)
     {
-      // expected
+      // error reported as after end of input string
+      assertEquals(rna.length(), e.getProblemPos());
     }
   }
 
   @Test(groups = { "Functional" })
   public void testGetSimpleBPs_unmatchedCloser()
   {
-    String rna = "([{})]]";
+    String rna = "([{})]]]";
     try
     {
-      Rna.GetSimpleBPs(rna);
+      Rna.getSimpleBPs(rna);
       fail("expected exception");
     } catch (WUSSParseException e)
     {
-      // expected
+      // error reported as at first unmatched close
+      assertEquals(6, e.getProblemPos());
+    }
+
+    /*
+     * a variant where we have no opening bracket of the same type
+     * as the unmatched closing bracket (no stack rather than empty stack)
+     */
+    rna = "((()])";
+    try
+    {
+      Rna.getSimpleBPs(rna);
+      fail("expected exception");
+    } catch (WUSSParseException e)
+    {
+      assertEquals(4, e.getProblemPos());
+    }
+  }
+
+  @Test(groups = { "Functional" })
+  public void testGetRNASecStrucState()
+  {
+    assertNull(Rna.getRNASecStrucState(null));
+    for (int i = 0; i <= 255; i++)
+    {
+      String s = String.valueOf((char) i);
+      String ss = Rna.getRNASecStrucState(s);
+  
+      /*
+       * valid SS chars are a-z, A-Z, and various brackets;
+       * anything else is returned as a space
+       */
+      if ((i >= 'a' && i <= 'z') || (i >= 'A' && i <= 'Z')
+              || "()[]{}<>".indexOf(s) > -1)
+      {
+        assertEquals("" + i, s, ss);
+      }
+      else
+      {
+        assertEquals(" ", ss);
+      }
+    }
+  
+    /*
+     * a string is processed character by character
+     */
+    assertEquals("a [K ]z} {Q b(w)p><i",
+            Rna.getRNASecStrucState("a.[K-]z}?{Q b(w)p><i"));
+  }
+
+  @Test(groups = { "Functional" })
+  public void testIsClosingParenthesis()
+  {
+    /*
+     * only a-z, )]}> are closing bracket symbols
+     */
+    for (int i = 0; i <= 255; i++)
+    {
+      boolean isClosing = Rna.isClosingParenthesis((char) i);
+      if ((i >= 'a' && i <= 'z') || i == ')' || i == '}' || i == ']'
+              || i == '>')
+      {
+        assertTrue(String.format("close base pair %c", i), isClosing);
+      }
+      else
+      {
+        assertFalse(String.format("close base pair %c", i), isClosing);
+      }
+    }
+  }
+
+  @Test(groups = { "Functional" })
+  public void testIsCanonicalOrWobblePair()
+  {
+    String bases = "acgtuACGTU";
+    for (int i = 0; i < bases.length(); i++)
+    {
+      for (int j = 0; j < bases.length(); j++)
+      {
+        char first = bases.charAt(i);
+        char second = bases.charAt(j);
+        boolean result = Rna.isCanonicalOrWobblePair(first, second);
+        String pair = new String(new char[] { first, second })
+                .toUpperCase();
+        if (pair.equals("AT") || pair.equals("TA") || pair.equals("AU")
+                || pair.equals("UA") || pair.equals("GC")
+                || pair.equals("CG") || pair.equals("GT")
+                || pair.equals("TG") || pair.equals("GU")
+                || pair.equals("UG"))
+        {
+          assertTrue(pair + " should be valid", result);
+        }
+        else
+        {
+          assertFalse(pair + " should be invalid", result);
+        }
+      }
+    }
+  }
+
+  @Test(groups = { "Functional" })
+  public void testIsOpeningParenthesis()
+  {
+    /*
+     * only A-Z, ([{< are opening bracket symbols
+     */
+    for (int i = 0; i <= 255; i++)
+    {
+      boolean isOpening = Rna.isOpeningParenthesis((char) i);
+      if ((i >= 'A' && i <= 'Z') || i == '(' || i == '{' || i == '['
+              || i == '<')
+      {
+        assertTrue(String.format("Open base pair %c", i), isOpening);
+      }
+      else
+      {
+        assertFalse(String.format("Open base pair %c", i), isOpening);
+      }
+    }
+  }
+
+  @Test(groups = { "Functional" })
+  public void testGetMatchingOpeningParenthesis() throws WUSSParseException
+  {
+    for (int i = 0; i <= 255; i++)
+    {
+      boolean isClosing = Rna.isClosingParenthesis((char) i);
+      if (isClosing)
+      {
+        char opening = Rna.getMatchingOpeningParenthesis((char) i);
+        if (i >= 'a' && i <= 'z')
+        {
+          assertEquals(i + 'A' - 'a', opening);
+        }
+        else if (i == ')' && opening == '(' || i == ']' && opening == '['
+                || i == '}' && opening == '{' || i == '>' && opening == '<')
+        {
+          // ok
+        }
+        else
+        {
+          fail("Got " + opening + " as opening bracket pair for "
+                  + ((char) i));
+        }
+      }
     }
   }
 }
index 271162b..1aff519 100644 (file)
@@ -219,4 +219,65 @@ public class AlignmentAnnotationTests
     assertEquals(1, ann.annotations[1].value, 0.001);
     assertEquals(2, ann.annotations[2].value, 0.001);
   }
-}
+
+  /**
+   * Test the method that defaults rna symbol to the one matching the preceding
+   * unmatched opening bracket (if any)
+   */
+  @Test(groups = { "Functional" })
+  public void testGetDefaultRnaHelixSymbol()
+  {
+    AlignmentAnnotation ann = new AlignmentAnnotation("SS",
+            "secondary structure", null);
+    assertEquals("(", ann.getDefaultRnaHelixSymbol(4));
+
+    Annotation[] anns = new Annotation[20];
+    ann.annotations = anns;
+    assertEquals("(", ann.getDefaultRnaHelixSymbol(4));
+
+    anns[1] = new Annotation("(", "S", '(', 0f);
+    assertEquals("(", ann.getDefaultRnaHelixSymbol(0));
+    assertEquals("(", ann.getDefaultRnaHelixSymbol(1));
+    assertEquals(")", ann.getDefaultRnaHelixSymbol(2));
+    assertEquals(")", ann.getDefaultRnaHelixSymbol(3));
+
+    /*
+     * .(.[.{.<.}.>.).].
+     */
+    anns[1] = new Annotation("(", "S", '(', 0f);
+    anns[3] = new Annotation("[", "S", '[', 0f);
+    anns[5] = new Annotation("{", "S", '{', 0f);
+    anns[7] = new Annotation("<", "S", '<', 0f);
+    anns[9] = new Annotation("}", "S", '}', 0f);
+    anns[11] = new Annotation(">", "S", '>', 0f);
+    anns[13] = new Annotation(")", "S", ')', 0f);
+    anns[15] = new Annotation("]", "S", ']', 0f);
+
+    String expected = "(())]]}}>>>>]]]](";
+    for (int i = 0; i < expected.length(); i++)
+    {
+      assertEquals("column " + i, String.valueOf(expected.charAt(i)),
+              ann.getDefaultRnaHelixSymbol(i));
+    }
+
+    /*
+     * .(.[.(.).{.}.<.].D.
+     */
+    anns[1] = new Annotation("(", "S", '(', 0f);
+    anns[3] = new Annotation("[", "S", '[', 0f);
+    anns[5] = new Annotation("(", "S", '(', 0f);
+    anns[7] = new Annotation(")", "S", ')', 0f);
+    anns[9] = new Annotation("{", "S", '{', 0f);
+    anns[11] = new Annotation("}", "S", '}', 0f);
+    anns[13] = new Annotation("<", "S", '>', 0f);
+    anns[15] = new Annotation("]", "S", ']', 0f);
+    anns[17] = new Annotation("D", "S", 'D', 0f);
+
+    expected = "(())]]))]]}}]]>>>>dd";
+    for (int i = 0; i < expected.length(); i++)
+    {
+      assertEquals("column " + i, String.valueOf(expected.charAt(i)),
+              ann.getDefaultRnaHelixSymbol(i));
+    }
+  }
+}
\ No newline at end of file