/** * Copyright Copyright 2007-14 Simon Andrews * * This file is part of BamQC. * * BamQC is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * BamQC is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with BamQC; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* * Changelog: * - Piero Dalle Pezze: Merging with SeqMonk: Location.java * - Simon Andrews: Class creation. */ package uk.ac.babraham.BamQC.DataTypes.Genome; import java.io.Serializable; import org.apache.log4j.Logger; /** * A simple class to represent a simple location in the genome. * For complex positions containing sublocations you should use * the SplitLocation class instead. * @author Simon Andrews * @author Piero Dalle Pezze */ public class Location implements Serializable, Comparable<Location> { private static Logger log = Logger.getLogger(Location.class); private static final long serialVersionUID = 4931115654048485228L; private long value; // cache the values including the LAST_31_BIT_MASK for start() and end() methods private int startValue, endValue; public static final int FORWARD = 1; public static final int REVERSE = -1; public static final int UNKNOWN = 0; private static final long LAST_31_BIT_MASK = Long.parseLong("0000000000000000000000000000000001111111111111111111111111111111",2); // Using the 64th bit is a pain. We can't use -0 to construct a mask since // it gets converted to +0 and loses the 64th bit. We therefore have to leave // the 63rd bit set as well and work around this later. private static final long KNOWN_BIT_MASK = Long.parseLong("-100000000000000000000000000000000000000000000000000000000000000",2); private static final long REVERSE_TEST_MASK = Long.parseLong("0100000000000000000000000000000000000000000000000000000000000000",2); private static final long REVERSE_BIT_MASK = ~REVERSE_TEST_MASK; /** * Constructs a new Location from a previously packed position. * This constructor should not be used normally. It's only * valid use is for reconstructing serialised positions which * have been written to a file. * * @param packedPosition */ public Location (long packedPosition) { this.value = packedPosition; // cache the starting and ending values setStartEndValues(); } public Location (int start, int end, int strand) { setPosition(start, end, strand); if (strand != strand()) { log.error("Strand "+strand+" didn't match "+strand()); } } /** * A constant defining the stand of this location. One of: * Location.FORWARD * Location.REVERSE * Location.UNKNOWN * * @return The strand */ public int strand () { if ((value & KNOWN_BIT_MASK) == KNOWN_BIT_MASK) { // KNOWN_BIT_MASK actually sets both known and forward. return FORWARD; } else if (value < 0) { // We can't test for the first bit with a bitmask since java // doesn't distinguish -0 and +0 so we just look for a negative // value to determine a positive position in bit 1. return REVERSE; } else { return UNKNOWN; } } /** * Retrieves the interal representation of this position. Not for * general use - only to be used within the BamQCDataWriter * * @return The internal packed representation of this position */ public long packedPosition () { return value; } /** * The start position of this location. Guaranteed to not be higher * than the end position * * @return The start position */ public int start () { return startValue; } /** * The end position of this location. Guaranteed to be the same * or higher than the start position. * * @return The end position */ public int end () { return endValue; } public int middle () { return start()+((end()-start())/2); } @Override public String toString () { return start()+"-"+end(); } @Override public int compareTo(Location o) { if (start() != o.start()) return start() - o.start(); else if (end() != o.end()) return end()- o.end(); else if (strand() != o.strand()) return strand() - o.strand(); else return hashCode() - o.hashCode(); } /** * Length. * * @return the length of this location */ public int length() { return 1+ (end() - start()); } /** * Provides an EMBL formatted version of the location * * @return An EMBL format location string */ public String locationString () { if (strand() == REVERSE) { return "complement("+start()+".."+end()+")"; } else if (strand() == UNKNOWN) { return "unknown("+start()+".."+end()+")"; } else { return start()+".."+end(); } } private void setStartEndValues() { startValue = (int)(value & LAST_31_BIT_MASK); endValue = (int)((value>>31) & LAST_31_BIT_MASK); } protected void setPosition (int start, int end, int strand) { if (start < 0 || end < 0) throw new IllegalArgumentException("Negative positions are not allowed"); if (end < start) { int temp = start; start = end; end = temp; } // Base is start value = start; // We need to remove the top sign bit from the end // and pack it starting at bit 32 value += (((end) & LAST_31_BIT_MASK) <<31); switch (strand) { case FORWARD : value = value | KNOWN_BIT_MASK; // Sets both forward and known break; case REVERSE : value = value | KNOWN_BIT_MASK; // Sets forward and known value = value & REVERSE_BIT_MASK; // Unsets forward break; case UNKNOWN : break; // Leaves known and forward as zero default : throw new IllegalArgumentException("Strand was not FORWARD, REVERSE or UNKNOWN"); } // cache the starting and ending values setStartEndValues(); } }