JAL-2629 tidy tests, refactor hmmer node mapping slightly
[jalview.git] / src / jalview / io / HMMFile.java
index 95c6f32..ca5cf1c 100644 (file)
@@ -23,25 +23,25 @@ import java.util.Scanner;
 public class HMMFile extends AlignFile
         implements AlignmentFileReaderI, AlignmentFileWriterI
 {
-  // HMM to store file data
-  private HiddenMarkovModel hmm;
-
-  // number of possible transitions
   private static final int NUMBER_OF_TRANSITIONS = 7;
 
-  private String NL = "\n";
+  private static final String SPACE = " ";
 
-  //number of symbols in the alphabet used in the hidden Markov model
-  int numberOfSymbols;
+  private static final String COMPO = "COMPO";
 
-  private final String SPACE = " ";
+  private static final String EMPTY = "";
 
-  private final String COMPO = "COMPO";
+  /*
+   * guide line added to an output HMMER file, purely for readability
+   */
+  private static final String TRANSITIONTYPELINE = "            m->m     m->i     m->d     i->m     i->i     d->m     d->d";
 
-  private final String EMPTY = "";
+  private static String NL = "\n";
 
-  //This is a line that needs to be added to each HMMER� file. It is purely for readability.
-  private static final String TRANSITIONTYPELINE = "            m->m     m->i     m->d     i->m     i->i     d->m     d->d";
+  private HiddenMarkovModel hmm;
+
+  // number of symbols in the alphabet used in the hidden Markov model
+  int numberOfSymbols;
 
   /**
    * Parses immediately.
@@ -223,23 +223,24 @@ public class HMMFile extends AlignFile
    */
   void parseModel(BufferedReader input) throws IOException
   {
+    boolean first = true;
     String line = input.readLine();
-    int node = 0;
     while (!"//".equals(line))
     {
-      hmm.getNodes().add(new HMMNode());
-      String next;
+      HMMNode node = new HMMNode();
+      hmm.getNodes().add(node);
       Scanner matchReader = new Scanner(line);
-      next = matchReader.next();
-      if (next.equals(COMPO) || node > 0)
+      String next = matchReader.next();
+      if (next.equals(COMPO) || !first)
       {
         // stores match emission line in list
         List<Double> matches = new ArrayList<>();
         matches = fillList(matchReader, numberOfSymbols);
-        hmm.getNodes().get(node).setMatchEmissions(matches);
-        if (node > 0)
+        node.setMatchEmissions(matches);
+        if (!first)
         {
-          parseAnnotations(matchReader, node);
+          int column = parseAnnotations(matchReader, node);
+          hmm.setAlignmentColumn(node, column - 1);
         }
       }
       matchReader.close();
@@ -248,7 +249,7 @@ public class HMMFile extends AlignFile
       Scanner insertReader = new Scanner(line);
       List<Double> inserts = new ArrayList<>();
       inserts = fillList(insertReader, numberOfSymbols);
-      hmm.getNodes().get(node).setInsertEmissions(inserts);
+      node.setInsertEmissions(inserts);
       insertReader.close();
 
       // stores state transition line in list
@@ -256,68 +257,85 @@ public class HMMFile extends AlignFile
       Scanner transitionReader = new Scanner(line);
       List<Double> transitions = new ArrayList<>();
       transitions = fillList(transitionReader, NUMBER_OF_TRANSITIONS);
-      hmm.getNodes().get(node).setStateTransitions(transitions);
+      node.setStateTransitions(transitions);
       transitionReader.close();
       line = input.readLine();
-      node++;
-    }
 
+      first = false;
+    }
   }
 
   /**
-   * Parses the annotations on the match emission line.
+   * Parses the annotations on the match emission line and add them to the node.
+   * (See p109 of the HMMER User Guide (V3.1b2) for the specification.) Returns
+   * the alignment column number (base 1) that the node maps to, if provided,
+   * else zero.
    * 
    * @param scanner
-   *          The scanner which is processing match emission line.
-   * @param index
-   *          The index of node which is being scanned.
+   * @param node
    */
-  void parseAnnotations(Scanner scanner, int index)
+  int parseAnnotations(Scanner scanner, HMMNode node)
   {
+    /*
+     * map from hmm node to alignment column index, if provided
+     * HMM counts columns from 1, convert to base 0 for Jalview
+     */
+    int column = 0;
     if (hmm.mapIsActive() && scanner.hasNext())
     {
-      int column;
       column = scanner.nextInt();
-      hmm.getNodes().get(index).setAlignmentColumn(column - 1);
-      hmm.getNodeLookup().put(column - 1, index);
+      node.setAlignmentColumn(column - 1);
     }
     else
     {
       scanner.next();
     }
 
+    /*
+     * hmm consensus residue if provided, else -
+     */
     if (scanner.hasNext())
     {
       char consensusR;
       consensusR = charValue(scanner.next());
-      hmm.getNodes().get(index).setConsensusResidue(consensusR);
+      node.setConsensusResidue(consensusR);
     }
 
+    /*
+     * RF reference annotation, if provided, else -
+     */
     if (scanner.hasNext())
     {
       char reference;
       reference = charValue(scanner.next());
-      hmm.getNodes().get(index).setReferenceAnnotation(reference);
+      node.setReferenceAnnotation(reference);
     }
 
+    /*
+     * 'm' for masked position, if provided, else -
+     */
     if (scanner.hasNext())
     {
       char value;
       value = charValue(scanner.next());
-      hmm.getNodes().get(index).setMaskValue(value);
+      node.setMaskValue(value);
     }
+
+    /*
+     * structure consensus symbol, if provided, else -
+     */
     if (scanner.hasNext())
     {
       char consensusS;
       consensusS = charValue(scanner.next());
-      hmm.getNodes().get(index).setConsensusStructure(consensusS);
+      node.setConsensusStructure(consensusS);
     }
-  }
-
 
+    return column;
+  }
 
   /**
-   * Fills a list of doubles based on an input line.
+   * Fills a list of doubles from an input line
    * 
    * @param input
    *          The scanner for the line containing the data to be transferred to