com.rtg.launcher.CommonFlags.java Source code

Java tutorial

Introduction

Here is the source code for com.rtg.launcher.CommonFlags.java

Source

/*
 * Copyright (c) 2014. Real Time Genomics Limited.
 *
 * Use of this source code is bound by the Real Time Genomics Limited Software Licence Agreement
 * for Academic Non-commercial Research Purposes only.
 *
 * If you did not receive a license accompanying this file, a copy must first be obtained by email
 * from support@realtimegenomics.com.  On downloading, using and/or continuing to use this source
 * code you accept the terms of that license agreement and any amendments to those terms that may
 * be made from time to time by Real Time Genomics Limited.
 */
package com.rtg.launcher;

import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

import org.apache.commons.lang.ArrayUtils;

import com.rtg.calibrate.Recalibrate;
import com.rtg.ngs.NgsFilterParams;
import com.rtg.ngs.NgsOutputParamsBuilder;
import com.rtg.ngs.OutputFilter;
import com.rtg.reference.ReferenceGenome;
import com.rtg.util.Constants;
import com.rtg.util.Environment;
import com.rtg.util.IntegerOrPercentage;
import com.rtg.util.InvalidParamsException;
import com.rtg.util.cli.CFlags;
import com.rtg.util.cli.CommonFlagCategories;
import com.rtg.util.cli.Flag;
import com.rtg.util.diagnostic.Diagnostic;
import com.rtg.util.diagnostic.ErrorType;
import com.rtg.util.diagnostic.NoTalkbackSlimException;
import com.rtg.util.intervals.LongRange;
import com.rtg.util.machine.MachineOrientation;

/**
 * Container for common flags
 */
public final class CommonFlags {

    /** Name of summary file. */
    public static final String SUMMARY_FILE = "summary.txt";

    private CommonFlags() {
    }

    /** Default word size */
    public static final int DEFAULT_WORD_SIZE = 22;
    /** output filter flag */
    public static final String OUTPUT_FILTER_FLAG = "output-filter";
    /** orientation of proper pairs */
    public static final String PAIR_ORIENTATION_FLAG = "orientation";
    /** minimum fragment flag */
    public static final String MIN_FRAGMENT_SIZE = "min-fragment-size";
    /** maximum fragment size flag */
    public static final String MAX_FRAGMENT_SIZE = "max-fragment-size";
    /** sort flag */
    public static final String SORT_FLAG = "sort";
    /** Flag which decides if unmapped sequences are to be written. */
    public static final String EXCLUDE_FLAG = "Xexclude";
    /** score indel flag */
    public static final String XSCORE_INDEL = "Xscoreindel";
    /** flag for whether to compress hashes or not. */
    public static final String COMPRESS_HASHES_FLAG = "Xcompress-hashes";
    /** Max top results flag. */
    public static final String MAX_TOP_RESULTS_FLAG = "max-top-results";
    /** don't report unmapped */
    public static final String NO_UNMAPPED = "no-unmapped";
    /** don't report unmated */
    public static final String NO_UNMATED = "no-unmated";
    /** Max allowed mismatches for unmated reads */
    public static final String UNMATED_MISMATCH_THRESHOLD = "max-unmated-mismatches";
    /** Max allowed mismatches for mated reads */
    public static final String MATED_MISMATCH_THRESHOLD = "max-mated-mismatches";
    /** Mask flag. */
    public static final String MASK_FLAG = "Xmask";
    /** Flag for using ids instead of names of sequences in output. */
    public static final String USEIDS_FLAG = "Xuseids";
    /** Top N reads flag. */
    public static final String TOPN_RESULTS_FLAG = "Xmax-topn-results";
    /** Default top n value */
    public static final int DEFAULT_TOP_N = 10;
    /** <code>topequal</code> output filter */
    public static final String TOPEQUAL = "topequal";
    /** <code>topn</code> output filter */
    public static final String TOPN = "topn";
    /** max matches allowed during alignment flag */
    public static final String MAX_ALIGNMENT_MISMATCHES = "max-mismatches";
    /** flag for setting the step size */
    public static final String STEP_FLAG = "step";
    /** flag for disabling compression of output files */
    public static final String NO_GZIP = "no-gzip";
    /** Specify directory for temporary files */
    public static final String TEMP_DIR = "tempdir";
    /** Specify whether temporary files should be compressed */
    public static final String TEMP_FILES_COMPRESSED = "Xtemp-files-gzipped";
    /** indel length flag */
    public static final String INDEL_LENGTH_FLAG = "indel-length";
    /** substitution flag */
    public static final String SUBSTITUTIONS_FLAG = "substitutions";
    /** word size flag */
    public static final String WORDSIZE_FLAG = "word";
    /** indel flag */
    public static final String INDELS_FLAG = "indels";
    /** template flag */
    public static final String TEMPLATE_FLAG = "template";
    /** don't load template into memory */
    public static final String NO_INMEMORY_TEMPLATE = "Xno-inmemory-template";
    /** reads flag */
    public static final String READS_FLAG = "input";
    /** Name of the output file. */
    public static final String OUTPUT_FLAG = "output";
    /** threads flag */
    public static final String THREADS_FLAG = "threads";
    /** Repeat frequency flag. */
    public static final String REPEAT_FREQUENCY_FLAG = "repeat-freq";
    /** Maximum repeat frequency */
    public static final String MAX_REPEAT_FREQUENCY_FLAG = "Xmax-repeat-freq";
    /** Maximum repeat frequency */
    public static final String MIN_REPEAT_FREQUENCY_FLAG = "Xmin-repeat-freq";
    /** Force mapping commands to use long read code path instead of short read */
    public static final String FORCE_LONG_FLAG = "Xforce-long";
    /** Flag for disabling the maximum file check **/
    public static final String NO_MAX_FILES_FLAG = "Xno-max-files";
    /** the read id of the first read to map */
    public static final String START_READ_ID = "start-read";
    /** the read id of the last read to map */
    public static final String END_READ_ID = "end-read";
    /** list of input files */
    public static final String INPUT_LIST_FLAG = "input-list-file";
    /** Parallel unmated processing */
    public static final String PARALLEL_UNMATED_PROCESSING_FLAG = "Xparallel-unmated-processing";
    /** flag for suppressing TABIX and BAM index creation */
    public static final String NO_INDEX = "no-index";

