uk.ac.tgac.conan.process.asm.KmerRange.java Source code

Java tutorial

Introduction

Here is the source code for uk.ac.tgac.conan.process.asm.KmerRange.java

Source

/**
 * RAMPART - Robust Automatic MultiPle AssembleR Toolkit
 * Copyright (C) 2013  Daniel Mapleson - TGAC
 *
 * This program 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.
 *
 * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 **/
package uk.ac.tgac.conan.process.asm;

import org.apache.commons.lang.StringUtils;
import org.w3c.dom.Element;
import uk.ac.tgac.conan.core.data.Library;
import uk.ac.tgac.conan.core.util.XmlHelper;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * User: maplesod
 * Date: 14/08/13
 * Time: 10:28
 */
public class KmerRange extends ArrayList<Integer> {

    // Limits
    public static final int KMER_MIN = 11;
    public static final int KMER_MAX = 125;

    // Xml Config keys
    private static final String KEY_ATTR_K_MIN = "min";
    private static final String KEY_ATTR_K_MAX = "max";
    private static final String KEY_ATTR_STEP = "step";
    private static final String KEY_ATTR_LIST = "list";

    private int minK;
    private int maxK;
    private int stepSize;

    /**
     * Default Kmer range (just K61)
     */
    public KmerRange() {
        this(61, 61, StepSize.MEDIUM);
    }

    /**
     * Generate Kmer Range from properties
     * @param minKmer Minimum kmer length (may be automatically adjusted by stepper to first valid kmer)
     * @param maxKmer Maximum kmer length
     * @param stepSize How big the jump between kmer values should be
     */
    public KmerRange(int minKmer, int maxKmer, StepSize stepSize) {

        this(minKmer, maxKmer, stepSize.getStep());
    }

    /**
     * Generate Kmer Range from properties
     * @param minKmer Minimum kmer length (may be automatically adjusted by stepper to first valid kmer)
     * @param maxKmer Maximum kmer length
     * @param stepSize How big the jump between kmer values should be
     */
    public KmerRange(int minKmer, int maxKmer, int stepSize) {

        // Init arraylist
        super();

        // Generate list of kmers from range properties
        this.setFromProperties(minKmer, maxKmer, stepSize);
    }

    /**
     * Generates a kmer range from a comma separated list
     * @param list kmer values separated by commas.  Whitespace is tolerated.
     */
    public KmerRange(String list) {

        // Init arraylist
        super();

        // Split string and generate kmers from list
        this.setFromString(list);
    }

    /**
     * Creates a kmer range object from an Xml Config element.  If there's a list attribute then use that.  If not then
     * we assume a range has been specified, and we generate a list from those attributes.
     * @param ele
     */
    public KmerRange(Element ele, int defaultMin, int defaultMax, int defaultStep) {

        if (ele.hasAttribute(KEY_ATTR_LIST)) {
            this.setFromString(XmlHelper.getTextValue(ele, KEY_ATTR_LIST));
        } else {

            String step = XmlHelper.getTextValue(ele, KEY_ATTR_STEP).toUpperCase();

            int min = ele.hasAttribute(KEY_ATTR_K_MIN) ? XmlHelper.getIntValue(ele, KEY_ATTR_K_MIN) : defaultMin;

            int max = ele.hasAttribute(KEY_ATTR_K_MAX) ? XmlHelper.getIntValue(ele, KEY_ATTR_K_MAX) : defaultMax;

            int stepSize = ele.hasAttribute(KEY_ATTR_STEP)
                    ? StringUtils.isNumeric(step) ? Integer.parseInt(step) : StepSize.valueOf(step).getStep()
                    : defaultStep;

            this.setFromProperties(min, max, stepSize);
        }
    }

    protected final void setFromProperties(int minKmer, int maxKmer, int stepSize) {

        this.minK = minKmer;
        this.maxK = maxKmer;
        this.stepSize = stepSize;

        if (stepSize == 0) {
            this.add(firstValidKmer(minKmer));
        } else {
            for (int k = firstValidKmer(minKmer); k <= maxKmer; k += stepSize) {
                this.add(k);
            }
        }
    }

    protected final void setFromString(String list) {
        String[] parts = list.split(",");
        for (String part : parts) {
            this.add(Integer.parseInt(part.trim()));
        }

        Collections.sort(this);

        this.minK = this.getFirstKmer();
        this.maxK = this.getLastKmer();
        this.stepSize = (this.maxK - this.minK) / this.size(); // This is a bit of a hack because we have no way of working
                                                               // out what the step size is from a list of kmers
    }

    public static int getLastKmerFromLibs(List<Library> libs) {

        if (libs == null || libs.isEmpty()) {
            return 0;
        }

        int maxK = 10000;

        for (Library lib : libs) {
            maxK = Math.min(maxK, getLastKmerFromLib(lib));
        }

        return maxK;
    }

    public static int getLastKmerFromLib(Library lib) {

        if (lib == null) {
            return 0;
        }

        return lib.getReadLength() - 1;
    }

    public int getFirstKmer() {
        return this.get(0);
    }

    public int getLastKmer() {
        return this.get(this.size() - 1);
    }

    public int getStepSize() {
        return this.stepSize;
    }

    private int firstValidKmer(int kmin) {
        return (kmin % 2 == 0) ? kmin + 1 : kmin;
    }

    public enum StepSize {
        FINE(4), MEDIUM(10), COARSE(20);

        private int ss;

        private StepSize(int stepSize) {
            ss = stepSize;
        }

        /**
         * Retrieves the next k-mer value in the sequence
         *
         * @param kmer The current k-mer value
         * @return The next kmer value
         */
        public int nextKmer(int kmer) {
            return kmer += this.ss;
        }

        public int getStep() {
            return ss;
        }
    }

    /**
     * Determines whether or not the supplied kmer range in this object is valid.  Throws an IllegalArgumentException if not.
     */
    public boolean validate() {

        //TODO This logic isn't bullet proof... we can still nudge the minKmer above the maxKmer

        if (this.size() > 0) {

            if (this.getFirstKmer() < KMER_MIN || this.getLastKmer() < KMER_MIN)
                throw new IllegalArgumentException("K-mer values must be >= " + KMER_MIN + "nt");

            if (this.getFirstKmer() > KMER_MAX || this.getLastKmer() > KMER_MAX)
                throw new IllegalArgumentException("K-mer values must be <= " + KMER_MAX + "nt");

            if (this.getFirstKmer() > this.getLastKmer())
                throw new IllegalArgumentException("Error: Min K-mer value must be <= Max K-mer value");

            return true;
        }

        return false;
    }

    @Override
    public String toString() {
        return "K-mer Range: {" + StringUtils.join(this, ",") + "}";
    }
}