efficiency
[vamsas.git] / src / uk / ac / vamsas / objects / utils / Range.java
1 /*\r
2  * This file is part of the Vamsas Client version 0.1. \r
3  * Copyright 2009 by Jim Procter, Iain Milne, Pierre Marguerite, \r
4  *  Andrew Waterhouse and Dominik Lindner.\r
5  * \r
6  * Earlier versions have also been incorporated into Jalview version 2.4 \r
7  * since 2008, and TOPALi version 2 since 2007.\r
8  * \r
9  * The Vamsas Client is free software: you can redistribute it and/or modify\r
10  * it under the terms of the GNU Lesser General Public License as published by\r
11  * the Free Software Foundation, either version 3 of the License, or\r
12  * (at your option) any later version.\r
13  *  \r
14  * The Vamsas Client is distributed in the hope that it will be useful,\r
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
17  * GNU Lesser General Public License for more details.\r
18  * \r
19  * You should have received a copy of the GNU Lesser General Public License\r
20  * along with the Vamsas Client.  If not, see <http://www.gnu.org/licenses/>.\r
21  */\r
22 package uk.ac.vamsas.objects.utils;\r
23 \r
24 import java.util.Vector;\r
25 \r
26 import uk.ac.vamsas.objects.core.Local;\r
27 import uk.ac.vamsas.objects.core.MapType;\r
28 import uk.ac.vamsas.objects.core.Mapped;\r
29 import uk.ac.vamsas.objects.core.RangeType;\r
30 import uk.ac.vamsas.objects.core.Seg;\r
31 \r
32 /**\r
33  * Utilities for working with RangeType and MapType objects. Derived from bitter\r
34  * experience.\r
35  * \r
36  * @author JimP\r
37  * \r
38  */\r
39 public class Range {\r
40   static org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory\r
41       .getFactory().getLog(Range.class);\r
42 \r
43   /**\r
44    * get start<end range of segment, adjusting for inclusivity flag and\r
45    * polarity.\r
46    * \r
47    * @param visSeg\r
48    * @param ensureDirection\r
49    *          when true - always ensure start is less than end.\r
50    * @return int[] { start, end, direction} where direction==1 for range running\r
51    *         from end to start.\r
52    */\r
53   public static int[] getSegRange(Seg visSeg, boolean ensureDirection) {\r
54     boolean incl = visSeg.getInclusive();\r
55     // adjust for inclusive flag.\r
56     int pol = (visSeg.getStart() <= visSeg.getEnd()) ? 1 : -1; // polarity of\r
57     // region.\r
58     int start = visSeg.getStart() + (incl ? 0 : pol);\r
59     int end = visSeg.getEnd() + (incl ? 0 : -pol);\r
60     if (ensureDirection && pol == -1) {\r
61       // jalview doesn't deal with inverted ranges, yet.\r
62       int t = end;\r
63       end = start;\r
64       start = t;\r
65     }\r
66     return new int[] { start, end, pol < 0 ? 1 : 0 };\r
67   }\r
68 \r
69   /**\r
70    * get real bounds of a RangeType's specification. start and end are an\r
71    * inclusive range within which all segments and positions lie. TODO: refactor\r
72    * to vamsas utils\r
73    * \r
74    * @param dseta\r
75    * @return int[] { start, end}\r
76    */\r
77   public static int[] getBounds(RangeType dseta) {\r
78     if (dseta != null) {\r
79       int[] se = null;\r
80       if (dseta.getSegCount() > 0 && dseta.getPosCount() > 0) {\r
81         throw new Error(\r
82             "Invalid vamsas RangeType - cannot resolve both lists of Pos and Seg from choice!");\r
83       }\r
84       if (dseta.getSegCount() > 0) {\r
85         se = getSegRange(dseta.getSeg(0), true);\r
86         for (int s = 1, sSize = dseta.getSegCount(); s < sSize; s++) {\r
87           int nse[] = getSegRange(dseta.getSeg(s), true);\r
88           if (se[0] > nse[0]) {\r
89             se[0] = nse[0];\r
90           }\r
91           if (se[1] < nse[1]) {\r
92             se[1] = nse[1];\r
93           }\r
94         }\r
95       }\r
96       if (dseta.getPosCount() > 0) {\r
97         // could do a polarity for pos range too. and pass back indication of\r
98         // discontinuities.\r
99         int pos = dseta.getPos(0).getI();\r
100         se = new int[] { pos, pos };\r
101         for (int p = 1, pSize = dseta.getPosCount(); p < pSize; p++) {\r
102           pos = dseta.getPos(p).getI();\r
103           if (se[0] > pos) {\r
104             se[0] = pos;\r
105           }\r
106           if (se[1] < pos) {\r
107             se[1] = pos;\r
108           }\r
109         }\r
110       }\r
111       return se;\r
112     }\r
113     return null;\r
114   }\r
115 \r
116   /**\r
117    * map from a rangeType's internal frame to the referenced object's coordinate\r
118    * frame.\r
119    * \r
120    * @param dseta\r
121    * @return int [] { ref(pos)...} for all pos in rangeType's frame.\r
122    */\r
123   public static int[] getMapping(RangeType dseta) {\r
124     Vector posList = new Vector();\r
125     if (dseta != null) {\r
126       int[] se = null;\r
127       if (dseta.getSegCount() > 0 && dseta.getPosCount() > 0) {\r
128         throw new Error(\r
129             "Invalid vamsas RangeType - cannot resolve both lists of Pos and Seg from choice!");\r
130       }\r
131       if (dseta.getSegCount() > 0) {\r
132         for (int s = 0, sSize = dseta.getSegCount(); s < sSize; s++) {\r
133           se = getSegRange(dseta.getSeg(s), false);\r
134           int se_end = se[1 - se[2]] + (se[2] == 0 ? 1 : -1);\r
135           for (int p = se[se[2]]; p != se_end; p += se[2] == 0 ? 1 : -1) {\r
136             posList.add(new Integer(p));\r
137           }\r
138         }\r
139       } else if (dseta.getPosCount() > 0) {\r
140         int pos = dseta.getPos(0).getI();\r
141 \r
142         for (int p = 0, pSize = dseta.getPosCount(); p < pSize; p++) {\r
143           pos = dseta.getPos(p).getI();\r
144           posList.add(new Integer(pos));\r
145         }\r
146       }\r
147     }\r
148     if (posList != null && posList.size() > 0) {\r
149       int[] range = new int[posList.size()];\r
150       for (int i = 0; i < range.length; i++) {\r
151         range[i] = ((Integer) posList.elementAt(i)).intValue();\r
152       }\r
153       posList.clear();\r
154       return range;\r
155     }\r
156     return null;\r
157   }\r
158 \r
159   public static int[] getIntervals(RangeType range) {\r
160     int[] intervals = null;\r
161     Vector posList = new Vector();\r
162     if (range != null) {\r
163       int[] se = null;\r
164       if (range.getSegCount() > 0 && range.getPosCount() > 0) {\r
165         throw new Error(\r
166             "Invalid vamsas RangeType - cannot resolve both lists of Pos and Seg from choice!");\r
167       }\r
168       if (range.getSegCount() > 0) {\r
169         for (int s = 0, sSize = range.getSegCount(); s < sSize; s++) {\r
170           se = getSegRange(range.getSeg(s), false);\r
171           posList.addElement(new Integer(se[0]));\r
172           posList.addElement(new Integer(se[1]));\r
173         }\r
174       } else if (range.getPosCount() > 0) {\r
175         int pos = range.getPos(0).getI();\r
176         for (int p = 0, pSize = range.getPosCount(); p < pSize; p++) {\r
177           pos = range.getPos(p).getI();\r
178           posList.add(new Integer(pos));\r
179           posList.add(new Integer(pos));\r
180         }\r
181       }\r
182     }\r
183     if (posList != null && posList.size() > 0) {\r
184       intervals = new int[posList.size()];\r
185       java.util.Enumeration e = posList.elements();\r
186       int i = 0;\r
187       while (e.hasMoreElements()) {\r
188         intervals[i++] = ((Integer) e.nextElement()).intValue();\r
189       }\r
190     }\r
191     return intervals;\r
192   }\r
193 \r
194   /**\r
195    * initialise a range type object from a set of start/end inclusive intervals\r
196    * \r
197    * @param mrt\r
198    * @param range\r
199    */\r
200   public static void initRangeType(RangeType mrt, int[] range) {\r
201     for (int i = 0; i < range.length; i += 2) {\r
202       Seg vSeg = new Seg();\r
203       vSeg.setStart(range[i]);\r
204       vSeg.setEnd(range[i + 1]);\r
205       vSeg.setInclusive(true);\r
206       mrt.addSeg(vSeg);\r
207     }\r
208   }\r
209 \r
210   /**\r
211    * \r
212    * @param maprange\r
213    *          where the from range is the local mapped range, and the to range\r
214    *          is the 'mapped' range in the MapRangeType\r
215    * @param default unit for local\r
216    * @param default unit for mapped\r
217    * @return MapList\r
218    */\r
219   public static MapList parsemapType(MapType maprange, int localu, int mappedu) {\r
220     MapList ml = null;\r
221     int[] localRange = getIntervals(maprange.getLocal());\r
222     int[] mappedRange = getIntervals(maprange.getMapped());\r
223     long lu = maprange.getLocal().hasUnit() ? maprange.getLocal().getUnit()\r
224         : localu;\r
225     long mu = maprange.getMapped().hasUnit() ? maprange.getMapped().getUnit()\r
226         : mappedu;\r
227     ml = new MapList(localRange, mappedRange, (int) lu, (int) mu);\r
228     return ml;\r
229   }\r
230 \r
231   /**\r
232    * Parse map with default units of 1,1\r
233    * \r
234    * @param map\r
235    * @return MapList representing map\r
236    */\r
237   public static MapList parsemapType(MapType map) {\r
238     if (!map.getLocal().hasUnit() || !map.getMapped().hasUnit()) {\r
239       if (log.isDebugEnabled()) {\r
240         log.debug("using default mapping length of 1:1 for map "\r
241             + (map.isRegistered() ? map.getVorbaId().toString()\r
242                 : ("<no Id registered> " + map.toString())));\r
243       }\r
244     }\r
245     return parsemapType(map, 1, 1);\r
246   }\r
247 \r
248   /**\r
249    * initialise a MapType object from a MapList object.\r
250    * \r
251    * @param maprange\r
252    * @param ml\r
253    * @param setUnits\r
254    */\r
255   public static void initMapType(MapType maprange, MapList ml, boolean setUnits) {\r
256     initMapType(maprange, ml, setUnits, false);\r
257   }\r
258 \r
259   /**\r
260    * \r
261    * @param maprange\r
262    * @param ml\r
263    * @param setUnits\r
264    * @param reverse\r
265    *          - reverse MapList mapping for Local and Mapped ranges and units\r
266    */\r
267   public static void initMapType(MapType maprange, MapList ml,\r
268       boolean setUnits, boolean reverse) {\r
269     maprange.setLocal(new Local());\r
270     maprange.setMapped(new Mapped());\r
271     if (!reverse) {\r
272       initRangeType(maprange.getLocal(), ml.getFromRanges());\r
273       initRangeType(maprange.getMapped(), ml.getToRanges());\r
274     } else {\r
275       initRangeType(maprange.getLocal(), ml.getToRanges());\r
276       initRangeType(maprange.getMapped(), ml.getFromRanges());\r
277     }\r
278     if (setUnits) {\r
279       if (!reverse) {\r
280         maprange.getLocal().setUnit(ml.getFromRatio());\r
281         maprange.getMapped().setUnit(ml.getToRatio());\r
282       } else {\r
283         maprange.getLocal().setUnit(ml.getToRatio());\r
284         maprange.getMapped().setUnit(ml.getFromRatio());\r
285       }\r
286     }\r
287   }\r
288 \r
289 }\r