    // Property name that says where to load AVR models from
    static final String ENVIRONMENT_MODELS_DIR = "models.dir";
    // Default AVR model name looked for within the above dir
    private static final String MODEL_DEFAULT = "illumina-exome.avr";
    // Name to explicitly specify no AVR model to be used
    private static final String AVR_NONE_NAME = "none";

    /** flag for loading an AVR model to use for predictions */
    public static final String AVR_MODEL_FILE_FLAG = "avr-model";
    /** Minimum AVR Score Filter Flag */
    public static final String FILTER_AVR_FLAG = "min-avr-score";

    //commonly used flag description strings
    /** DIR */
    public static final String DIR = "DIR";
    /** SDF */
    public static final String SDF = "SDF";
    /** INT */
    public static final String INT = "INT";
    /** FLOAT */
    public static final String FLOAT = "FLOAT";
    /** BOOL */
    public static final String BOOL = "BOOL";
    /** FILE */
    public static final String FILE = "FILE";
    /** STRING */
    public static final String STRING = "STRING";
    /** FILE OR SDF */
    public static final String SDF_OR_FILE = "SDF|FILE";

    /** File name argument used to indicate read from stdin or write to stdout */
    public static final String STDIO_NAME = "-";

    private static final int BITS_FOR_SCORE1 = 12;
    /** Maximum value of score that can be stored in top n data structure. */
    public static final int MAX_SCORE = (1 << BITS_FOR_SCORE1) - 1;

    /**
     * Validate the reads flags.
     * @param flags the flags
     * @param sdf true if reads are in SDF format
     * @return true if valid, otherwise false
     */
    public static boolean validateReads(CFlags flags, boolean sdf) {
        if (sdf && !validateSDF(flags, READS_FLAG)) {
            return false;
        }
        return validateStartEnd(flags, CommonFlags.START_READ_ID, CommonFlags.END_READ_ID);
    }

    /**
     * Validate start and end flag for sequences
     * @param flags object containing flags
     * @param startFlag flag representing start sequence
     * @param endFlag flag representing end sequence
     * @return true if valid
     */
    public static boolean validateStartEnd(CFlags flags, String startFlag, String endFlag) {
        if (flags.isSet(startFlag)) {
            if (((Long) flags.getValue(startFlag)) < 0) {
                flags.error("--" + startFlag + " should be positive");
                return false;
            }
        }
        if (flags.isSet(endFlag)) {
            if (((Long) flags.getValue(endFlag)) < 1) {
                flags.error("--" + endFlag + " should be greater than 0");
                return false;
            }
        }
        if (flags.isSet(startFlag) && flags.isSet(endFlag)) {
            final long start = (Long) flags.getValue(startFlag);
            final long end = (Long) flags.getValue(endFlag);
            if (start >= end) {
                flags.error("--" + startFlag + " should be less than --" + endFlag);
                return false;
            }
            if (end - start > Integer.MAX_VALUE) {
                flags.error("You have specified too many reads, please specify a range of less than "
                        + Integer.MAX_VALUE + " reads.");
                return false;
            }
        }
        return true;
    }

    /**
     * Validate the templates flags.
     * @param flags the flags
     * @return true if valid, otherwise false
     */
    public static boolean validateTemplate(final CFlags flags) {
        return validateSDF(flags, TEMPLATE_FLAG);
    }

    /**
     * Basic SDF file check
     * @param flags flags to check
     * @param flagName name of the flag
     * @return true if indicated path exists and is a directory
     */
    public static boolean validateSDF(CFlags flags, String flagName) {
        return validateSDF((File) flags.getFlag(flagName).getValue());
    }

