9582e2ea1938c7bfcac3d25844573a8a986c542d
[jalview.git] / src / jalview / util / DnaUtils.java
1 package jalview.util;
2
3 import java.text.ParseException;
4 import java.util.ArrayList;
5 import java.util.Collections;
6 import java.util.List;
7
8 public class DnaUtils
9 {
10
11   /**
12    * Parses an ENA/GenBank format location specifier and returns a list of
13    * [start, end] ranges. Throws an exception if not able to parse.
14    * <p>
15    * Currently we do not parse "order()" specifiers, or indeterminate ranges of
16    * the format "&lt;start..end" or "start..&gt;end" or "start.end" or
17    * "start^end"
18    * 
19    * @param location
20    * @return
21    * @throws ParseException
22    *           if unable to parse the location (the exception message is the
23    *           location specifier being parsed); we use ParseException in
24    *           preference to the unchecked IllegalArgumentException
25    * @see http://www.insdc.org/files/feature_table.html#3.4
26    */
27   public static List<int[]> parseLocation(String location)
28           throws ParseException
29   {
30     if (location.startsWith("join("))
31     {
32       return parseJoin(location);
33     }
34     else if (location.startsWith("complement("))
35     {
36       return parseComplement(location);
37     }
38     if (location.startsWith("order("))
39     {
40       throw new ParseException(location, 0);
41     }
42
43     /*
44      * try to parse m..n (or simply m)
45      */
46     String[] range = location.split("\\.\\.");
47     if (range.length == 1 || range.length == 2)
48     {
49       try
50       {
51         int start = Integer.valueOf(range[0]);
52         int end = range.length == 1 ? start : Integer.valueOf(range[1]);
53         return Collections.singletonList(new int[] { start, end });
54       } catch (NumberFormatException e)
55       {
56         /*
57          * could be a location like <1..888 or 1..>888
58          */
59         throw new ParseException(location, 0);
60       }
61     }
62     else
63     {
64       /*
65        * could be a location like 102.110 or 123^124
66        */
67       throw new ParseException(location, 0);
68     }
69   }
70
71   /**
72    * Parses a complement(locationSpec) into a list of start-end ranges
73    * 
74    * @param location
75    * @return
76    * @throws ParseException
77    */
78   static List<int[]> parseComplement(String location) throws ParseException
79   {
80     /*
81      * take what is inside complement()
82      */
83     if (!location.endsWith(")"))
84     {
85       throw new ParseException(location, 0);
86     }
87     String toComplement = location.substring("complement(".length(),
88             location.length() - 1);
89     List<int[]> ranges = parseLocation(toComplement);
90
91     /*
92      * reverse the order and direction of ranges
93      */
94     Collections.reverse(ranges);
95     for (int[] range : ranges)
96     {
97       int temp = range[0];
98       range[0] = range[1];
99       range[1] = temp;
100     }
101     return ranges;
102   }
103
104   /**
105    * Parses a join(loc1,loc2,...,locn) into a list of start-end ranges
106    * 
107    * @param location
108    * @return
109    * @throws ParseException
110    */
111   static List<int[]> parseJoin(String location) throws ParseException
112   {
113     List<int[]> ranges = new ArrayList<int[]>();
114
115     /*
116      * take what is inside join()
117      */
118     if (!location.endsWith(")"))
119     {
120       throw new ParseException(location, 0);
121     }
122     String joinedLocs = location.substring("join(".length(),
123             location.length() - 1);
124     String[] locations = joinedLocs.split(",");
125     for (String loc : locations)
126     {
127       List<int[]> range = parseLocation(loc);
128       ranges.addAll(range);
129     }
130     return ranges;
131   }
132
133 }