JAL-2416 more test coverage of matrix parser
[jalview.git] / src / jalview / io / ScoreMatrixFile.java
index 4e89c3f..3a7ff4f 100644 (file)
@@ -42,7 +42,15 @@ public class ScoreMatrixFile extends AlignFile implements
 
   private String matrixName;
 
-  boolean lowerDiagonalOnly;
+  /*
+   * aaindex format has scores for diagonal and below only
+   */
+  boolean isLowerDiagonalOnly;
+
+  /*
+   * ncbi format has symbols as first column on score rows
+   */
+  boolean hasGuideColumn;
 
   /**
    * Constructor
@@ -93,7 +101,7 @@ public class ScoreMatrixFile extends AlignFile implements
     int row = 0;
     String err = null;
     String data;
-    lowerDiagonalOnly = false;
+    isLowerDiagonalOnly = false;
 
     while ((data = nextLine()) != null)
     {
@@ -111,8 +119,8 @@ public class ScoreMatrixFile extends AlignFile implements
          */
         if (name != null)
         {
-          System.err
-                  .println("Warning: 'ScoreMatrix' repeated in file at line "
+          throw new FileFormatException(
+                  "Error: 'ScoreMatrix' repeated in file at line "
                           + lineNo);
         }
         StringTokenizer nameLine = new StringTokenizer(data, DELIMITERS);
@@ -244,17 +252,8 @@ public class ScoreMatrixFile extends AlignFile implements
       {
         parseValues(data, lineNo, scores, row, alphabet);
         row++;
-        if (row == size)
-        {
-          break;
-        }
       }
     }
-    if (data != null)
-    {
-      System.err.println("Warning: unexpected extra data in matrix file: "
-              + data);
-    }
 
     ScoreMatrix sm = new ScoreMatrix(name, alphabet, scores);
     sm.setDescription(description);
@@ -289,7 +288,24 @@ public class ScoreMatrixFile extends AlignFile implements
     StringTokenizer scoreLine = new StringTokenizer(data, DELIMITERS);
 
     int tokenCount = scoreLine.countTokens();
-    if (tokenCount == size + 1)
+
+    /*
+     * inspect first row to see if it includes the symbol in the first column,
+     * and to see if it is lower diagonal values only (i.e. just one score)
+     */
+    if (row == 0)
+    {
+      if (data.startsWith(String.valueOf(alphabet[0])))
+      {
+        hasGuideColumn = true;
+      }
+      if (tokenCount == (hasGuideColumn ? 2 : 1))
+      {
+        isLowerDiagonalOnly = true;
+      }
+    }
+
+    if (hasGuideColumn)
     {
       /*
        * check 'guide' symbol is the row'th letter of the alphabet
@@ -302,36 +318,32 @@ public class ScoreMatrixFile extends AlignFile implements
                         lineNo, alphabet[row], symbol);
         throw new FileFormatException(err);
       }
+      tokenCount = scoreLine.countTokens(); // excluding guide symbol
     }
 
-    tokenCount = scoreLine.countTokens();
-
     /*
-     * AAIndex format only has the lower diagonal i.e.
-     * 1 score in row 0, 2 in row 1, etc
-     * check this in all but the last row (which is the same either way)
+     * check the right number of values (lower diagonal or full format)
      */
-    if (row < size - 1)
+    if (isLowerDiagonalOnly && tokenCount != row + 1)
     {
-      boolean lowerDiagonal = tokenCount == row + 1;
-      if (lowerDiagonalOnly && !lowerDiagonal)
-      {
-        /*
-         * had detected lower diagonal form but now it isn't - error
-         */
-        err = String.format("Unexpected number of tokens at line %d",
-                lineNo);
+      err = String.format(
+              "Expected %d scores at line %d: '%s' but found %d", row + 1,
+              lineNo, data, tokenCount);
         throw new FileFormatException(err);
-      }
-      lowerDiagonalOnly = lowerDiagonal;
     }
 
-    if (!lowerDiagonalOnly && tokenCount != size)
+    if (!isLowerDiagonalOnly && tokenCount != size)
     {
-      err = String.format("Expected %d scores at line %d but found %d",
-              size, lineNo, scoreLine.countTokens());
+      err = String.format(
+              "Expected %d scores at line %d: '%s' but found %d", size,
+              lineNo, data, scoreLine.countTokens());
       throw new FileFormatException(err);
     }
+
+    /*
+     * parse and set the values, setting the symmetrical value
+     * as well if lower diagonal format data
+     */
     scores[row] = new float[size];
     int col = 0;
     String value = null;
@@ -341,7 +353,7 @@ public class ScoreMatrixFile extends AlignFile implements
       {
         value = scoreLine.nextToken();
         scores[row][col] = Float.valueOf(value);
-        if (lowerDiagonalOnly)
+        if (isLowerDiagonalOnly)
         {
           scores[col][row] = scores[row][col];
         }
@@ -415,37 +427,6 @@ public class ScoreMatrixFile extends AlignFile implements
     return false;
   }
 
-  /**
-   * Answers true if the data line consists of the alphabet characters,
-   * delimited (as to provide a heading row). Otherwise returns false (e.g. if
-   * the data is a row of score values).
-   * 
-   * @param data
-   * @param alphabet
-   * @return
-   */
-  private boolean isHeaderLine(String data, String alphabet)
-  {
-    StringTokenizer scoreLine = new StringTokenizer(data, DELIMITERS);
-    int i = 0;
-    while (scoreLine.hasMoreElements())
-    {
-      /*
-       * skip over characters in the alphabet that are 
-       * also a delimiter (e.g. space)
-       */
-      char symbol = alphabet.charAt(i++);
-      if (!DELIMITERS.contains(String.valueOf(symbol)))
-      {
-        if (!String.valueOf(symbol).equals(scoreLine.nextToken()))
-        {
-          return false;
-        }
-      }
-    }
-    return true;
-  }
-
   public String getMatrixName()
   {
     return matrixName;