    /**
     * Basic SDF file check
     * @param file file to check
     * @return true if indicated path exists and is a directory
     */
    public static boolean validateSDF(File file) {
        if (!file.exists()) {
            Diagnostic.error(ErrorType.SDF_NOT_FOUND, file.getPath());
            return false;
        }
        if (!file.isDirectory()) {
            Diagnostic.error(ErrorType.NOT_SDF, file.getPath());
            return false;
        }
        return true;
    }

    /**
     * Validate the output dir flags.
     * @param flags the flags
     * @return true if valid, otherwise false
     */
    public static boolean validateOutputDirectory(final CFlags flags) {
        final File outputDir = (File) flags.getValue(OUTPUT_FLAG);
        return validateOutputDirectory(outputDir);
    }

    /**
     * Validate the threads flags.
     * @param flags the flags
     * @return true if valid, otherwise false
     */
    public static boolean validateThreads(final CFlags flags) {
        if (flags.isSet(THREADS_FLAG)) {
            final int threads = (Integer) flags.getValue(THREADS_FLAG);
            if (threads <= 0) {
                Diagnostic.error(ErrorType.INVALID_MIN_INTEGER_FLAG_VALUE, "--" + THREADS_FLAG, threads + "", "1");
                return false;
            }
            final int maxThreads = Environment.getAvailableProcessors() * 10;
            if (threads > maxThreads) {
                Diagnostic.error(ErrorType.INVALID_MAX_INTEGER_FLAG_VALUE, "--" + THREADS_FLAG, threads + "",
                        maxThreads + "");
                return false;
            }
        }
        return true;
    }

    /**
     * Return the word size based on word size flag or read length
     * @param flags the flags
     * @param readLength the read length
     * @param defWordSize the default max word size
     * @return the word size
     */
    public static int getWordSize(CFlags flags, int readLength, int defWordSize) {
        if (flags.isSet(WORDSIZE_FLAG)) {
            return (Integer) flags.getValue(WORDSIZE_FLAG);
        } else {
            return Math.min(defWordSize, readLength / 2);
        }
    }

    /**
     * Validate step and word size flags
     * @param flags shared flags
     * @return true if valid, otherwise false
     */
    public static boolean validateStepAndWordSize(CFlags flags) {
        final Integer wordSize = flags.isSet(WORDSIZE_FLAG) ? (Integer) flags.getValue(WORDSIZE_FLAG) : null;
        if (wordSize != null && wordSize < 1) {
            Diagnostic.error(ErrorType.INVALID_MIN_INTEGER_FLAG_VALUE, "--" + WORDSIZE_FLAG,
                    Integer.toString(wordSize), "1");
            return false;
        }
        if (flags.isSet(CommonFlags.STEP_FLAG)) {
            final int step = (Integer) flags.getValue(CommonFlags.STEP_FLAG);
            if (step < 1) {
                Diagnostic.error(ErrorType.INVALID_MIN_INTEGER_FLAG_VALUE, "--" + CommonFlags.STEP_FLAG,
                        Integer.toString(step), "1");
                return false;
            }
        }
        return true;
    }

    /**
     * Validates an integer flag has a value between two values
     * @param flags the flags
     * @param flagName the flag name to check
     * @param lowValue the low value (inclusive)
     * @param highValue the high value (inclusive)
     * @return true if the flag exists and is between the values, otherwise false.
     */
    public static boolean validateFlagBetweenValues(CFlags flags, String flagName, int lowValue, int highValue) {
        final int value = (Integer) flags.getValue(flagName);
        if (value < lowValue) {
            Diagnostic.error(ErrorType.INVALID_MIN_INTEGER_FLAG_VALUE, "--" + flagName, value + "", lowValue + "");
            return false;
        }
        if (value > highValue) {
            Diagnostic.error(ErrorType.INVALID_MAX_INTEGER_FLAG_VALUE, "--" + flagName, value + "", highValue + "");
            return false;
        }
        return true;
    }

    /**
     * Validates an float flag has a value between two values
     * @param flags the flags
     * @param flagName the flag name to check
     * @param lowValue the low value (inclusive)
     * @param highValue the high value (inclusive)
     * @return true if the flag exists and is between the values, otherwise false.
     */
    public static boolean validateFlagBetweenValues(CFlags flags, String flagName, double lowValue,
            double highValue) {
        final double value = (double) flags.getValue(flagName);
        if (value < lowValue) {
            Diagnostic.error(ErrorType.INVALID_MIN_INTEGER_FLAG_VALUE, "--" + flagName, value + "", lowValue + "");
            return false;
        }
        if (value > highValue) {
            Diagnostic.error(ErrorType.INVALID_MAX_INTEGER_FLAG_VALUE, "--" + flagName, value + "", highValue + "");
            return false;
        }
        return true;
    }

