From: gmungoc Date: Fri, 26 Aug 2016 11:15:06 +0000 (+0100) Subject: Merge branch 'develop' into bug/JAL-1841rnaSecStr X-Git-Tag: Release_2_10_0~70^2~2 X-Git-Url: http://source.jalview.org/gitweb/?a=commitdiff_plain;h=0ca96db9937136626dcd867e122d1762b28e15bd;hp=b186ddae1bf9d9ae8e61bc2a3c1854c13d21cc87;p=jalview.git Merge branch 'develop' into bug/JAL-1841rnaSecStr --- diff --git a/resources/lang/Messages.properties b/resources/lang/Messages.properties index 54181fe..b01464a 100644 --- a/resources/lang/Messages.properties +++ b/resources/lang/Messages.properties @@ -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 diff --git a/resources/lang/Messages_es.properties b/resources/lang/Messages_es.properties index 45941c2..87538be 100644 --- a/resources/lang/Messages_es.properties +++ b/resources/lang/Messages_es.properties @@ -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 ignorar 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 diff --git a/src/jalview/analysis/Rna.java b/src/jalview/analysis/Rna.java index e752126..f497f0e 100644 --- a/src/jalview/analysis/Rna.java +++ b/src/jalview/analysis/Rna.java @@ -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 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 openingParsSet = new HashSet( - Arrays.asList(openingPars)); - - private static HashSet closingParsSet = new HashSet( - Arrays.asList(closingPars)); - - private static Hashtable closingToOpening = new Hashtable() - // 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 GetSimpleBPs(CharSequence line) + public static Vector getSimpleBPs(CharSequence line) throws WUSSParseException { Hashtable> stacks = new Hashtable>(); @@ -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 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 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 bps) throws WUSSParseException { - Vector 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 GetModeleBP(CharSequence line) + public static List getModeleBP(CharSequence line) throws WUSSParseException { - Vector bps = GetSimpleBPs(line); + Vector bps = getSimpleBPs(line); return new ArrayList(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 helices = new Hashtable(); + // 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; + } + } } diff --git a/src/jalview/analysis/StructureFrequency.java b/src/jalview/analysis/StructureFrequency.java index 88387dd..479c8562 100644 --- a/src/jalview/analysis/StructureFrequency.java +++ b/src/jalview/analysis/StructureFrequency.java @@ -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))); } } } diff --git a/src/jalview/appletgui/AnnotationPanel.java b/src/jalview/appletgui/AnnotationPanel.java index e5475f8..6012c1a 100755 --- a/src/jalview/appletgui/AnnotationPanel.java +++ b/src/jalview/appletgui/AnnotationPanel.java @@ -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); diff --git a/src/jalview/datamodel/AlignmentAnnotation.java b/src/jalview/datamodel/AlignmentAnnotation.java index 7990a5c..2a89fa1 100755 --- a/src/jalview/datamodel/AlignmentAnnotation.java +++ b/src/jalview/datamodel/AlignmentAnnotation.java @@ -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 bps = null; + public List 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++; diff --git a/src/jalview/gui/AnnotationPanel.java b/src/jalview/gui/AnnotationPanel.java index 3c9a13e..6a621ff 100755 --- a/src/jalview/gui/AnnotationPanel.java +++ b/src/jalview/gui/AnnotationPanel.java @@ -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); } diff --git a/src/jalview/io/RnamlFile.java b/src/jalview/io/RnamlFile.java index 0941a6f..f48f825 100644 --- a/src/jalview/io/RnamlFile.java +++ b/src/jalview/io/RnamlFile.java @@ -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 allarray = new ArrayList(); - ArrayList> BP = new ArrayList(); - ArrayList strucinarray = new ArrayList(); - SequenceI[] seqs = new SequenceI[result.size()]; + // ArrayList allarray = new ArrayList(); + // ArrayList> 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 getRNA() { return result; } diff --git a/src/jalview/io/StockholmFile.java b/src/jalview/io/StockholmFile.java index ab80ffa..bec7d82 100644 --- a/src/jalview/io/StockholmFile.java +++ b/src/jalview/io/StockholmFile.java @@ -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 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 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 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 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) diff --git a/src/jalview/renderer/AnnotationRenderer.java b/src/jalview/renderer/AnnotationRenderer.java index 073ea92..94015de 100644 --- a/src/jalview/renderer/AnnotationRenderer.java +++ b/src/jalview/renderer/AnnotationRenderer.java @@ -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 diff --git a/src/jalview/schemes/ResidueProperties.java b/src/jalview/schemes/ResidueProperties.java index 2aa24a1..d0d26b0 100755 --- a/src/jalview/schemes/ResidueProperties.java +++ b/src/jalview/schemes/ResidueProperties.java @@ -1603,87 +1603,6 @@ public class ResidueProperties return ss.toString(); } - /** - * Used by getRNASecStrucState - * - */ - public static Hashtable toRNAssState; - - public static boolean RNAcloseParen[] = new boolean[255]; - static - { - toRNAssState = new Hashtable(); - 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) diff --git a/test/jalview/analysis/RnaTest.java b/test/jalview/analysis/RnaTest.java index 5801437..95c37ac 100644 --- a/test/jalview/analysis/RnaTest.java +++ b/test/jalview/analysis/RnaTest.java @@ -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 bps = Rna.GetSimpleBPs(rna); + Vector 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> 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)); + } + } } } } diff --git a/test/jalview/datamodel/AlignmentAnnotationTests.java b/test/jalview/datamodel/AlignmentAnnotationTests.java index 271162b..1aff519 100644 --- a/test/jalview/datamodel/AlignmentAnnotationTests.java +++ b/test/jalview/datamodel/AlignmentAnnotationTests.java @@ -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