/*
* Changelog:
* - Piero Dalle Pezze: Class creation. Code taken from Location.java and adjusted.
*/
package uk.ac.babraham.BamQC.AnnotationParsers;
import uk.ac.babraham.BamQC.DataTypes.Genome.Feature;
import uk.ac.babraham.BamQC.DataTypes.Genome.Location;
/**
* The Class ProtoFeature. This class represents a feature with temporary location
* parameters which can still be updated.
* @author Piero Dalle Pezze
*/
public class ProtoFeature {
/** The feature. */
protected Feature feature;
protected long value;
protected int startValue, endValue, strandValue;
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;
/**
* Instantiates a new proto feature.
*/
public ProtoFeature (Feature feature, int start, int end, int strand) {
this.feature = feature;
setPosition (start, end, strand);
}
public void update(int start, int end, int strand) {
long newValue = computePosition(start, end, strand);
int newStartValue = (int)(newValue & LAST_31_BIT_MASK);
int newEndValue = (int)((newValue>>31) & LAST_31_BIT_MASK);
// From Location.java:
// 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();
// From SplitLocation.java
// Arrays.sort(this.subLocations);
// setPosition(subLocations[0].start(),subLocations[subLocations.length-1].end(),subLocations[0].strand());
// Equivalent code for two intervals (Don't think this is correct though.. )
if(startValue > newStartValue) {
setPosition(newStartValue, endValue, strand);
} else if(endValue > newEndValue) {
setPosition(newStartValue, endValue, strand);
} else if(strandValue > strand) {
setPosition(newStartValue, endValue, strand);
} else {
setPosition(startValue, newEndValue, strandValue);
}
// new code (TO TEST)
// if(startValue <= newStartValue) {
// // A1 <= B1 ---> A1
// if(endValue < newStartValue) {
// // ---> A2
// // Do nothing
// } else if(endValue < newEndValue) {
// // A2 >= B1
// // ---> B2
// setPosition(startValue, newEndValue, strandValue);
// } else {
// // A2 >= B2
// // ---> A2
// // Do nothing
// }
// } else {
// // A1 > B1 ---> B1
// if(startValue > newEndValue) {
// // --- > B2
// setPosition(newStartValue, newEndValue, strand);
// } else if(endValue > newEndValue) {
// // A1 <= B2
// // ---> A2
// setPosition(newStartValue, endValue, strand);
// } else {
// // A2 <= B2
// setPosition(newStartValue, newEndValue, strand);
// }
// }
}
/**
* Feature.
*
* @return the feature
*/
public Feature getFeature() {
feature.setLocation(new Location(value));
return feature;
}
protected void setPosition (int start, int end, int strand) {
value = computePosition(start, end, strand);
// cache the starting and ending values
setStartEndValues();
}
private long computePosition(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
long thisValue = start;
// We need to remove the top sign bit from the end
// and pack it starting at bit 32
thisValue += (((end) & LAST_31_BIT_MASK) <<31);
switch (strand) {
case FORWARD :
thisValue = thisValue | KNOWN_BIT_MASK; // Sets both forward and known
break;
case REVERSE :
thisValue = thisValue | KNOWN_BIT_MASK; // Sets forward and known
thisValue = thisValue & 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");
}
return thisValue;
}
private void setStartEndValues() {
startValue = (int)(value & LAST_31_BIT_MASK);
endValue = (int)((value>>31) & LAST_31_BIT_MASK);
if ((value & KNOWN_BIT_MASK) == KNOWN_BIT_MASK) {
// KNOWN_BIT_MASK actually sets both known and forward.
strandValue = 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.
strandValue = REVERSE;
}
else {
strandValue = UNKNOWN;
}
}
}