    /**
     * Return the output filter specified by the flags
     * @param flags the flags
     * @return the output filter, or none if no filter specified
     */
    public static OutputFilter filter(final CFlags flags) {
        if (flags.getFlag(OUTPUT_FILTER_FLAG) != null) {
            return OutputFilter.valueOf((String) flags.getValue(OUTPUT_FILTER_FLAG));
        } else {
            return OutputFilter.NONE;
        }
    }

    /**
     *
     * @param flags shared flags
     */
    public static void initNoMaxFile(CFlags flags) {
        flags.registerOptional(NO_MAX_FILES_FLAG, "override maximum number of files")
                .setCategory(CommonFlagCategories.UTILITY);
    }

    /**
     * Initialise mapping IO flags (input, output, template)
     * @param flags shared flags
     */
    public static void initOutputDirFlag(CFlags flags) {
        flags.registerRequired('o', OUTPUT_FLAG, File.class, DIR, BuildCommon.RESOURCE.getString("OUTPUT_DESC"))
                .setCategory(CommonFlagCategories.INPUT_OUTPUT);
    }

    /**
     * Initialise mapping IO flags (input, output, template)
     * @param flags shared flags
     */
    public static void initMapIOFlags(CFlags flags) {
        flags.registerRequired('i', READS_FLAG, File.class, SDF, BuildCommon.RESOURCE.getString("READS_DESC"))
                .setCategory(CommonFlagCategories.INPUT_OUTPUT);
        initTemplateFlag(flags);
        initOutputDirFlag(flags);
    }

    /**
     * Initialise the template flag
     * @param flags shared flags
     */
    public static void initTemplateFlag(CFlags flags) {
        flags.registerRequired('t', TEMPLATE_FLAG, File.class, SDF, BuildCommon.RESOURCE.getString("TEMPLATE_DESC"))
                .setCategory(CommonFlagCategories.INPUT_OUTPUT);
    }

    /**
     * Initialise shared flags
     * @param flags shared flags
     */
    public static void initSharedFlags(CFlags flags) {
        initMaskFlags(flags);
        initSharedFlagsOnly(flags);
        initStepSize(flags);
    }

    /**
     * Initialise shared flags without mask flags
     * @param flags shared flags
     */
    public static void initSharedFlagsOnly(CFlags flags) {
        initSharedFlagsOnly(flags, IntegerOrPercentage.valueOf("90%"), 1, 1000);
    }

    /**
     * Initialise shared flags without mask flags
     * @param flags shared flags
     * @param repeat value to set default repeat frequency
     * @param minRepeat value to set default minimum repeat frequency
     * @param maxRepeat value to set default max repeat frequency
     */
    public static void initSharedFlagsOnly(CFlags flags, IntegerOrPercentage repeat, int minRepeat, int maxRepeat) {
        flags.registerOptional(REPEAT_FREQUENCY_FLAG, IntegerOrPercentage.class, INT,
                BuildCommon.RESOURCE.getString("REPEAT_FREQUENCY_DESC"), repeat)
                .setCategory(CommonFlagCategories.SENSITIVITY_TUNING);
        flags.registerOptional(MAX_REPEAT_FREQUENCY_FLAG, Integer.class, INT,
                BuildCommon.RESOURCE.getString("MAX_REPEAT_FREQUENCY_DESC"), maxRepeat)
                .setCategory(CommonFlagCategories.SENSITIVITY_TUNING);
        flags.registerOptional(MIN_REPEAT_FREQUENCY_FLAG, Integer.class, INT,
                BuildCommon.RESOURCE.getString("MIN_REPEAT_FREQUENCY_DESC"), minRepeat)
                .setCategory(CommonFlagCategories.SENSITIVITY_TUNING);
        initNoGzip(flags);
        flags.registerOptional(PARALLEL_UNMATED_PROCESSING_FLAG, Boolean.class, BOOL,
                "run unmated processing in parallel with mated processing", Boolean.FALSE)
                .setCategory(CommonFlagCategories.UTILITY);
        initThreadsFlag(flags);
    }

    /**
     * initialize flag to not compress output.
     * @param flags flags to register with
     */
    public static void initNoGzip(CFlags flags) {
        flags.registerOptional('Z', NO_GZIP, "do not gzip the output").setCategory(CommonFlagCategories.UTILITY);
    }

    /**
     * initialize flag to read number of threads.
     * @param flags flags to register with
     */
    public static void initThreadsFlag(CFlags flags) {
        flags.registerOptional('T', THREADS_FLAG, Integer.class, INT,
                "number of threads. Defaults to the number of available cores")
                .setCategory(CommonFlagCategories.UTILITY);
    }

    /**
     * Init word size flag with given description.
     * @param flags flags to add to
     * @param desc description to register
     */
    public static void initWordSize(CFlags flags, String desc) {
        flags.registerOptional('w', WORDSIZE_FLAG, Integer.class, INT, desc)
                .setCategory(CommonFlagCategories.SENSITIVITY_TUNING);
    }

