package org.genedb.web.gui;
import org.genedb.db.domain.objects.CompoundLocatedFeature;
import org.genedb.db.domain.objects.LocatedFeature;
import org.apache.log4j.Logger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/**
* Represents a CompoundLocatedFeature that has been allocated a track
* or tracks.
*/
public class AllocatedCompoundFeature implements Comparable<AllocatedCompoundFeature> {
private static final Logger logger = Logger.getLogger(AllocatedCompoundFeature.class);
public enum Mode {
UNPACKED, PACKED, STRATIFIED, STRATIFIED_LTR
}
private final CompoundLocatedFeature feature;
private int track;
/**
* Create an AllocatedCompoundFeature with the specified feature and track number.
* Each subfeature is allocated its own track.
*
* @param feature the feature
* @param track the number of the track
*/
public AllocatedCompoundFeature(CompoundLocatedFeature feature, int track) {
this(feature, track, Mode.UNPACKED);
}
/**
* Create an AllocatedCompoundFeature with the specified feature and track number.
*
* @param feature the feature
* @param track the number of the track
* @param packSubfeatures whether to pack subfeatures into as few tracks as possible:
* if <code>false</code>, each subfeature is allocated its own track.
*/
public AllocatedCompoundFeature(CompoundLocatedFeature feature, int track, Mode mode) {
this.feature = feature;
this.track = track;
logger.debug(feature.getUniqueName());
switch (mode) {
case UNPACKED: dontPackSubfeatures(); break;
case PACKED: packSubfeatures(); break;
case STRATIFIED: stratifySubfeatures(); break;
case STRATIFIED_LTR: stratifySubfeaturesByFirst(); break;
}
}
private List<Collection<LocatedFeature>> packedFeatures = new ArrayList<Collection<LocatedFeature>>();
private void addSubfeature(int track, LocatedFeature subfeature) {
for (int i = packedFeatures.size(); i <= track; i++) {
packedFeatures.add(new ArrayList<LocatedFeature>());
}
packedFeatures.get(track).add(subfeature);
logger.trace(String.format("Added '%s' to track %d", subfeature.getUniqueName(), track));
}
private void packSubfeatures() {
DiagramLayout layout = new DiagramLayout();
for (LocatedFeature subfeature: feature.getSubfeaturesOrderedByPosition()) {
int track = layout.addBlock(subfeature.getFmin(), subfeature.getFmax(), 1) - 1;
addSubfeature(track, subfeature);
}
}
private void stratifySubfeatures() {
stratifySubfeatures(feature.getStratifiedSubfeatures());
}
private void stratifySubfeaturesByFirst() {
stratifySubfeatures(feature.getStratifiedSubfeaturesByFirst());
}
private void stratifySubfeatures(Iterable<? extends Iterable<? extends LocatedFeature>> strata) {
int baseTrack = 0;
for (Iterable<? extends LocatedFeature> stratum: strata) {
logger.trace("Start of stratum");
DiagramLayout layout = new DiagramLayout();
for (LocatedFeature subfeature: stratum) {
int track = layout.addBlock(subfeature.getFmin(), subfeature.getFmax(), 1) - 1;
addSubfeature(track + baseTrack, subfeature);
}
baseTrack += layout.numberOfTracks();
}
}
private void dontPackSubfeatures() {
for (LocatedFeature subfeature: feature.getSubfeatures()) {
packedFeatures.add(Collections.singleton(subfeature));
}
}
/**
* Get the sub-tracks of this feature. Each track consists of a
* (non-empty) collection of <code>LocatedFeature</code>s.
*
* @return a list of tracks
*/
public List<Collection<LocatedFeature>> getSubtracks() {
return packedFeatures;
}
public CompoundLocatedFeature getFeature() {
return feature;
}
/**
* Get the track this compound feature starts on.
* @return the track number: positive or negative, non-zero.
*/
public int getTrack() {
return track;
}
/**
* Set the start track for this compound feature.
* @param track
*/
public void setTrack(int track) {
if (track == 0) {
throw new IllegalArgumentException("Track number must be positive or negative, not zero");
}
this.track = track;
}
/**
* Get the subfeatures that sit on the specified track.
*
* @param subfeatureTrack the track number
* @return a collection of the subfeatures on the specified track, possibly empty
*/
Collection<? extends LocatedFeature> forTrack(int subfeatureTrack) {
if (subfeatureTrack == 0)
throw new IllegalArgumentException("Track number must be positive or negative, not zero");
if ((track < 0 && subfeatureTrack > 0) || (track > 0 && subfeatureTrack < 0))
return Collections.emptySet();
int subfeatureIndex;
if (track > 0)
subfeatureIndex = subfeatureTrack - track;
else
subfeatureIndex = track - subfeatureTrack;
if (subfeatureIndex < 0 || subfeatureIndex >= packedFeatures.size())
return Collections.emptySet();
return packedFeatures.get(subfeatureIndex);
}
public int compareTo (AllocatedCompoundFeature other) {
return this.feature.compareTo(other.feature);
}
}