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 tpaterso
029     */
030    public class CoordinateSet extends TreeSet<Coordinate>  implements Serializable {
031    
032        public CoordinateSet() {
033        }
034    
035    
036        public Integer getStart() {
037            Integer out = null;
038    
039            if ( !this.isEmpty()) {
040                out = this.first().getStart();
041            }
042    
043            return out;
044        }
045    
046        public Integer getEnd() {
047            Integer out = null;
048    
049            if ( !this.isEmpty()) {
050    
051                out = this.last().getEnd();
052    
053                for (Coordinate c : this) {
054                 if (c.getEnd()>out) {
055                     out = c.getEnd();
056                 }
057                }
058            }
059             return out;
060        }
061    
062        public Coordinate getRange() {
063             Coordinate out = null;
064             if (  !this.isEmpty()) {
065    
066                 out = new Coordinate(this.getStart(),this.getEnd() ,1);
067    
068             }
069             return out;
070         }
071    
072        public CoordinateSet getGaps(){
073            CoordinateSet gaps = new CoordinateSet();
074    
075    
076            Integer rangeEnd = this.getEnd();
077            Integer currentPosition = this.getStart();
078    
079            for (Coordinate c : this) {
080    
081                Integer s = c.getStart();
082                Integer e = c.getEnd();
083    
084                //if we're already past the extent of this region
085                if (currentPosition>=e) {
086                    continue;
087                }
088    
089                if (s>currentPosition) {
090    
091                    Coordinate newC = new Coordinate(currentPosition, s-1, 1);
092                    gaps.add(newC);
093                }
094    
095                if (e>=rangeEnd) {
096                    break;
097                }
098    
099    
100                currentPosition = e+1;
101    
102            }
103    
104            return gaps;
105        }
106    
107        public Boolean containsCoordinateWithoutGaps(Coordinate test) {
108            Boolean out = false;
109    
110            Integer testBegin = test.getStart();
111            Integer testEnd = test.getEnd();
112    
113            if (testBegin<this.getStart() || testEnd>this.getEnd()) {
114                out = false;
115            } else if (this.getGaps().isEmpty()) {
116                out = true;
117            } else {
118    
119                out = true;
120                for(Coordinate c: this.getGaps()) {
121                    if(test.overlaps(c)) {
122                        out = false;
123                        break;
124                    }
125                }
126    
127            }
128            return out;
129        }
130    
131        public CoordinateSet getUncoveredRegions(Coordinate test) {
132    
133            CoordinateSet gaps = new CoordinateSet();
134    
135            if (this.isEmpty()) {
136                gaps.add(test);
137                return gaps;
138            }
139    
140            if (this.containsCoordinateWithoutGaps(test)) {
141                return gaps;
142            }
143    
144            if (!this.getRange().overlaps(test)) {
145                gaps.add(test);
146                return gaps;
147            }
148    
149            Integer testBegin = test.getStart();
150            Integer testEnd = test.getEnd();
151            Integer thisBegin = this.getStart();
152            Integer thisEnd = this.getEnd();
153    
154            CoordinateSet thisGaps = this.getGaps();
155    
156            //overlap at start
157            if (testBegin<thisBegin) {
158                Coordinate newC = new Coordinate(testBegin, thisBegin-1, 1);
159                gaps.add(newC);
160            }
161    
162            //overlap at end
163            if (testEnd>thisEnd) {
164                Coordinate newC2 = new Coordinate(thisEnd+1, testEnd,  1);
165                gaps.add(newC2);
166            }
167    
168            for (Coordinate cd : thisGaps) {
169    
170                //this gap completely contains the test Coordinate
171                if (test.liesWithinCoordinate(cd)) {
172                    gaps.add(test);
173                    break;
174                }
175    
176                // this gap lies completely within the range
177                else if (cd.liesWithinCoordinate(test)) {
178                    gaps.add(cd);
179                }
180    
181                else if (cd.overlaps(test)) {
182    
183                    gaps.add(cd.getOverlap(test));
184                }
185    
186            }
187    
188            return gaps;
189        }
190    
191    }