    /**
     * initialize flag to suppress TABIX and BAM index creation
     * @param flags flags to register with
     */
    public static void initIndexFlags(CFlags flags) {
        flags.registerOptional(NO_INDEX, "do not produce indexes for output files")
                .setCategory(CommonFlagCategories.UTILITY);
    }

    /**
     * Init step size flag
     * @param flags flags to add to.
     */
    public static void initStepSize(CFlags flags) {
        initStepSize(flags, "step size (Default is word size)");
    }

    /**
     * Init step size flag
     * @param flags flags to add to.
     * @param desc description for flag
     */
    public static void initStepSize(CFlags flags, String desc) {
        flags.registerOptional('s', STEP_FLAG, Integer.class, INT, desc)
                .setCategory(CommonFlagCategories.SENSITIVITY_TUNING);
    }

    /**
     * Adds mask flags -w -a -b -c to the given <code>CFlags</code> object
     * @param flags flags to add to
     */
    public static void initMaskFlags(CFlags flags) {
        initWordSize(flags,
                "word size (Default is " + DEFAULT_WORD_SIZE + ", or read length / 2, whichever is smaller)");
        initMaskFlagsOnly(flags);
    }

    /**
     * Adds mask flags -a -b -c to the given <code>CFlags</code> object
     * @param flags flags to add to
     */
    public static void initMaskFlagsOnly(CFlags flags) {
        flags.registerOptional('a', SUBSTITUTIONS_FLAG, Integer.class, INT,
                "guaranteed minimum number of substitutions which will be detected")
                .setCategory(CommonFlagCategories.SENSITIVITY_TUNING);
        flags.registerOptional('b', INDELS_FLAG, Integer.class, INT,
                "guaranteed minimum number of indels which will be detected")
                .setCategory(CommonFlagCategories.SENSITIVITY_TUNING);
        flags.registerOptional('c', INDEL_LENGTH_FLAG, Integer.class, INT,
                "guaranteed number of positions that will be detected in a single indel", 1)
                .setCategory(CommonFlagCategories.SENSITIVITY_TUNING);
    }

    /**
     * Initialise flags for paired end specific details (min and max insert sizes)
     * @param flags shared flags
     */
    public static void initFragmentSizeFlags(final CFlags flags) {
        flags.registerOptional('M', MAX_FRAGMENT_SIZE, Integer.class, "INT",
                "maximum permitted fragment size when mating paired reads", 1000)
                .setCategory(CommonFlagCategories.SENSITIVITY_TUNING);
        flags.registerOptional('m', MIN_FRAGMENT_SIZE, Integer.class, "INT",
                "minimum permitted fragment size when mating paired reads", 0)
                .setCategory(CommonFlagCategories.SENSITIVITY_TUNING);
    }

    /**
     * Initialise flags for paired end specific details (min and max insert sizes)
     * @param flags shared flags
     */
    public static void initPairedEndFlags(final CFlags flags) {
        flags.registerOptional('d', PAIR_ORIENTATION_FLAG, MachineOrientation.class, "STRING",
                "orientation for proper pairs", MachineOrientation.ANY)
                .setCategory(CommonFlagCategories.SENSITIVITY_TUNING);
        initFragmentSizeFlags(flags);
    }

    /**
     * Initialise flags for ranges of reads
     * @param flags shared flags
     */
    public static void initReadRange(final CFlags flags) {
        flags.registerOptional(CommonFlags.START_READ_ID, Long.class, "INT", "inclusive lower bound on read id")
                .setCategory(CommonFlagCategories.FILTERING);
        flags.registerOptional(CommonFlags.END_READ_ID, Long.class, "INT", "exclusive upper bound on read id")
                .setCategory(CommonFlagCategories.FILTERING);
    }

    /**
     * Initialise flag for minimum AVR score
     * @param flags shared flags
     */
    public static void initMinAvrScore(final CFlags flags) {
        flags.registerOptional(FILTER_AVR_FLAG, Double.class, "Float",
                "if set, fail variants with AVR scores below this value")
                .setCategory(CommonFlagCategories.REPORTING);
    }

