JAL-1260 refactored GenBank (and EMBL) flat file parser
[jalview.git] / src / jalview / util / DnaUtils.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.util;
22
23 import java.text.ParseException;
24 import java.util.ArrayList;
25 import java.util.Collections;
26 import java.util.List;
27
28 public class DnaUtils
29 {
30
31   /**
32    * Parses an ENA/GenBank format location specifier and returns a list of
33    * [start, end] ranges. Throws an exception if not able to parse.
34    * <p>
35    * Currently we do not parse "order()" specifiers, or indeterminate ranges of
36    * the format "&lt;start..end" or "start..&gt;end" or "start.end" or
37    * "start^end"
38    * 
39    * @param location
40    * @return
41    * @throws ParseException
42    *           if unable to parse the location (the exception message is the
43    *           location specifier being parsed); we use ParseException in
44    *           preference to the unchecked IllegalArgumentException
45    * @see http://www.insdc.org/files/feature_table.html#3.4
46    */
47   public static List<int[]> parseLocation(String location)
48           throws ParseException
49   {
50     location = location.trim(); // failsafe for untidy input data
51     if (location.startsWith("join("))
52     {
53       return parseJoin(location);
54     }
55     else if (location.startsWith("complement("))
56     {
57       return parseComplement(location);
58     }
59     if (location.startsWith("order("))
60     {
61       throw new ParseException(location, 0);
62     }
63
64     /*
65      * try to parse m..n (or simply m)
66      */
67     String[] range = location.split("\\.\\.");
68     if (range.length == 1 || range.length == 2)
69     {
70       try
71       {
72         int start = Integer.valueOf(range[0]);
73         int end = range.length == 1 ? start : Integer.valueOf(range[1]);
74         return Collections.singletonList(new int[] { start, end });
75       } catch (NumberFormatException e)
76       {
77         /*
78          * could be a location like <1..888 or 1..>888
79          */
80         throw new ParseException(location, 0);
81       }
82     }
83     else
84     {
85       /*
86        * could be a location like 102.110 or 123^124
87        */
88       throw new ParseException(location, 0);
89     }
90   }
91
92   /**
93    * Parses a complement(locationSpec) into a list of start-end ranges
94    * 
95    * @param location
96    * @return
97    * @throws ParseException
98    */
99   static List<int[]> parseComplement(String location) throws ParseException
100   {
101     /*
102      * take what is inside complement()
103      */
104     if (!location.endsWith(")"))
105     {
106       throw new ParseException(location, 0);
107     }
108     String toComplement = location.substring("complement(".length(),
109             location.length() - 1);
110     List<int[]> ranges = parseLocation(toComplement);
111
112     /*
113      * reverse the order and direction of ranges
114      */
115     Collections.reverse(ranges);
116     for (int[] range : ranges)
117     {
118       int temp = range[0];
119       range[0] = range[1];
120       range[1] = temp;
121     }
122     return ranges;
123   }
124
125   /**
126    * Parses a join(loc1,loc2,...,locn) into a list of start-end ranges
127    * 
128    * @param location
129    * @return
130    * @throws ParseException
131    */
132   static List<int[]> parseJoin(String location) throws ParseException
133   {
134     List<int[]> ranges = new ArrayList<int[]>();
135
136     /*
137      * take what is inside join()
138      */
139     if (!location.endsWith(")"))
140     {
141       throw new ParseException(location, 0);
142     }
143     String joinedLocs = location.substring("join(".length(),
144             location.length() - 1);
145     String[] locations = joinedLocs.split(",");
146     for (String loc : locations)
147     {
148       List<int[]> range = parseLocation(loc);
149       ranges.addAll(range);
150     }
151     return ranges;
152   }
153
154 }