package utils.nexus; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Iterator; import org.apache.commons.lang.math.IntRange; import org.apache.log4j.Logger; public class Ranges implements Iterable<Range> { private static final Logger logger = Logger.getLogger(Ranges.class); protected ArrayList<Range> backend; public Ranges() { this.backend = new ArrayList<Range>(); } public Ranges(Ranges template) { this(); for(Range templateRange: template){ backend.add(templateRange.getCopy()); } } public Ranges getCopy() { return new Ranges(this); } public void addRange(int start, int stop) { int min = Math.min(start, stop); int max = Math.max(start, stop); addRange(new Range(min, max, 0)); } public void clearRange(int start, int stop) { int min = Math.min(start, stop); int max = Math.max(start, stop); clearRange(new Range(min, max, 0)); } public void addRange(Range additionalRange) { logger.info("add=" + additionalRange); // first remove completely containing ones ArrayList<Range> toDelete = new ArrayList<Range>(); for(Range range: backend){ if(additionalRange.containsRange(range)){ toDelete.add(range); } } backend.removeAll(toDelete); // if it within merge if same frame, otherwise cut out ArrayList<Range> additionalParts = new ArrayList<Range>(); for(Range range: backend){ if(additionalRange.within(range)){ // if same reading frame then skip additional if(additionalRange.startVal == range.getPosVal(additionalRange.start)){ return; } else{ Range restPart = range.cutOut(additionalRange); additionalParts.add(restPart); } } } backend.addAll(additionalParts); // now adjust the existing ones making new one fit in between // or merge together with overlapping existing if same reading frame ArrayList<Range> toRemove = new ArrayList<Range>(); for(Range range: backend){ if(additionalRange.intersects(range)){ // if same reading frame then merge and remove the overlapping if(additionalRange.startVal == range.getPosVal(additionalRange.start)){ logger.info("merge" + additionalRange); additionalRange.merge(range); toRemove.add(range); } // if not same reading frame then crop else{ range.crop(additionalRange); } } } // remove overlapping and same frame backend.removeAll(toRemove); // and add the new one backend.add(additionalRange); // and remove 0-length ones removeZeroLengthOnes(); // and sort it Collections.sort(backend); this.debug(); } public void clearRange(Range rangeToRemove) { logger.info("remove=" + rangeToRemove); // first remove completely containing ones ArrayList<Range> toDelete = new ArrayList<Range>(); for(Range range: backend){ if(rangeToRemove.containsRange(range)){ toDelete.add(range); } } backend.removeAll(toDelete); // if it within cut out ArrayList<Range> additionalParts = new ArrayList<Range>(); for(Range range: backend){ if(rangeToRemove.within(range)){ Range restPart = range.cutOut(rangeToRemove); additionalParts.add(restPart); } } backend.addAll(additionalParts); // now adjust the existing ones making new one fit in between // or merge together with overlapping existing if same reading frame ArrayList<Range> toRemove = new ArrayList<Range>(); for(Range range: backend){ if(rangeToRemove.intersects(range)){ range.crop(rangeToRemove); } } // and remove 0-length ones removeZeroLengthOnes(); // and sort it Collections.sort(backend); this.debug(); } private void removeZeroLengthOnes() { ArrayList<Range> zeroLengthOnes = new ArrayList<Range>(); for(Range range: backend){ if(range.getLength() < 0){ zeroLengthOnes.add(range); } } backend.removeAll(zeroLengthOnes); } public void debug() { int count = 0; for(Range range: backend){ logger.info("range.toString() count =" + count + " " + range.toString()); count ++; } } public Range getRange(int pos){ for(int n = this.backend.size() - 1; n >= 0; n--){ Range range = backend.get(n); if(range.contains(pos)){ return range; } } return null; } public boolean contains(int pos) { for(Range range: backend){ if(range.contains(pos)){ return true; } } return false; } public boolean intersects(Ranges testRanges) { for(Range aRange: backend){ for(Range testRange: testRanges){ if(testRange.intersects(aRange)){ return true; } } } return false; } public boolean intersects(int minX, int maxX) { Ranges templateRanges = new Ranges(); templateRanges.addRange(minX, maxX); return intersects(templateRanges); } public int size() { return backend.size(); } public Iterator<Range> iterator() { return backend.iterator(); } public void reverse(int length) { if(length <=0){ return; } ArrayList<Range> newBackend = new ArrayList<Range>(); for(Range range: backend){ int newStart = (length - 1) - range.start; int newEnd = (length - 1) - range.end; int newStartVal = range.getPosVal(range.end); CodonRange reverseRange = new CodonRange(newStart, newEnd, newStartVal); newBackend.add(reverseRange); // and sort it Collections.sort(backend); // and set it backend = newBackend; } } public int countPositions(){ int length = 0; for(Range range: backend){ length += range.getLength(); } return length; } public int getMaximumEndPos() { int max = -1; for(Range range: backend){ max = Math.max(max, range.end); } return max; } public int getMinimumStartPos() { if(backend == null || backend.size() == 0){ return -1; } int min = Integer.MAX_VALUE; for(Range range: backend){ min = Math.min(min, range.start); } return min; } public void deletePosition(int pos) { for(int n = backend.size() - 1; n >= 0; n--){ Range range = backend.get(n); if(range.end >= pos){ range.end = range.end - 1; } if(range.start > pos){ // if start is same then leave it range.start = range.start - 1; } } // and remove 0-length ones removeZeroLengthOnes(); } public void insertPosition(int pos) { for(int n = backend.size() - 1; n >= 0; n--){ Range range = backend.get(n); if(range.end >= pos){ range.end = range.end + 1; } if(range.start > pos){ // if start is same then leave it range.start = range.start + 1; } } } public void set(int pos, boolean boolVal){ if(boolVal == true){ addRange(new Range(pos, pos, 0)); }else{ clearRange(new Range(pos, pos, 0)); } } public boolean containsAnyPosition() { if(size() > 0){ return true; } return false; } public ArrayList<NexusRange> getAsContinousNexusRanges(){ ArrayList<NexusRange> nexusRanges = new ArrayList<NexusRange>(); for(Range range: backend){ logger.info("range=" + range); NexusRange continousRange=new NexusRange(range.start + 1, range.end + 1, range.step, range.startVal); // +1 because Nexus uses 1 as first index nexusRanges.add(continousRange); logger.info(continousRange.debug()); } return nexusRanges; } }