    /**
     * Initialise flag for AVR model to use for prediction
     *
     * @param flags shared flags
     * @param anonymous if true, register the flag as an anonymous flag
     * @return the newly registered flag
     */
    public static Flag initAvrModel(final CFlags flags, boolean anonymous) {
        String description = "name of AVR model to use when scoring variants";
        boolean hasDefault = false;
        final String modelDirName = Environment.getEnvironmentMap().get(ENVIRONMENT_MODELS_DIR);
        if (modelDirName != null) {
            final File modelDir = new File(modelDirName);
            if (modelDir.exists() && modelDir.isDirectory()) {
                final String[] models = modelDir.list(new FilenameFilter() {
                    @Override
                    public boolean accept(File dir, String name) {
                        return name.endsWith(".avr");
                    }
                });
                if (!anonymous && models != null && models.length > 0) {
                    final String[] newModels = new String[models.length + 1];
                    newModels[0] = AVR_NONE_NAME;
                    System.arraycopy(models, 0, newModels, 1, models.length);
                    description += " (Must be one of " + Arrays.toString(newModels) + " or a path to a model file)";
                    if (ArrayUtils.contains(newModels, MODEL_DEFAULT)) {
                        hasDefault = true;
                    }
                }
            }
        }
        final Flag modelFlag = anonymous
                ? flags.registerRequired(File.class, "file", description)
                        .setCategory(CommonFlagCategories.REPORTING)
                : flags.registerOptional(AVR_MODEL_FILE_FLAG, File.class, "file", description)
                        .setCategory(CommonFlagCategories.REPORTING);
        if (!anonymous && hasDefault) {
            modelFlag.setParameterDefault(MODEL_DEFAULT);
        }
        return modelFlag;
    }

    // If the environment for models is set up and the default model exists within it, return the default model, otherwise null
    private static File defaultAvrModel() {
        final String modelsDir = Environment.getEnvironmentMap().get(ENVIRONMENT_MODELS_DIR);
        if (modelsDir == null) {
            return null;
        } else {
            final File modelsDirFile = new File(modelsDir);
            if (!modelsDirFile.exists() || !modelsDirFile.isDirectory()) {
                throw new InvalidParamsException(
                        "The AVR models directory cannot be found or is not a directory: " + modelsDir);
            }
            final File model = new File(modelsDir, MODEL_DEFAULT);
            return model.exists() ? model : null;
        }
    }

    /**
     * Gets the AVR model to use for prediction. If the user has supplied a model name, it will be first
     * searched for as an actual file name, or secondly as a name within the environmental model directory
     * (if configured)
     *
     *
     * @param flags shared flags
     * @param anonymous true if the model was registered as the only anonymous flag
     * @return a File pointing at the specified AVR model, or null if no AVR model is to be used
     * @throws InvalidParamsException if the user specified a model that does not exist, or a models directory which does
     * not exist or is not a directory.
     */
    public static File getAvrModel(final CFlags flags, boolean anonymous) {
        File avrModel = defaultAvrModel();
        final Flag flag = anonymous ? flags.getAnonymousFlag(0) : flags.getFlag(AVR_MODEL_FILE_FLAG);
        if (flag != null && flag.isSet()) {
            final String modelsDir = Environment.getEnvironmentMap().get(ENVIRONMENT_MODELS_DIR);
            final File userModel = (File) flag.getValue();
            if (AVR_NONE_NAME.equals(userModel.toString())) {
                return null;
            } else if (userModel.exists()) {
                avrModel = userModel;
            } else if (modelsDir != null) {
                avrModel = new File(modelsDir, userModel.getName());
            }
            if (avrModel == null || !avrModel.exists()) {
                throw new InvalidParamsException(
                        "The specified AVR model could not be found: " + userModel.toString());
            }
        }
        return avrModel;
    }

    /**
     * Populate {@link NgsOutputParamsBuilder}
     * @param flags command line flags
     * @param builder builder to add
     * @param filterParams ngs filter params
     */
    public static void buildOutputParams(final CFlags flags, final NgsOutputParamsBuilder builder,
            final NgsFilterParams filterParams) {
        builder.outputDir((File) flags.getValue(OUTPUT_FLAG)).filterParams(filterParams)
                .sorted(flags.isSet(SORT_FLAG));
    }

    /**
     * validate mask parameter
     * @param readLength read length
     * @param w word
     * @param subs substitution
     * @param i indel
     * @param l indel length
     * @throws InvalidParamsException if parameter is not valid
     */
    public static void validateMaskParams(final int readLength, final int w, final int subs, final int i,
            final int l) {
        if (w <= 0) {
            throw new InvalidParamsException(ErrorType.INVALID_MIN_INTEGER_FLAG_VALUE, "-w", w + "", "1");
        }
        if (subs < 0) {
            throw new InvalidParamsException(ErrorType.INVALID_MIN_INTEGER_FLAG_VALUE, "-a", subs + "", "0");
        }
        if (i < 0) {
            throw new InvalidParamsException(ErrorType.INVALID_MIN_INTEGER_FLAG_VALUE, "-b", i + "", "0");
        }
        if (l <= 0) {
            throw new InvalidParamsException(ErrorType.INVALID_MIN_INTEGER_FLAG_VALUE, "-c", l + "", "1");
        }
        if (readLength < w) {
            throw new InvalidParamsException(ErrorType.WORD_NOT_LESS_READ, w + "", readLength + "");
        }
        if (i > 0 && l > readLength - w) {
            throw new InvalidParamsException(ErrorType.INVALID_MAX_INTEGER_FLAG_VALUE, "-c", l + "",
                    (readLength - w) + "");
        }
        if (i > 0 && l <= 0) {
            throw new InvalidParamsException(ErrorType.INVALID_MIN_INTEGER_FLAG_VALUE, "-c", l + "", "1");
        }
        if (i > readLength - w) {
            throw new InvalidParamsException(ErrorType.INVALID_MAX_INTEGER_FLAG_VALUE, "-b", i + "",
                    (readLength - w) + "");
        }
        if (subs > readLength - w) {
            throw new InvalidParamsException(ErrorType.INVALID_MAX_INTEGER_FLAG_VALUE, "-a", subs + "",
                    (readLength - w) + "");
        }
    }

