Merge branch 'develop' into task/JAL-3796_notarization
[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     if (location.startsWith("join("))
51     {
52       return parseJoin(location);
53     }
54     else if (location.startsWith("complement("))
55     {
56       return parseComplement(location);
57     }
58     if (location.startsWith("order("))
59     {
60       throw new ParseException(location, 0);
61     }
62
63     /*
64      * try to parse m..n (or simply m)
65      */
66     String[] range = location.split("\\.\\.");
67     if (range.length == 1 || range.length == 2)
68     {
69       try
70       {
71         int start = Integer.valueOf(range[0]);
72         int end = range.length == 1 ? start : Integer.valueOf(range[1]);
73         return Collections.singletonList(new int[] { start, end });
74       } catch (NumberFormatException e)
75       {
76         /*
77          * could be a location like <1..888 or 1..>888
78          */
79         throw new ParseException(location, 0);
80       }
81     }
82     else
83     {
84       /*
85        * could be a location like 102.110 or 123^124
86        */
87       throw new ParseException(location, 0);
88     }
89   }
90
91   /**
92    * Parses a complement(locationSpec) into a list of start-end ranges
93    * 
94    * @param location
95    * @return
96    * @throws ParseException
97    */
98   static List<int[]> parseComplement(String location) throws ParseException
99   {
100     /*
101      * take what is inside complement()
102      */
103     if (!location.endsWith(")"))
104     {
105       throw new ParseException(location, 0);
106     }
107     String toComplement = location.substring("complement(".length(),
108             location.length() - 1);
109     List<int[]> ranges = parseLocation(toComplement);
110
111     /*
112      * reverse the order and direction of ranges
113      */
114     Collections.reverse(ranges);
115     for (int[] range : ranges)
116     {
117       int temp = range[0];
118       range[0] = range[1];
119       range[1] = temp;
120     }
121     return ranges;
122   }
123
124   /**
125    * Parses a join(loc1,loc2,...,locn) into a list of start-end ranges
126    * 
127    * @param location
128    * @return
129    * @throws ParseException
130    */
131   static List<int[]> parseJoin(String location) throws ParseException
132   {
133     List<int[]> ranges = new ArrayList<int[]>();
134
135     /*
136      * take what is inside join()
137      */
138     if (!location.endsWith(")"))
139     {
140       throw new ParseException(location, 0);
141     }
142     String joinedLocs = location.substring("join(".length(),
143             location.length() - 1);
144     String[] locations = joinedLocs.split(",");
145     for (String loc : locations)
146     {
147       List<int[]> range = parseLocation(loc);
148       ranges.addAll(range);
149     }
150     return ranges;
151   }
152
153 }