JAL-2114 parser + tests for GenBank location descriptors
[jalview.git] / src / jalview / util / DnaUtils.java
diff --git a/src/jalview/util/DnaUtils.java b/src/jalview/util/DnaUtils.java
new file mode 100644 (file)
index 0000000..639eb8e
--- /dev/null
@@ -0,0 +1,131 @@
+package jalview.util;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class DnaUtils
+{
+
+  /**
+   * Parses an ENA/GenBank format location specifier and returns a list of
+   * [start, end] ranges. Returns null if not able to parse.
+   * 
+   * @param location
+   * @return
+   * @see http://www.insdc.org/files/feature_table.html#3.4
+   */
+  public static List<int[]> parseLocation(String location)
+  {
+    if (location.startsWith("join("))
+    {
+      return parseJoin(location);
+    }
+    else if (location.startsWith("complement("))
+    {
+      return parseComplement(location);
+    }
+    String errorMessage = "Unable to process location specifier: "
+            + location;
+    if (location.startsWith("order("))
+    {
+      System.err.println(errorMessage);
+      return null;
+    }
+    String[] range = location.split("\\.\\.");
+    if (range.length == 2)
+    {
+      try
+      {
+        int start = Integer.valueOf(range[0]);
+        int end = Integer.valueOf(range[1]);
+        return Collections.singletonList(new int[] { start, end });
+      } catch (NumberFormatException e)
+      {
+        /*
+         * could be a location like <1..888 or 1..>888
+         */
+        System.err.println(errorMessage);
+        return null;
+      }
+    }
+    else
+    {
+      /*
+       * could be a location like 102.110 or 123^124
+       */
+      System.err.println(errorMessage);
+      return null;
+    }
+  }
+
+  /**
+   * Parses a complement(locationSpec) into a list of start-end ranges
+   * 
+   * @param location
+   * @return
+   */
+  static List<int[]> parseComplement(String location)
+  {
+    /*
+     * take what is inside complement()
+     */
+    String toComplement = location.substring("complement(".length(),
+            location.length() - 1);
+    List<int[]> ranges = parseLocation(toComplement);
+    if (ranges == null)
+    {
+      /*
+       * something bad in there
+       */
+      return null;
+    }
+
+    /*
+     * reverse the order and direction of ranges
+     */
+    Collections.reverse(ranges);
+    for (int[] range : ranges)
+    {
+      int temp = range[0];
+      range[0] = range[1];
+      range[1] = temp;
+    }
+    return ranges;
+  }
+
+  /**
+   * Parses a join(loc1,loc2,...,locn) into a list of start-end ranges
+   * 
+   * @param location
+   * @return
+   */
+  static List<int[]> parseJoin(String location)
+  {
+    List<int[]> ranges = new ArrayList<int[]>();
+
+    /*
+     * take what is inside join()
+     */
+    String joinedLocs = location.substring("join(".length(),
+            location.length() - 1);
+    String[] locations = joinedLocs.split(",");
+    for (String loc : locations)
+    {
+      List<int[]> range = parseLocation(loc);
+      if (range == null)
+      {
+        /*
+         * something bad in there
+         */
+        return null;
+      }
+      else
+      {
+        ranges.addAll(range);
+      }
+    }
+    return ranges;
+  }
+
+}