    /**
     * This method checks that the repeat frequency flag is within appropriate boundaries.
     * @param flags the flags to check
     * @return <code>true</code> if all okay <code>false</code> otherwise
     */
    public static boolean checkRepeatFrequency(CFlags flags) {
        if (flags.isSet(REPEAT_FREQUENCY_FLAG)) {
            if (!validateFlagBetweenValues(flags, REPEAT_FREQUENCY_FLAG, 1, 100000)) {
                return false;
            }
        }
        return true;
    }

    /**
     * This method checks that the repeat frequency flag is within appropriate boundaries.
     * @param flags the flags to check
     * @return <code>true</code> if all okay <code>false</code> otherwise
     */
    public static boolean checkPercentRepeatFrequency(CFlags flags) {
        if (flags.isSet(REPEAT_FREQUENCY_FLAG)) {
            final IntegerOrPercentage repeat = (IntegerOrPercentage) flags.getValue(REPEAT_FREQUENCY_FLAG);
            if (repeat.isPercentage()) {
                if (repeat.getValue(100) < 0) {
                    Diagnostic.error(ErrorType.INVALID_MIN_INTEGER_FLAG_VALUE, "--" + REPEAT_FREQUENCY_FLAG,
                            repeat + "", "0%");
                    return false;
                } else if (repeat.getValue(100) > 100) {
                    Diagnostic.error(ErrorType.INVALID_MAX_INTEGER_FLAG_VALUE, "--" + REPEAT_FREQUENCY_FLAG,
                            repeat + "", "100%");
                    return false;
                }
            } else {
                if (repeat.getValue(100) < 1) {
                    Diagnostic.error(ErrorType.INVALID_MIN_INTEGER_FLAG_VALUE, "--" + REPEAT_FREQUENCY_FLAG,
                            repeat + "", 1 + "");
                    return false;
                }
                if (repeat.getValue(100) > 100000) {
                    Diagnostic.error(ErrorType.INVALID_MAX_INTEGER_FLAG_VALUE, "--" + REPEAT_FREQUENCY_FLAG,
                            repeat + "", 100000 + "");
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * Returns true if the input file is the stdin/stdout indicator
     * @param file to test
     * @return true if the file indicates stdin/stdout should be used
     */
    public static boolean isStdio(File file) {
        return isStdio(file.toString());
    }

    /**
     * Returns true if the input file is the stdin/stdout indicator
     * @param filename to test
     * @return true if the file indicates stdin/stdout should be used
     */
    public static boolean isStdio(String filename) {
        return STDIO_NAME.equals(filename);
    }

    /**
     * Check the file list and anonymous file input flags.
     * @param flags the flags to check
     * @param fileListFlag the flag name for the file list
     * @param singleInputFlag the flag name for single inputs, null if using anonymous
     * @param maxFiles the maximum number of files allowed
     * @return <code>true</code> if all okay <code>false</code> otherwise
     */
    public static boolean checkFileList(CFlags flags, String fileListFlag, String singleInputFlag, int maxFiles) {
        return checkFileList(flags, fileListFlag, singleInputFlag, maxFiles, false);
    }

    /**
     * Check the file list and anonymous file input flags.
     * @param flags the flags to check
     * @param fileListFlag the flag name for the file list
     * @param singleInputFlag the flag name for single inputs, null if using anonymous
     * @param maxFiles the maximum number of files allowed
     * @param ignoreCalibrationFiles ignores *.calibration files while counting number of files
     * @return <code>true</code> if all okay <code>false</code> otherwise
     */
    public static boolean checkFileList(CFlags flags, String fileListFlag, String singleInputFlag, int maxFiles,
            boolean ignoreCalibrationFiles) {
        final Collection<File> files;
        try {
            files = new CommandLineFiles(fileListFlag, singleInputFlag, CommandLineFiles.EXISTS).getFileList(flags);
        } catch (final NoTalkbackSlimException e) {
            flags.setParseMessage(e.getMessage());
            return false;
        } catch (final IOException e) {
            flags.setParseMessage("An error occurred reading " + flags.getValue(fileListFlag));
            return false;
        }
        if (getSize(files, ignoreCalibrationFiles) == 0) {
            flags.setParseMessage("No input files specified"
                    + (null == singleInputFlag ? "" : (" in --" + fileListFlag + " or --" + singleInputFlag))
                    + ".");
            return false;
        } else if (getSize(files, ignoreCalibrationFiles) > maxFiles && !flags.isSet(NO_MAX_FILES_FLAG)) {
            flags.setParseMessage("More than " + maxFiles + " input files specified.");
            return false;
        }
        return true;
    }

    /**
     * Function counts the files size
     * @param files Collection containing files
     * @param ignoreCalibrationFiles if true, ignores <code>.calibration</code> file count
     * @return count of files
     */
    private static int getSize(Collection<File> files, boolean ignoreCalibrationFiles) {
        if (!ignoreCalibrationFiles) {
            return files.size();
        }
        int count = 0;
        for (final File f : files) {
            if (!f.getName().endsWith(Recalibrate.EXTENSION)) {
                count++;
            }
        }
        return count;
    }

    /**
     * Get the list of files from the anonymous flag and a file list file flag.
     * @param flags the flags to get the list from
     * @param fileListFlag the flag name for the file list
     * @param singleInputFlag the flag name for single inputs, null if using anonymous
     * @param sdf check for valid SDF directories instead of for files
     * @return the list of files to process or null if there are missing files in the list
     * @throws IOException if reading the file list fails
     */
    public static List<File> getFileList(CFlags flags, String fileListFlag, String singleInputFlag, boolean sdf)
            throws IOException {
        final CommandLineFiles files = new CommandLineFiles(fileListFlag, singleInputFlag, CommandLineFiles.EXISTS);
        if (sdf) {
            files.addConstraint(CommandLineFiles.SDF);
        } else {
            files.addConstraint(CommandLineFiles.NOT_DIRECTORY); // REGULAR_FILE breaks bash-fu
        }
        return files.getFileList(flags);
    }

    /**
     * Test if the supplied directory already exists.
     * @param directory directory to test
     * @return true if directory is valid, otherwise a false and calls
     * <code>Diagnostic.error</code> with details of the reason it is invalid.
     * @exception NullPointerException if <code>directory</code> is null.
     */
    public static boolean validateOutputDirectory(File directory) {
        if (directory.exists()) {
            Diagnostic.error(ErrorType.DIRECTORY_EXISTS, directory.getPath());
            return false;
        }
        return true;
    }

    /**
     * Get the region defined by the start read and end read flags.
     * @param flags the flags to get the region from
     * @return the region to process
     */
    public static LongRange getReaderRestriction(CFlags flags) {
        final long start = flags.isSet(CommonFlags.START_READ_ID) ? (Long) flags.getValue(CommonFlags.START_READ_ID)
                : LongRange.MISSING;
        final long end = flags.isSet(CommonFlags.END_READ_ID) ? (Long) flags.getValue(CommonFlags.END_READ_ID)
                : LongRange.MISSING;
        return new LongRange(start, end);
    }

    /**
     * Check that the sex and template values are compatible.
     * @param flags the flags to check
     * @param sexFlag the flag specifying the sex
     * @param pedigreeFlag the flag specifying the pedigree
     * @param templateFlag the flag specifying the template
     * @return <code>true</code> if all okay <code>false</code> otherwise
     */
    public static boolean validateSexTemplateReference(CFlags flags, String sexFlag, String pedigreeFlag,
            String templateFlag) {
        return validateSexTemplateReference(flags, sexFlag, pedigreeFlag, (File) flags.getValue(templateFlag));
    }

    /**
     * Check that the sex and template values are compatible.
     * @param flags the flags to check
     * @param sexFlag the flag specifying the sex
     * @param pedigreeFlag the flag specifying the pedigree
     * @param template the File corresponding to the template SDF  @return <code>true</code> if all okay <code>false</code> otherwise
     * @return <code>true</code> if all okay <code>false</code> otherwise
     */
    public static boolean validateSexTemplateReference(CFlags flags, String sexFlag, String pedigreeFlag,
            File template) {
        if (flags.isSet(sexFlag) || (pedigreeFlag != null && flags.isSet(pedigreeFlag))) {
            if (!new File(template, ReferenceGenome.REFERENCE_FILE).isFile()) {
                flags.error("Sex-specific processing was specified but " + template + " is missing a '"
                        + ReferenceGenome.REFERENCE_FILE + "'");
                return false;
            }
        }
        return true;
    }

    /**
     * Returns number of I/O threads from a threads parameter (where 1
     * thread is reserved for main processing).  The total number of
     * threads is user supplied, otherwise the minimum of the number of
     * available processors and Constants.MAX_THREADS.
     * @param threads number of threads
     * @return an <code>int</code> value
     */
    public static int parseIOThreads(Integer threads) {
        final int result = threads == null ? Math.min(Constants.MAX_IO_THREADS, Environment.defaultThreads())
                : threads;
        return Math.max(1, result - 1);
    }

    /**
     * Returns number of threads.  If the <code>threads</code> parameter is null then the default thread number will be returned
     * @param threads number of threads
     * @return an <code>int</code> value
     */
    public static int parseThreads(Integer threads) {
        final int result = threads == null ? Environment.defaultThreads() : threads;
        return result;
    }

}