net.maizegenetics.dna.map.PositionListBuilder Maven / Gradle / Ivy
package net.maizegenetics.dna.map;
import cern.colt.GenericSorting;
import cern.colt.Swapper;
import cern.colt.function.IntComparator;
import com.google.common.base.Preconditions;
import net.maizegenetics.dna.snp.genotypecall.GenotypeCallTableBuilder;
import net.maizegenetics.util.Tuple;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/**
* A builder for creating immutable PositionList.
*
*
* Example:
*
{@code
* PositionListBuilder b=new PositionArrayList.Builder();
* for (int i = 0; i
* If being built separately from the genotypes, then use validate ordering to make sure sites are added in the
* intended order. This list WILL be sorted.
* Builder instances can be reused - it is safe to call {@link #build()}
* multiple times to build multiple lists in series. Each new list
* contains the one created before it.
*
*
Builder instances can be reused - it is safe to call {@link #build()}
*/
public class PositionListBuilder {
private static final Logger myLogger = LogManager.getLogger(PositionListBuilder.class);
private ArrayList myPositions = new ArrayList<>();
private String genomeVersion = null;
/**
* Creates a new builder. The returned builder is equivalent to the builder
* generated by {@link }.
*/
public PositionListBuilder() {
}
/**
* Creates a new builder with a given number of Positions. This is most
* useful when the number of sites is known from the beginning and the set
* method will be used to set positions perhaps out of order. Useful in
* multithreaded builders.
*/
public PositionListBuilder(int numberOfPositions) {
for (int i = 0; i < numberOfPositions; i++) {
myPositions.add(new GeneralPosition.Builder(Chromosome.UNKNOWN, i).build());
}
}
public boolean contains(Position pos) {
return myPositions.contains(pos);
}
/**
* Adds {@code element} to the {@code PositionList}.
*
* @param element the element to add
*
* @return this {@code Builder} object
* @throws NullPointerException if {@code element} is null
*/
public PositionListBuilder add(Position element) {
Preconditions.checkNotNull(element, "element cannot be null");
myPositions.add(element);
return this;
}
/**
* Adds each element of {@code elements} to the {@code PositionList}.
*
* @param collection collection containing positions to be added to this
* list
*
* @return this {@code Builder} object
* @throws NullPointerException if {@code elements} is or contains null
*/
public PositionListBuilder addAll(Collection extends Position> collection) {
myPositions.ensureCapacity(myPositions.size() + collection.size());
for (Position elem : collection) {
Preconditions.checkNotNull(elem, "elements contains a null");
myPositions.add(elem);
}
return this;
}
public PositionListBuilder addAll(PositionListBuilder builder) {
myPositions.ensureCapacity(myPositions.size() + builder.size());
for (Position elem : builder.myPositions) {
Preconditions.checkNotNull(elem, "elements contains a null");
myPositions.add(elem);
}
return this;
}
/**
* Replaces the element at the specified position in this list with the
* specified element.
*
* @param index index of the element to replace
* @param element element to be stored at the specified position
*
* @return this {@code Builder} object
*/
public PositionListBuilder set(int index, Position element) {
myPositions.set(index, element);
return this;
}
public PositionListBuilder genomeVersion(String genomeVersion) {
this.genomeVersion = genomeVersion;
return this;
}
/**
* Returns whether List is already ordered. Important to check this if
* genotype and sites are separately built, as the PositionArrayList must be
* sorted, and will be with build.
*/
public boolean validateOrdering() {
boolean result = true;
Position startAP = myPositions.get(0);
for (Position ap : myPositions) {
if (ap.compareTo(startAP) < 0) {
myLogger.warn("validateOrdering: " + ap.toString() + " and " + startAP.toString() + " out of order.");
return false;
}
startAP = ap;
}
return result;
}
/**
* Returns the size (number of positions) in the current list
*
* @return current size
*/
public int size() {
return myPositions.size();
}
/**
* Generates a generic position list when no position information known
*
* @param numSites number of sites
*
* @return generic position list
*/
public static PositionList getInstance(int numSites) {
PositionListBuilder builder = new PositionListBuilder();
for (int i = 0; i < numSites; i++) {
builder.add(new GeneralPosition.Builder(Chromosome.UNKNOWN, i).build());
}
return builder.build();
}
/**
* Creates in memory of PositionList from the an array of positions.
*/
public static PositionList getInstance(List positions) {
PositionListBuilder builder = new PositionListBuilder();
builder.addAll(positions);
return builder.build();
}
/**
* Returns a newly-created {@code ImmutableList} based on the myPositions of
* the {@code Builder}.
*/
public PositionList build() {
Collections.sort(myPositions);
return new PositionArrayList(myPositions, genomeVersion);
}
public Tuple buildWithSiteRedirect() {
int[] siteRedirect = sort();
PositionList positions = new PositionArrayList(myPositions, genomeVersion);
return new Tuple<>(positions, siteRedirect);
}
public PositionList build(GenotypeCallTableBuilder genotypes) {
sortPositions(genotypes);
return new PositionArrayList(myPositions, genomeVersion);
}
public PositionListBuilder sortPositions(GenotypeCallTableBuilder genotypes) {
int numPositions = myPositions.size();
if (numPositions != genotypes.getSiteCount()) {
throw new IllegalArgumentException("PositionListBuilder: sortPositions: position list size: " + numPositions + " doesn't match genotypes num position: " + genotypes.getSiteCount());
}
genotypes.reorderPositions(sort());
return this;
}
public PositionListBuilder sortPositions() {
sort();
return this;
}
public int[] sort() {
int numPositions = myPositions.size();
final int indicesOfSortByPosition[] = new int[numPositions];
for (int i = 0; i < indicesOfSortByPosition.length; i++) {
indicesOfSortByPosition[i] = i;
}
Swapper swapPosition = new Swapper() {
@Override
public void swap(int a, int b) {
int temp = indicesOfSortByPosition[a];
indicesOfSortByPosition[a] = indicesOfSortByPosition[b];
indicesOfSortByPosition[b] = temp;
}
};
IntComparator compPosition = new IntComparator() {
@Override
public int compare(int a, int b) {
return myPositions.get(indicesOfSortByPosition[a]).compareTo(myPositions.get(indicesOfSortByPosition[b]));
}
};
GenericSorting.quickSort(0, indicesOfSortByPosition.length, compPosition, swapPosition);
ArrayList temp = new ArrayList<>(numPositions);
for (int t = 0; t < numPositions; t++) {
temp.add(myPositions.get(indicesOfSortByPosition[t]));
}
myPositions = temp;
return indicesOfSortByPosition;
}
}