001    /**
002     * Copyright (C) 2010 The Roslin Institute <contact andy.law@roslin.ed.ac.uk>
003     *
004     * This file is part of the Ensembl Java API demonstration project developed by the
005     * Bioinformatics Group at The Roslin Institute, The Royal (Dick) School of
006     * Veterinary Studies, University of Edinburgh.
007     *
008     * This is free software: you can redistribute it and/or modify
009     * it under the terms of the GNU General Public License (version 3) as published by
010     * the Free Software Foundation.
011     *
012     * This software is distributed in the hope that it will be useful,
013     * but WITHOUT ANY WARRANTY; without even the implied warranty of
014     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
015     * GNU General Public License for more details.
016     *
017     * You should have received a copy of the GNU General Public License
018     * in this software distribution. If not, see <http://www.gnu.org/licenses/gpl-3.0.html/>.
019     */
020    
021    package uk.ac.roslin.ensembl.model;
022    
023    import java.io.Serializable;
024    import java.util.TreeSet;
025    
026    /**
027     *
028     * @author paterson
029     */
030    public class Coordinate implements Comparable<Coordinate>, Serializable {
031    
032        Integer start = null;
033        Integer end = null;
034        Strand strand = null;
035        private Integer strandInt = null;
036    
037    
038        public Coordinate() {
039    
040        }
041    
042        public Coordinate(Integer start, Integer end) {
043    
044            this(start, end, Strand.strand(0));
045        }
046    
047        public Coordinate(Integer start, Integer end, Integer strand) {
048    
049            this(start, end, Strand.strand(strand));
050        }
051    
052        public Coordinate(Integer start, Integer end, Coordinate.Strand strand) {
053    
054            if (start!= null && end != null) {
055                if (start<=end) {
056                    this.setStart(start);
057                    this.setEnd(end);
058                } else {
059                    this.setStart(end);
060                    this.setEnd(start);
061                }
062            }
063            this.setStrand(strand); 
064        }
065    
066        @Override
067        public String toString() {
068            String out = "";
069            out += this.start+" - "+this.end+" ("
070                    + (( this.strand != null) ? this.strand.toString() : "UNSPECIFIED_STRAND") +")";
071            return out;
072        }
073    
074        public static enum Strand implements Serializable  {
075    
076            FORWARD_STRAND,
077            REVERSE_STRAND;
078    
079            public static Strand strand(int i) {
080                if (i == 1) {
081                    return FORWARD_STRAND;
082                } else if (i == -1) {
083                    return REVERSE_STRAND;
084                } else {
085                    return null;
086                }
087            }
088            public static Strand strand(Integer i) {
089                if (i == 1) {
090                    return FORWARD_STRAND;
091                } else if (i == -1) {
092                    return REVERSE_STRAND;
093                } else {
094                    return null;
095                }
096            }
097        }
098    
099    
100        public Integer getStart() {
101            return start;
102        }
103    
104        public Integer getEnd() {
105            return end;
106        }
107    
108        public Strand getStrand() {
109            return strand;
110        }
111    
112        public void setStart(Integer b) {
113            start = b;
114        }
115    
116        public void setEnd(Integer e) {
117                end = e;
118            }
119    
120        public void setStrand(Strand s) {
121                strand = s;
122        }
123    
124        public void setStrandInt(Integer s) {
125                strandInt = s;
126                strand = Coordinate.Strand.strand(s);
127        }
128    
129        public Integer getStrandInt() {
130            return strandInt;
131        }
132    
133    //    public Integer getPlusStrandStart() {
134    //        if (strand == Strand.FORWARD_STRAND) {
135    //            return start;
136    //        } else  if (strand == Strand.REVERSE_STRAND) {
137    //            return end;
138    //        } else return null;
139    //    }
140    //
141    //    public Integer getPlusStrandEnd() {
142    //        if (strand == Strand.FORWARD_STRAND) {
143    //            return end;
144    //        } else  if (strand == Strand.REVERSE_STRAND) {
145    //            return start;
146    //        } else return null;
147    //    }
148    
149        //this can be used for ordering by the plus strand start, or the plus strand
150        //end if the starts are identical
151    
152        //nb careful with Integer comparisons
153    
154        //nb 0 != identity , because it doesnt consider the strand, just the order
155    
156        public int compareTo(Coordinate o) {
157    
158            //dont compare if any nulls
159            if (this.start==null || this.end==null
160                    || o.getStart()==null ||o.getEnd()==null) {
161                return 0;
162            }
163    
164    
165            if (!this.start.equals(o.getStart())) {
166    
167                if (this.start.compareTo(o.getStart()) < 0) {
168                    return -1;
169                } else {
170                    return 1;
171                } 
172    
173            } else {
174                //put the longest one first
175                //this has consequences for getting the end coordinate of a set
176                if (this.end.compareTo(o.getEnd()) < 0) {
177                    return 1;
178                } else  if (this.end.compareTo(o.getEnd()) > 0) {
179                    return -1;
180                } else {
181                    //the coordinates are identical
182                    return 0;
183                }
184            }
185    }
186    
187        public  Boolean liesWithinCoordinate(Coordinate test) {
188            Boolean out = false;
189    
190            if (test.getStart()==null || test.getEnd()==null
191                    || this.start == null || this.end==null) {
192                return out;
193            }
194    
195            if (this.start>=test.getStart() && this.end<=test.getEnd()) {
196                out = true;
197            }
198    
199            return out;
200        }
201    
202        public Boolean overlaps(Coordinate test) {
203    
204            boolean out = false;
205    
206            if (this.start==null || this.end==null || test.getEnd()==null || test.getStart()==null) {
207                out = false;
208    //        } else if ( (this.start>=test.getStart() && this.start<=test.getEnd())
209    //                || (this.end>=test.getStart() && this.end<=test.getEnd() )) {
210            } else if (
211                    (this.liesWithinCoordinate(test)) || (test.liesWithinCoordinate(this))
212                    || (this.start <= test.getStart() && this.end >= test.getStart())
213                    || (this.end >= test.getEnd() && this.start <= test.getEnd() )) {
214                out = true;
215            }
216    
217            return out;
218        }
219    
220        public Coordinate getOverlap(Coordinate test) {
221    
222            Coordinate out = null;
223            Integer start = test.getStart();
224            Integer end = test.getEnd();
225    
226    
227            if (this.start==null || this.end==null || test.getEnd()==null || test.getStart()==null
228                    || !this.overlaps(test)) {
229                out = null;
230            } else {
231                    if (this.start>=test.getStart() && this.start<=test.getEnd() ) {
232                        start = this.start;
233                    }
234                    if (this.end>=test.getStart() && this.end<=test.getEnd() )
235                    {
236                        end = this.end;
237                    }
238                    out = new Coordinate(start, end, 1);
239            }
240    
241            return out;
242        }
243    
244    
245    
246    //    public static Integer getStart(TreeSet<Coordinate> testSet) {
247    //        Integer out = null;
248    //
249    //        if ( testSet != null && !testSet.isEmpty()) {
250    //            out = testSet.first().getStart();
251    //        }
252    //
253    //        return out;
254    //    }
255    //
256    //    public static Integer getEnd(TreeSet<Coordinate> testSet) {
257    //        Integer out = null;
258    //
259    //        if ( testSet != null && !testSet.isEmpty()) {
260    //
261    //            out = testSet.last().getEnd();
262    //
263    //            for (Coordinate c : testSet) {
264    //             if (c.getEnd()>out) {
265    //                 out = c.getEnd();
266    //             }
267    //            }
268    //        }
269    //         return out;
270    //    }
271    //
272    //    public static Coordinate getRange(TreeSet<Coordinate> testSet) {
273    //         Coordinate out = null;
274    //         if ( testSet != null && !testSet.isEmpty()) {
275    //
276    //             out = new Coordinate(Coordinate.getStart(testSet),Coordinate.getEnd(testSet) ,1);
277    //
278    //         }
279    //
280    //         return null;
281    //     }
282    
283    
284    
285    //
286    //    /**
287    //     * tests whether this Coordinate represents a region fully covered in the
288    //     * parameter Set of Coordinates - without any gaps
289    //     * @param testSet TreeSet<Coordinate>
290    //     * @return Boolean
291    //     */
292    //    public  Boolean liesWithinCoordinateSetWithoutGaps(TreeSet<Coordinate> testSet) {
293    //        Boolean out = false;
294    //
295    //        Integer rangeBegin = Coordinate.getStart(testSet);
296    //        Integer rangeEnd = Coordinate.getEnd(testSet);
297    //
298    //        if (this.start<rangeBegin || this.end>rangeEnd ) {
299    //            out = false;
300    //        } else if (this.getGaps(testSet).isEmpty()) {
301    //            out = true;
302    //        }
303    //
304    //        return out;
305    //    }
306    
307    //    /**
308    //     *
309    //     * @param testSet TreeSet<Coordinate>
310    //     * @return TreeSet<Coordinate>
311    //     */
312    //    public  TreeSet<Coordinate> getGaps(TreeSet<Coordinate> testSet) {
313    //
314    //        Integer rangeBegin = Coordinate.getStart(testSet);
315    //        Integer rangeEnd = Coordinate.getEnd(testSet);
316    //
317    //        TreeSet<Coordinate> gaps = new TreeSet<Coordinate>();
318    //        TreeSet<Coordinate> temp = Coordinate.getCoordinateGaps(testSet);
319    //
320    //
321    //        if (this.start<rangeBegin) {
322    //            Coordinate newC = new Coordinate(this.getStart(), rangeBegin-1, 1);
323    //            gaps.add(newC);
324    //        }
325    //
326    //        if (this.end>rangeEnd) {
327    //            Coordinate newC2 = new Coordinate(rangeEnd+1, this.end, 1);
328    //            gaps.add(newC2);
329    //        }
330    //
331    //
332    //
333    //        for (Coordinate cd : temp) {
334    //
335    //            //this gap completely contains the desired range
336    //            if (this.liesWithinCoordinate(cd)) {
337    //                Coordinate newC = new Coordinate(this.getStart(), this.getEnd(), 1);
338    //                gaps.add(newC);
339    //                break;
340    //            }
341    //
342    //            // this gap lies completely within the range
343    //            else if (cd.liesWithinCoordinate(this)) {
344    //                gaps.add(cd);
345    //            }
346    //
347    //            //this gap overlaps the beginning of the range
348    //            else if (cd.getStart()<=this.getStart() && cd.getEnd()>=this.getStart()) {
349    //                Coordinate newC = new Coordinate(this.getStart(), cd.getEnd(), 1);
350    //                gaps.add(newC);
351    //            }
352    //
353    //            //this gap overlies the end of the range
354    //            else if (cd.getEnd()>=this.getEnd() && cd.getStart()<=this.getEnd()) {
355    //                Coordinate newC = new Coordinate(cd.getStart(), this.getEnd(), 1);
356    //                gaps.add(newC);
357    //            }
358    //
359    //        }
360    //
361    //        return gaps;
362    //    }
363    
364    //    public static TreeSet<Coordinate> getCoordinateGaps(TreeSet<Coordinate> testSet){
365    //        TreeSet<Coordinate> gaps = new TreeSet<Coordinate>();
366    //
367    //        Integer rangeBegin = Coordinate.getStart(testSet);
368    //        Integer rangeEnd = Coordinate.getEnd(testSet);
369    //        Integer currentPosition = Coordinate.getStart(testSet);
370    //
371    //        for (Coordinate c : testSet) {
372    //
373    //            Integer s = c.getStart();
374    //            Integer e = c.getEnd();
375    //
376    //            if (s>currentPosition) {
377    //
378    //                Coordinate newC = new Coordinate(currentPosition, s-1, 1);
379    //                gaps.add(newC);
380    //            }
381    //
382    //            if (e>=rangeEnd) {
383    //                break;
384    //            }
385    //
386    //
387    //            currentPosition = e+1;
388    //
389    //        }
390    //
391    //        return gaps;
392    //    }
393    
394    }