1 /* MarkerRange.java
2  *
3  * created: Tue Dec 29 1998
4  *
5  * This file is part of Artemis
6  *
7  * Copyright (C) 1998,1999,2000  Genome Research Limited
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * as published by the Free Software Foundation; either version 2
12  * of the License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22  *
23  * $Header: //tmp/pathsoft/artemis/uk/ac/sanger/artemis/sequence/MarkerRange.java,v 1.1 2004-06-09 09:52:20 tjc Exp $
24  */
25 
26 package uk.ac.sanger.artemis.sequence;
27 
28 import uk.ac.sanger.artemis.util.*;
29 
30 import uk.ac.sanger.artemis.io.Range;
31 
32 /**
33  *  This class implements a range that has end points specified by a pair of
34  *  positions in a Strand object.
35  *
36  *  @author Kim Rutherford
37  *  @version $Id: MarkerRange.java,v 1.1 2004-06-09 09:52:20 tjc Exp $
38  **/
39 
40 public class MarkerRange {
41   /**
42    *  Create a new MarkerRange object.  If the start position is greater than
43    *  the end position the two positions will be silently swapped.
44    *  @param strand The Strand that contains this range.
45    *  @param start The start position.
46    *  @param end The end position.
47    **/
MarkerRange(final Strand strand, final int start, final int end)48   public MarkerRange (final Strand strand, final int start, final int end)
49       throws OutOfRangeException {
50     if (start <= end) {
51       this.start = strand.makeMarker (start);
52       this.end = strand.makeMarker (end);
53     } else {
54       this.start = strand.makeMarker (end);
55       this.end = strand.makeMarker (start);
56     }
57   }
58 
59   /**
60    *  Create a new MarkerRange object that covers one base (given by a Marker
61    *  object).
62    *  @param marker The position of the new MarkerRange.
63    **/
MarkerRange(final Marker marker)64   public MarkerRange (final Marker marker) {
65     this.start = marker;
66     this.end = marker;
67   }
68 
69   /**
70    *  Return the Strand object that was passed to the constructor.
71    **/
getStrand()72   public Strand getStrand () {
73     return getStart ().getStrand ();
74   }
75 
76   /**
77    *  Return true if and only if this MarkerRange is a MarkerRange on the
78    *  forward strand.
79    **/
isForwardMarker()80   public boolean isForwardMarker () {
81     return getStrand ().isForwardStrand ();
82   }
83 
84   /**
85    *  Return the start Marker of this range.
86    **/
getStart()87   public Marker getStart () {
88     return start;
89   }
90 
91   /**
92    *  Return the end Marker of this range.
93    **/
getEnd()94   public Marker getEnd () {
95     return end;
96   }
97 
98   /**
99    *  Return the number of bases in the range, inclusive of the end points.
100    **/
getCount()101   public int getCount () {
102     return
103       getRawEnd ().getRawPosition () - getRawStart ().getRawPosition ()  + 1;
104   }
105 
106   /**
107    *  Return the end marker if it marks a lower position on the underlying
108    *  Bases of the Strand than the start Marker, otherwise return the start
109    *  Marker.
110    **/
getRawStart()111   public Marker getRawStart () {
112     if (getStart ().getRawPosition () <= getEnd ().getRawPosition ()) {
113       return getStart ();
114     } else {
115       return getEnd ();
116     }
117   }
118 
119   /**
120    *  Return the start marker if it marks a lower position on the underlying
121    *  Bases of the Strand than the end Marker, otherwise return the end
122    *  Marker.
123    **/
getRawEnd()124   public Marker getRawEnd () {
125     if (getEnd ().getRawPosition () <= getStart ().getRawPosition ()) {
126       return getStart ();
127     } else {
128       return getEnd ();
129     }
130   }
131 
132   /**
133    *  Return an embl.Range object that corresponds a range of bases on the
134    *  underlying Bases object of this MarkerRange.
135    **/
getRawRange()136   public Range getRawRange () {
137     try {
138       if (getStrand ().isForwardStrand ()) {
139         return new Range (getStart ().getRawPosition (),
140                           getEnd ().getRawPosition ());
141       } else {
142         return new Range (getEnd ().getRawPosition (),
143                           getStart ().getRawPosition ());
144       }
145     } catch (OutOfRangeException e) {
146       throw new Error ("internal error - unexpected exception: " + e);
147     }
148   }
149 
150   /**
151    *  Return an embl.Range object for this MarkerRange.  The start and end
152    *  positions of the Range will be the same as the start and end that was
153    *  passed to the constructor.
154    **/
getRange()155   public Range getRange () {
156     try {
157       return new Range (getStart ().getPosition (), getEnd ().getPosition ());
158     } catch (OutOfRangeException e) {
159       throw new Error ("internal error - unexpected exception: " + e);
160     }
161   }
162 
163   /**
164    *  Return true if and only if this MarkerRange and the given MarkerRange
165    *  overlap.
166    **/
overlaps(final MarkerRange arg_range)167   public boolean overlaps (final MarkerRange arg_range) {
168     if (getStart ().getPosition () < arg_range.getStart ().getPosition () &&
169         getEnd ().getPosition () < arg_range.getStart ().getPosition ()) {
170       return false;
171     }
172     if (getStart ().getPosition () > arg_range.getEnd ().getPosition () &&
173         getEnd ().getPosition () > arg_range.getEnd ().getPosition ()) {
174       return false;
175     }
176     return true;
177   }
178 
179   /**
180    *  Return a new MarkerRange object that is the same as this object but has
181    *  been extended to contain the given range.
182    *  @param arg_range The returned MarkerRange will cover this range too.
183    **/
extendRange(final MarkerRange arg_range)184   public MarkerRange extendRange (final MarkerRange arg_range) {
185     return combineRanges (arg_range, true);
186   }
187 
188   /**
189    *  Return a new MarkerRange object that is the same as this object but is
190    *  guaranteed to contain the given range.
191    *  @param arg_range The returned MarkerRange will cover this range too.
192    *  @param truncate_if_contained If the arg_range is completely contained in
193    *    this range then the returned range will be truncated if and only if
194    *    this is true.  If true then one extreme of the returned range will be
195    *    moved so that it coincides with the closest extreme of the arg_range.
196    *    The returned range will be only as small as necessary to achieve this.
197    *    eg. if this is 1..100, the arg is 20..30 and truncate_if_contained is
198    *    true then the returned range will be 20..100
199    **/
combineRanges(final MarkerRange arg_range, final boolean truncate_if_contained)200   public MarkerRange combineRanges (final MarkerRange arg_range,
201                                     final boolean truncate_if_contained) {
202     if (getStrand () != arg_range.getStrand ()) {
203       throw new Error ("internal error - strands do not match");
204     }
205 
206     final MarkerRange return_range;
207 
208     try {
209        return_range =
210         new MarkerRange (getStrand (),
211                          getStart ().getPosition (),
212                          getEnd ().getPosition ());
213     } catch (OutOfRangeException e) {
214       throw new Error ("internal error - unexpected exception: " + e);
215     }
216 
217     if (return_range.contains (arg_range)) {
218       if (truncate_if_contained) {
219         // truncate the return_range
220         if ((arg_range.getStart ().getPosition () -
221              return_range.getStart ().getPosition ()) >
222             (return_range.getEnd ().getPosition () -
223              arg_range.getEnd ().getPosition ())) {
224           return_range.end = arg_range.getEnd ();
225         } else {
226           return_range.start = arg_range.getStart ();
227         }
228       } else {
229         // do nothing
230       }
231     } else {
232       if (return_range.getStart ().getPosition () >
233           arg_range.getStart ().getPosition ()) {
234         return_range.start = arg_range.getStart ();
235       }
236 
237       if (return_range.getEnd ().getPosition () <
238           arg_range.getEnd ().getPosition ()) {
239         return_range.end = arg_range.getEnd ();
240       }
241     }
242 
243     return return_range;
244   }
245 
246   /**
247    *  Return true if and only if the given MarkerRange is contained within
248    *  this range.
249    **/
contains(final MarkerRange arg_range)250   public boolean contains (final MarkerRange arg_range) {
251     if (getStart ().getPosition () <= arg_range.getStart ().getPosition () &&
252         getEnd ().getPosition () >= arg_range.getEnd ().getPosition ()) {
253       return true;
254     } else {
255       return false;
256     }
257   }
258 
259   /**
260    *  Create and return a Location object from the embl package that describes
261    *  this MarkerRange object.  For example if the sequence is 1000 bases long
262    *  and the MarkerRange object covers the reverse strand bases 100..200 then
263    *  the Location object will be "complement(800..900)".
264    **/
createLocation()265   public uk.ac.sanger.artemis.io.Location createLocation () {
266     try {
267       final uk.ac.sanger.artemis.io.Location new_location =
268         new uk.ac.sanger.artemis.io.Location (getRawRange ());
269 
270       if (getStrand ().isForwardStrand ()) {
271         return new_location;
272       } else {
273         return new_location.getComplement ();
274       }
275     } catch (OutOfRangeException e) {
276       throw new Error ("internal error - unexpected exception: " + e);
277     }
278   }
279 
280   /**
281    *  The start Marker created from the Strand object and the start position
282    *  passed to the constructor.
283    **/
284   private Marker start = null;
285 
286   /**
287    *  The end Marker created from the Strand object and the end position
288    *  passed to the constructor.
289    **/
290   private Marker end = null;
291 }
292 
293 
294