src/main/java/es/uvigo/darwin/prottest/util/fileio/AlignmentReader.java
public class AlignmentReader {

    /**
     * Instantiates a new alignment reader.
     */
    public AlignmentReader() {

    }

    /**
     * Read alignment.
     * 
     * @param output the output writer
     * @param filename the filename
     * @param debug the debug
     * 
     * @return the alignment
     * 
     * @throws AlignmentParseException the alignment parse exception.
     * @throws FileNotFoundException Signals that the input filename does not exist.
     * @throws IOException Signals that an I/O exception has occured.
     */
    public static Alignment readAlignment(PrintWriter output, String filename, boolean debug)
            throws AlignmentParseException, FileNotFoundException, IOException {

        Alignment alignment;
        StringBuilder text = new StringBuilder();

        BufferedReader br = new BufferedReader(new FileReader(filename));
        String s;
        while ((s = br.readLine()) != null) {
            text.append(s).append("\r\n");
        }
        br.close();


        //Get options
        String in = text.toString();
        String inO = "";
        String inP = "";
        String inF = "";
        boolean autodetect = true;
        boolean collapse = false;
        boolean gaps = false;
        boolean missing = false;
        int limit = 0;
        String out = "";
        String outO = "Linux";
        String os = System.getProperty("os.name");
        if (os.startsWith("Mac")) {
            outO = "MacOS";
        } else if (os.startsWith("Linux")) {
            outO = "Linux";
        } else if (os.startsWith("Win")) {
            outO = "Windows";
        }
        String outP = "ProtTest";
        String outF = "PHYLIP";
        boolean lower = false;
        boolean numbers = false;
        boolean sequential = true;
        boolean match = false;

        //Get converter and convert MSA
        Factory factory = new DefaultFactory();
        Converter converter;

        //Inicializar logger
        Logger logger = Logger.getLogger("alter" + System.currentTimeMillis());
        logger.setUseParentHandlers(false);
        logger.setLevel(Level.ALL);
        for (Handler handler : ProtTestLogger.getDefaultLogger().getHandlers()) {
            logger.addHandler(handler);
        }

        try {
            converter = factory.getConverter(inO, inP, inF, autodetect,
                    collapse, gaps, missing, limit,
                    outO, outP, outF, lower, numbers, sequential, match, logger.getName());
            out = converter.convert(in);
        } catch (UnsupportedOperationException ex) {
            throw new AlignmentParseException("There's some error in your data: " + ex.getMessage());
        } catch (ParseException ex) {
            throw new AlignmentParseException("There's some error in your data: " + ex.getMessage());
        }

        try {
        	PushbackReader pr = new PushbackReader(new StringReader(out));
        	alignment = readAlignment(output, pr, debug);
        } catch (AlignmentParseException ex) {
        	sequential = false;
        	try {
                converter = factory.getConverter(inO, inP, inF, autodetect,
                        collapse, gaps, missing, limit,
                        outO, outP, outF, lower, numbers, sequential, match, logger.getName());
                out = converter.convert(in);
            } catch (UnsupportedOperationException exUO) {
                throw new AlignmentParseException("There's some error in your data: " + ex.getMessage());
            } catch (ParseException exP) {
                throw new AlignmentParseException("There's some error in your data: " + ex.getMessage());
            }
        	PushbackReader pr = new PushbackReader(new StringReader(out));
        	alignment = readAlignment(output, pr, debug);
        }
        
        if (alignment == null) {
            throw new AlignmentParseException("There's some error in your data, exiting...");
        }

        return alignment;
    }

    /**
     * Read alignment.
     * 
     * @param out the out
     * @param pr the pushback reader which contains the alignment
     * @param debug the debug state
     * 
     * @return the alignment
     * 
     * @throws AlignmentParseException the alignment parse exception
     * @throws IOException Signals that an I/O exception has occured.
     */
    public static Alignment readAlignment(PrintWriter out, PushbackReader pr, boolean debug)
            throws AlignmentParseException, IOException {

        if (debug) {
            out.println("");
            out.println("**********************************************************");
            out.println("  Reading alignment...");
        }
        Alignment alignment = null;
        try {
            alignment = new ReadAlignment(pr);
        } catch (pal.alignment.AlignmentParseException e) {
            throw new AlignmentParseException(e.getMessage());
        }

        List<String> seqNames = new ArrayList<String>(alignment.getSequenceCount());
        for (int i = 0; i < alignment.getSequenceCount(); i++) {
            seqNames.add(alignment.getIdentifier(i).getName());
        }

        String currString;
        int size = alignment.getSequenceCount();
        for (int i = 0; i < size; i++) {
            currString = seqNames.get(i);
            for (int j = i + 1; j < size; j++) {
                if (seqNames.get(j).equals(currString)) {
                    throw new AlignmentParseException("ERROR: There are duplicated taxa names in the alignment: " + currString);
                }
            }
        }

        //TODO: print TAXA and check if data is in correct format
        if (debug) {
            for (int i = 0; i < alignment.getSequenceCount(); i++) {
                out.println("    Sequence #" + (i + 1) + ": " + alignment.getIdentifier(i).getName());
            }
            out.println("   Alignment contains " + alignment.getSequenceCount() + " sequences of length " + alignment.getSiteCount());
            out.println("");
            out.println("**********************************************************");
            out.println("");
        }

        return alignment;
    }

    /**
     * Read tree.
     * 
     * @param out the out
     * @param filename the filename
     * @param debug the debug
     * 
     * @return the tree
     * 
     * @throws TreeFormatException When the input filename is not in the right format
     * @throws FileNotFoundException Signals that the input filename does not exist.
     * @throws IOException Signals that an I/O exception has occured.
     */
    public static Tree readTree(PrintWriter out, String filename, boolean debug)
            throws TreeFormatException, FileNotFoundException, IOException {
        //FOR PHYML:
        //if tree is not in newick format, reformat and save to a new file:
        //TreeUtils: public static void printNH(Tree tree, java.io.PrintWriter out)
        if (debug) {
            out.println("Reading tree...");
        }
        Tree tree;
        try {
            tree = new ReadTree(filename);
        } catch (TreeParseException e) {
            throw new TreeFormatException("Error: Wrong tree format : " + e.getMessage());
        }
        if (debug) {
            out.println("Tree contains " + tree.getExternalNodeCount() + " external nodes");
            out.println("");
        }
        return tree;
    }

    /**
     * Read tree.
     * 
     * @param out the out
     * @param in the tree pushback reader
     * @param debug the debug
     * 
     * @return the tree
     * 
     * @throws TreeFormatException the tree format exception
     */
    public static Tree readTree(PrintWriter out, PushbackReader in, boolean debug)
            throws TreeFormatException {
        //FOR PHYML:
        //if tree is not in newick format, reformat and save to a new file:
        //TreeUtils: public static void printNH(Tree tree, java.io.PrintWriter out)
        if (debug) {
            out.println("Reading tree...");
        }
        Tree tree;
        try {
            tree = new ReadTree(in);
        } catch (TreeParseException e) {
            throw new TreeFormatException("Error: Wrong tree format : " + e.getMessage());
        }
        if (debug) {
            out.println("Tree contains " + tree.getExternalNodeCount() + " external nodes");
            out.println("");
        }
        return tree;
    }
}
src/main/java/es/uvigo/darwin/prottest/facade/util/SelectionChunk.java
public class SelectionChunk {

    /**
     * Akaike Information Criteiron constant.
     */
    public static final int AIC = 1;
    /**
     * Bayesian Information Criteiron constant.
     */
    public static final int BIC = 2;
    /**
     * Corrected Akaike Information Criteiron constant.
     */
    public static final int AICC = 3;
    /**
     * Decision Theory Criteiron constant.
     */
    public static final int DT = 4;
    /**
     * log Likelihood Criterion constant.
     */
    public static final int LNL = 5;
    /**
     * The inner information criterion
     */
    private InformationCriterion informationCriterion;

    /**
     * Gets the inner information criterion
     * 
     * @return the information criterion
     */
    public InformationCriterion getInformationCriterion() {
        return informationCriterion;
    }

    /**
     * Instantiates a new Selection Chunk.
     * 
     * @param informationCriterion the inner information criterion
     */
    public SelectionChunk(InformationCriterion informationCriterion) {
        this.informationCriterion = informationCriterion;
    }

    public boolean existGammaModels() {
        return informationCriterion.isExistGammaModels();
    }

    public boolean existInvModels() {
        return informationCriterion.isExistInvModels();
    }

    public boolean existGammaInvModels() {
        return informationCriterion.isExistGammaInvModels();
    }

    public boolean existFModels() {
        return informationCriterion.isExistFModels();
    }
    
    /**
     * Gets the criterion value of a specific substitution model.
     * 
     * @param model the substitution model
     * 
     * @return the criterion value
     */
    public double getValue(Model model) {
        return informationCriterion.get(model).getValue();
    }

    /**
     * Gets the incremental criterion value of a specific substitution model.
     * 
     * @param model the substitution model
     * 
     * @return the incremental criterion value
     */
    public double getDeltaValue(Model model) {
        return informationCriterion.get(model).getDeltaValue();
    }

    /**
     * Gets the relative weight of a specific substitution model.
     * 
     * @param model the substitution model
     * 
     * @return the relative weight
     */
    public double getWeightValue(Model model) {
        return informationCriterion.get(model).getWeightValue();
    }

    /**
     * Gets the overall importance of the alpha parameter.
     * 
     * @return the overall importance of the +G models.
     */
    public double getOverallAlpha() {
        return informationCriterion.getOverallAlpha();
    }

    /**
     * Gets the overall importance of the alpha parameter on +I+G models..
     * 
     * @return the overall importance.
     */
    public double getOverallAlphaInv() {
        return informationCriterion.getOverallAlphaInv();
    }

    /**
     * Gets the overall importance of the proportion of invariant sites
     * parameter.
     * 
     * @return the overall importance of the +I models.
     */
    public double getOverallInv() {
        return informationCriterion.getOverallInv();
    }

    /**
     * Gets the overall importance of the proportion of invariant sites 
     * parameter on +I+G models.
     * 
     * @return the overall importance
     */
    public double getOverallInvAlpha() {
        return informationCriterion.getOverallInvAlpha();
    }

    /**
     * Gets the relative importance of +G parameter.
     * 
     * @return the relative importance
     */
    public double getAlphaImportance() {
        return informationCriterion.getAlphaImportance();
    }

    /**
     * Gets the relative importance of +I parameter.
     * 
     * @return the relative importance
     */
    public double getInvImportance() {
        return informationCriterion.getInvImportance();
    }

    /**
     * Gets the relative importance of +I+G parameter.
     * 
     * @return the relative importance
     */
    public double getAlphaInvImportance() {
        return informationCriterion.getAlphaInvImportance();
    }

    /**
     * Gets the relative importance of empirical frequencies parameter.
     * 
     * @return the relative importance
     */
    public double getFImportance() {
        return informationCriterion.getFImportance();
    }

    /**
     * Gets the models which belong to the confidence interval.
     * 
     * @return the models in the confidence interval.
     */
    public Collection<SelectionModel> getConfidenceModels() {
        return informationCriterion.getConfidenceModels();
    }
}
src/main/java/es/uvigo/darwin/prottest/model/Model.java
public abstract class Model implements Serializable {

    /** The serialVersionUID. */
    private static final long serialVersionUID = 20090804L;

    // distributions
    /** Useful constant for uniform distribution. */
    public static final int DISTRIBUTION_UNIFORM = 0;
    /** Useful constant for distribution with a proportion of invariable sites. */
    public static final int DISTRIBUTION_INVARIABLE = 1;
    /** Useful constant for gamma distribution. */
    public static final int DISTRIBUTION_GAMMA = 2;
    /** Useful constant for gamma distribution with a proportion of invariable sites. */
    public static final int DISTRIBUTION_GAMMA_INV = 3;
    
    // frequencies distribution
    /** The value of Uniform Frequencies Distribution. */
    static final int FREQ_DISTRIBUTION_UNIFORM = 1;
    /** The value of Empirical Frequencies Distribution. */
    static final int FREQ_DISTRIBUTION_EMPIRICAL = 2;
    /** The value of Maximum Likelihood Frequencies Distribution. */
    static final int FREQ_DISTRIBUTION_MAXIMUM_LIKELIHOOD = 3;
    /** The value of any other frequencies distribution. */
    static final int FREQ_DISTRIBUTION_OTHER = 4;
    
    /** Useful constant for consider observed frequencies. */
    public static final String PROP_PLUS_F = "plusF";
    /** The matrix name. */
    private String matrix;
    /** The distribution. */
    private int distribution;
    /** Consider observed frequencies. */
    private boolean plusF;
    /** The frequencies distribution. */
    protected int frequenciesDistribution;
    /** The alignment hashcode. */
    private int alignment;
    /** The number of sequences. */
    private int numberOfSequences;
    /** The tree. */
    private Tree tree;
    /** The number of model parameters. */
//	private  int numModelParameters;
    /** The number of transition categories. */
    private int numOfTransCategories;
    /** The likelihood calculation state. */
    private ModelLkState lkState;
    /** The external executor command line. */
    private String[] commandLine;

    /**
     * Gets the likelihood estimated value.
     * 
     * @return the likelihood estimated value
     */
    public double getLk() {
        return lkState.getLk();
    }

    /**
     * Checks if is computed.
     * 
     * @return true, if is computed
     */
    public boolean isComputed() {
        return (lkState instanceof ModelFilledLkState);
    }

    /**
     * Sets the likelihood estimated value.
     * 
     * @param lk the new likelihood estimated value
     */
    public void setLk(double lk) {
        lkState = lkState.setLk(lk);
    }

    /**
     * Gets the alpha estimated value.
     * 
     * @return the alpha estimated value
     */
    public double getAlpha() {
        return lkState.getAlpha();
    }

    /**
     * Sets the alpha estimated value.
     * 
     * @param alpha the new alpha estimated value
     */
    public void setAlpha(double alpha) {
        lkState = lkState.setAlpha(alpha);
    }

    /**
     * Gets the proportion of invariant sites.
     * 
     * @return the proportion of invariant sites
     */
    public double getInv() {
        return lkState.getInv();
    }

    /**
     * Sets the proportion of invariant sites.
     * 
     * @param inv the new proportion of invariant sites
     */
    public void setInv(double inv) {
        lkState = lkState.setInv(inv);
    }

    /**
     * Gets the alignment hashcode.
     * 
     * @return the alignment hashcode
     */
    public int getAlignment() {
        return alignment;
    }

    /**
     * Sets the alignment only if there wasn't set or it is a different instance of the same object.
     * 
     * @param alignment the new alignment
     */
    public void setAlignment(Alignment alignment) {
        if (this.alignment != 0 && this.alignment != alignment.toString().hashCode()) {
            throw new ProtTestInternalException("cannot set a different alignment");
        }
        this.alignment = alignment.toString().hashCode();
        this.numberOfSequences = alignment.getSequenceCount();
    }

    /**
     * Checks if an alignment matches the internal model alignment
     * 
     * @return alignment equality
     */
    public boolean checkAlignment(Alignment alignment) {
        if (alignment == null) {
            return false;
        }
        return (this.alignment == alignment.toString().hashCode() &&
                this.numberOfSequences == alignment.getSequenceCount());
    }

    /**
     * Gets the tree.
     * 
     * @return the tree
     */
    public Tree getTree() {
        return tree;
    }

    /**
     * Sets the tree.
     * 
     * @param tree the new tree
     */
    public void setTree(Tree tree) {
        this.tree = tree;
    }

    /**
     * Gets the command line.
     * 
     * @return the command line
     */
    public String[] getCommandLine() {
        return commandLine;
    }

    /**
     * Sets the command line.
     * 
     * @param commandLine the new command line
     */
    public void setCommandLine(String[] commandLine) {
        this.commandLine = commandLine;
    }

    /**
     * Gets the number of transition categories.
     * 
     * @return the number of transition categories
     */
    public int getNumberOfTransitionCategories() {
        return numOfTransCategories;
    }

    /**
     * Instantiates a new substitution model.
     * 
     * @param matrix the matrix name
     * @param distribution the distribution value
     * @param plusF consider observed frequencies
     * @param alignment the alignment
     * @param tree the tree
     * @param ncat the ncat
     */
    public Model(String matrix, int distribution, boolean plusF, Alignment alignment, Tree tree, int ncat) {

        if (distribution < 0 || distribution > 3) {
            throw new ProtTestInternalException("Distribution not supported " + distribution);
        }

        if (alignment == null) {
            throw new ProtTestInternalException("Null alignment");
        }

        this.matrix = matrix;
        this.distribution = distribution;
        this.plusF = plusF;
        this.alignment = alignment.toString().hashCode();
        this.numberOfSequences = alignment.getSequenceCount();
        this.tree = tree;
        this.lkState = new ModelEmptyLkState();

//		numBranches = 2*alignment.getSequenceCount() - 3;
        switch (distribution) {
            case DISTRIBUTION_UNIFORM:
                numOfTransCategories = 1;
                break;
            case DISTRIBUTION_INVARIABLE:
                numOfTransCategories = 1;
                break;
            case DISTRIBUTION_GAMMA:
                numOfTransCategories = ncat;
                break;
            case DISTRIBUTION_GAMMA_INV:
                numOfTransCategories = ncat;
                break;
            }

    }

    /**
     * Gets the number of parameters according with the distribution.
     * 
     * @return the distribution parameters according with the distribution
     */
    public int getDistributionParameters() {
        int value = -1;
        switch (distribution) {
            case DISTRIBUTION_UNIFORM:
                value = 0;
                break;
            case DISTRIBUTION_INVARIABLE:
                value = 1;
                break;
            case DISTRIBUTION_GAMMA:
                value = 1;
                break;
            case DISTRIBUTION_GAMMA_INV:
                value = 2;
                break;
            }
        return value;
    }

    /**
     * Gets the number of model parameters.
     * 
     * @return the number of model parameters
     */
    public abstract int getNumberOfModelParameters();

    /**
     * Gets the matrix name.
     * 
     * @return the matrix name
     */
    public String getMatrix() {
        return matrix;
    }

    /**
     * Gets the distribution.
     * 
     * @return the distribution
     */
    public int getDistribution() {
        return distribution;
    }

    /**
     * Checks if observed frequencies are considered.
     * 
     * @return true, if the model considers observed frequencies
     */
    public boolean isPlusF() {
        return plusF;
    }

    /* (non-Javadoc)
     * @see java.lang.Object#hashCode()
     */
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + distribution;
        result = prime * result + ((matrix == null) ? 0 : matrix.hashCode());
        result = prime * result + (isPlusF() ? 1231 : 1237);
        return result;
    }

    /* (non-Javadoc)
     * @see java.lang.Object#equals(java.lang.Object)
     */
    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        Model other = (Model) obj;
        if (distribution != other.distribution) {
            return false;
        }
        if (matrix == null) {
            if (other.matrix != null) {
                return false;
            }
        } else if (!matrix.equals(other.matrix)) {
            return false;
        }
        if (isPlusF() != other.isPlusF()) {
            return false;
        }
        return true;
    }

    /* (non-Javadoc)
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return "Model [distribution=" + distribution + ", matrix=" + matrix + ", plusF=" + isPlusF();
//		+ ", weight=" + weight + "]";
    }

    /**
     * Gets the complete model name.
     * 
     * @return the model name
     */
    public abstract String getModelName();

    /**
     * Gets the number of branches.
     * 
     * @return the number of branches
     */
    public int getNumBranches() {
        return 2 * numberOfSequences - 3;
    }

    /**
     * Checks if distribution is gamma.
     * 
     * @return true, if is gamma
     */
    public boolean isGamma() {
        return (distribution == DISTRIBUTION_GAMMA ||
                distribution == DISTRIBUTION_GAMMA_INV);
    }

    /**
     * Checks if a proportion of invariant sites are considered.
     * 
     * @return true, if considers a proportion of invariant sites
     */
    public boolean isInv() {
        return (distribution == DISTRIBUTION_INVARIABLE ||
                distribution == DISTRIBUTION_GAMMA_INV);
    }

    /**
     * Prints the model status report.
     */
    public void printReport() {

        println("Model................................ : " + getModelName());
        print("  Number of parameters............... : " + getNumberOfModelParameters());
        println(" (" + (getNumberOfModelParameters() - getNumBranches()) + " + " +
                getNumBranches() + " branch length estimates)");

        if (isComputed()) {
            if (isGamma()) {
                println("    gamma shape (" + getNumberOfTransitionCategories() + " rate categories).. = " + getAlpha());
            }
            if (isInv()) {
                println("    proportion of invariable sites... = " + getInv());
            }
            if (isPlusF()) {
                println("    aminoacid frequencies............ = observed (see above)");
            }
            print(" -lnL................................ = " + ProtTestFormattedOutput.getDecimalString((-1 * getLk()), 2));
            println("");

            verboseln("The tree:");
            verboseln("---------");
            StringWriter ascciiSw = new StringWriter();
            TreeUtils.report(getTree(), new PrintWriter(ascciiSw));
            ascciiSw.flush();
            verboseln(ascciiSw.toString());
            StringWriter newickSw = new StringWriter();
            verboseln("---------");
            TreeUtils.printNH(getTree(), new PrintWriter(newickSw));
            newickSw.flush();
            verboseln(newickSw.toString());
            verboseln("");
        }
        flush(Model.class);
    }

    private void print(String message) {
        info(message, Model.class);
    }

    private void println(String message) {
        infoln(message, Model.class);
    }

    private void verbose(String message) {
        finer(message, Model.class);
    }

    private void verboseln(String message) {
        finerln(message, Model.class);
    }
}
src/main/java/es/uvigo/darwin/prottest/global/options/ApplicationOptions.java
public class ApplicationOptions {

    /** Verbose mode on/off. */
    protected boolean debug = DEFAULT_DEBUG;
    /** The sample size. It is only useful if the sample size mode is custom */
    protected double sampleSize = 0.0;
    /** The alignment filename. */
    protected String align_file = null;
    /** The tree filename. */
    protected String tree_file = null;
    /** Logging Enabled/Disabled **/
    protected boolean logEnabled = true;
    /** The log filename. */
    protected String log_file = null;
    /** The alignment, according to the alignment input file. */
    protected Alignment alignment;
    /** The tree, according to the tree filename. */
    protected Tree tree;
    /** The number of categories. It is only useful if the distribution is gamma */
    public int ncat = 4;
    /** The optimization strategy mode. */
    public int strategyMode = DEFAULT_STRATEGY_MODE;
    /** The matrices of the models to optimize. */
    private List<String> matrices = new ArrayList<String>();	//{"JTT", "MtREV", "Dayhoff", "WAG"};
    /** The distributions of the models to optimize. */
    private List<Integer> distributions = new ArrayList<Integer>();	//{0,1,2,3};
    /** Hash table with the relationship between distribution name and distribution internal parameter. */
    private HashMap<String, Integer> distributionsHash;
    /** Boolean value to consider or not different kind of amino-acid frequencies. */
    private boolean plusF = false;
    // display options
    /** Display Newick tree on results. */
    private boolean displayNewickTree = DEFAULT_DISPLAY_NEWICK_TREE;
    /** Display ASCII tree on results. */
    private boolean displayASCIITree = DEFAULT_DISPLAY_ASCII_TREE;
    /** Display Consensus tree on results. */
    private boolean displayConsensusTree = DEFAULT_DISPLAY_CONSENSUS_TREE;
    /** Consensus threshold **/
    private double consensusThreshold;
    /** Display a 7-framework comparison. */
    private boolean compareAll = DEFAULT_COMPARE_ALL;
    /** Write a log file. */
    public boolean writeLog = true;
    /** Criterion to sort the models. */
    private boolean doAIC,doBIC,doAICc,doDT;
    /** Tree search operation */
    private String treeSearchOperation = TREE_SEARCH_NNI;

    @Override
    public boolean equals(Object obj) {
System.out.println("Check equals");
        if (this == obj) return true;
        if (obj == null) return false;
        if (getClass() != obj.getClass()) return false;
        ApplicationOptions other = (ApplicationOptions) obj;
        if (align_file == null || other.align_file == null) {
            return false;
        } else if (!align_file.equals(other.align_file)) {
            return false;
        }
        if (strategyMode != other.strategyMode) return false;
        if (ncat != other.ncat) return false;
        if (tree_file != other.tree_file &&
              (tree_file == null || !tree_file.equals(other.tree_file)))
            return false;
        if (!alignment.equals(other.alignment)) return false;
        if (!tree.equals(other.tree)) return false;
        return true;
    }

    /**
     * Sets the number of categories.
     * 
     * @param ncat the new number of categories
     */
    public void setNumberOfCategories(int ncat) {
        this.ncat = ncat;
    }

    /**
     * Gets the value of plusF attribute.
     * 
     * @return the plusF value
     */
    public boolean isPlusF() {
        return plusF;
    }

    /**
     * Sets the empirical amino-acid frequencies usage.
     * 
     * @param plusF the new value for consider empirical frequencies
     */
    public void setPlusF(boolean plusF) {
        this.plusF = plusF;
    }

    /**
     * Checks if is required to display newick tree.
     * 
     * @return true, if is required to display newick tree
     */
    public boolean isDisplayNewickTree() {
        return displayNewickTree;
    }

    /**
     * Checks if is required to display ascii tree.
     * 
     * @return true, if is required to display ascii tree
     */
    public boolean isDisplayASCIITree() {
        return displayASCIITree;
    }

    /**
     * Checks whether PhyML logging is enabled
     * 
     * @return true, if PhyML logging is enabled
     */
    public boolean isLogEnabled() {
    	return logEnabled;
    }
    
    /**
     * Checks if is required to display consensus tree.
     * 
     * @return true, if is required to display consensus tree
     */
    public boolean isDisplayConsensusTree() {
        return displayConsensusTree;
    }

    /**
     * Gets the threshold to create consensus tree.
     * 
     * @return the threshold
     */
    public double getConsensusThreshold() {
        return consensusThreshold;
    }

    /**
     * Check if should compute the AIC criterion
     * 
     * @return true, if AIC should be computed
     */
    public boolean isAIC() {
        return doAIC;
    }
    
    /**
     * Check if should compute the BIC criterion
     * 
     * @return true, if BIC should be computed
     */
    public boolean isBIC() {
        return doBIC;
    }
    
    /**
     * Check if should compute the AICc criterion
     * 
     * @return true, if AICc should be computed
     */
    public boolean isAICc() {
        return doAICc;
    }
    
    /**
     * Check if should compute the DT criterion
     * 
     * @return true, if DT should be computed
     */
    public boolean isDT() {
        return doDT;
    }
    public String getTreeSearchOperation() {
		return treeSearchOperation;
	}

	public void setTreeSearchOperation(String treeSearchOperation) {
		this.treeSearchOperation = treeSearchOperation;
	}

	/**
     * Sets the alignment filename without checking
     * 
     * @param alignFile the alignment filename
     */
    public void setAlignmentFilename(String alignFile) {
        this.align_file = alignFile;
    }

    /**
     * Sets the alignment file
     * 
     * @param alignFile the alignment filename
     * 
     * @return true, if successful
     * 
     * @throws AlignmentParseException Signals that the alignment filename is not correct.
     * @throws IOException Signals that an I/O exception has occurred.
     */
    public boolean setAlignment(String alignFile)
            throws AlignmentParseException,
            IOException {

        this.align_file = alignFile;

        StringWriter sw = new StringWriter();
        setAlignment(AlignmentReader.readAlignment(new PrintWriter(sw), alignFile, debug));
        sw.flush();
        println(sw.toString());

        return alignFile != null;
    }

    /**
     * Gets the alignment.
     * 
     * @return the alignment
     */
    public Alignment getAlignment() {
        return alignment;
    }

    /**
     * Gets the tree.
     * 
     * @return the tree
     */
    public Tree getTree() {
        return tree;
    }

    /**
     * Sets the alignment.
     * 
     * @param alignment the new alignment
     */
    public void setAlignment(Alignment alignment) {
        this.alignment = alignment;
    }

    /**
     * Sets the tree.
     * 
     * @param tree the new tree
     */
    public synchronized void setTree(Tree tree) {
        this.tree = tree;
    }

    /**
     * Checks if is debug (verbose mode).
     * 
     * @return true, if is debug
     */
    public boolean isDebug() {
        return debug;
    }

    /**
     * Sets the debug mode value.
     * 
     * @param debug the new debug mode value
     */
    public void setDebug(boolean debug) {
        this.debug = debug;
    }

    /**
     * Checks if a 7-framework comparison should be displayed.
     * 
     * @return true, if a 7-framework comparison should be displayed
     */
    public boolean isAll() {
        return compareAll;
    }

    /**
     * Sets the 7-framework comparison display value.
     * 
     * @param all the new 7-framework comparison display value
     */
    public void setAll(boolean all) {
        this.compareAll = all;
    }

    /**
     * Gets the tree filename.
     * 
     * @return the tree filename
     */
    public String getTreeFile() {
        return tree_file;
    }

    /**
     * Sets the tree filename.
     * 
     * @param treeFile the new tree filename
     * 
     * @throws TreeFormatException Signals there was an error parsing the tree file
     */
    public void setTreeFile(String treeFile)
            throws TreeFormatException, FileNotFoundException, IOException {
        if (treeFile != null) {
            this.tree_file = treeFile;

            StringWriter sw = new StringWriter();
            setTree(AlignmentReader.readTree(new PrintWriter(sw), treeFile, debug));
            sw.flush();
            println(sw.toString());
        }
    }

    /**
     * Gets the number of model matrices.
     * 
     * @return the number of model matrices
     */
    public int getNumberOfMatrices() {
        return matrices.size();
    }

    /**
     * Gets the model matrices.
     * 
     * @return the model matrices
     */
    public List<String> getMatrices() {
        List<String> matricesClone = new ArrayList<String>(matrices);
        return matricesClone;
    }

    /**
     * Sets the model matrices.
     * 
     * @param newMatrices the new model matrices
     */
    public void setMatrices(List<String> newMatrices) {
        matrices = newMatrices;
    }

    /**
     * Gets the matrix at a certain position.
     * 
     * @param pos the position of the matrix
     * 
     * @return the matrix at the required position
     */
    public String getMatrixAt(int pos) {
        return matrices.get(pos);
    }

    /**
     * Check if one matrix exists in the model matrices set of the application.
     * 
     * @param matrix the matrix to check out
     * 
     * @return true, if the set of matrices contains the model matrix
     */
    public boolean existsMatrix(String matrix) {
        return matrices.contains(matrix);
    }

    /**
     * Removes one matrix of the set.
     * 
     * @param matrix the matrix to remove
     * 
     * @return true, if the matrix was successfully removed
     */
    public boolean removeMatrix(String matrix) {
        return matrices.remove(matrix);
    }

    /**
     * Adds one matrix to the set.
     * 
     * @param matrix the matrix to add
     */
    public void addMatrix(String matrix) {
        if (!matrices.contains(matrix)) {
            matrices.add(matrix);
        }
    }

    /**
     * Gets the distributions.
     * 
     * @return the distributions
     */
    public List<Integer> getDistributions() {
        List<Integer> distributionsClone = new ArrayList<Integer>(distributions);
        return distributionsClone;
    }

    /**
     * Sets the distributions.
     * 
     * @param newDistributions the new distributions
     */
    public void setDistributions(List<Integer> newDistributions) {
        distributions = newDistributions;
    }

    /**
     * Gets the number of distributions.
     * 
     * @return the number of distributions
     */
    public int getNumberOfDistributions() {
        return distributions.size();
    }

    /**
     * Gets the distribution at a certain position of the list.
     * 
     * @param pos the position of the distribution to get
     * 
     * @return the distribution at the required position
     */
    public int getDistributionAt(int pos) {
        return distributions.get(pos);
    }

    /**
     * Gets the internal value of a distribution.
     * 
     * @param distribution the distribution name
     * 
     * @return the distribution value
     */
    public int getDistribution(String distribution) {
        return distributionsHash.get(distribution);
    }

    /**
     * Check if exist one distribution in the distribution set.
     * 
     * @param distribution the distribution to check out
     * 
     * @return true, if the set of distribution contains the distribution
     */
    public boolean existsDistribution(String distribution) {
        return distributionsHash.containsKey(distribution);
    }

    /**
     * Adds one distribution to the distributions set.
     * 
     * @param distribution the distribution to add
     */
    public void addDistribution(String distribution) {
        Integer distributionValue = distributionsHash.get(distribution);
        if (!distributions.contains(distributionValue)) {
            distributions.add(distributionValue);
        }
    }

    /**
     * Removes the distribution from the distributions set.
     * 
     * @param distribution the distribution to remove
     * 
     * @return true, if the distribution was successfully removed
     */
    public boolean removeDistribution(String distribution) {
        Integer distributionValue = distributionsHash.get(distribution);
        return distributions.remove(distributionValue);
    }

    /**
     * Gets the sample size.
     * 
     * @return the sample size
     */
    public double getSampleSize() {
        return alignment.getSiteCount();
    }

    /**
     * Sets the sample size.
     * 
     * @param sampleSize the new sample size
     */
    public void setSampleSize(double sampleSize) {
        this.sampleSize = sampleSize;
    }

    /**
     * Sets the strategy mode.
     * 
     * @param strategyMode the new strategy mode
     */
    public void setStrategyMode(int strategyMode) {
        if (strategyMode == OPTIMIZE_USER && getTreeFile() == null) {
            throw new ProtTestInternalException("User defined topology must be set");
        }
        this.strategyMode = strategyMode;
    }

    /**
     * Instantiates a new application options.
     */
    public ApplicationOptions() {
        initLists();
    }

    /**
     * Fill in attribute values with the arguments.
     * 
     * @param arguments the arguments too fill the application options instance in
     */
    public void fillIn(ProtTestArgumentParser arguments) {

    	if(arguments.exists(ProtTestArgumentParser.PARAM_HELP))
        {
        	usage();
        	System.exit(0);
        }
    	
        if (arguments.exists(ProtTestArgumentParser.PARAM_ALIGNMENT_FILE)) {
            try {
                setAlignment(arguments.getValue(ProtTestArgumentParser.PARAM_ALIGNMENT_FILE));
            } catch (AlignmentParseException e) {
                throw new IllegalArgumentException(e.getMessage());
            } catch (IOException e) {
                throw new IllegalArgumentException(e.getMessage());
            }
        } else {
            throw new IllegalArgumentException("Required input file argument -i");
        }
        if (arguments.exists(ProtTestArgumentParser.PARAM_TREE_FILE)) {
            try {
                setTreeFile(arguments.getValue(ProtTestArgumentParser.PARAM_TREE_FILE));
            } catch (FileNotFoundException ex) {
                throw new IllegalArgumentException(ex.getMessage());
            } catch (IOException ex) {
                throw new IllegalArgumentException(ex.getMessage());
            }
        }
        if (arguments.exists(ProtTestArgumentParser.PARAM_OUTPUT_FILE)) {
            //open the PrintWriter with file
            try {
                FileOutputStream fo = new FileOutputStream(arguments.getValue(ProtTestArgumentParser.PARAM_OUTPUT_FILE));
                ProtTestLogger.getDefaultLogger().addHandler(fo, Level.INFO);
            } catch (FileNotFoundException fnfe) {
                throw new ProtTestInternalException(fnfe.getMessage());
            }
        }

        if (arguments.exists(ProtTestArgumentParser.PARAM_LOGGING)) {
            logEnabled = arguments.getValue(ProtTestArgumentParser.PARAM_LOGGING).equalsIgnoreCase("enabled");
        }
            
        if (arguments.exists(ProtTestArgumentParser.PARAM_TREE_SEARCH_OP)) {
            setTreeSearchOperation(arguments.getValue(ProtTestArgumentParser.PARAM_TREE_SEARCH_OP));
        }
        setDebug(arguments.isSet(ProtTestArgumentParser.PARAM_VERBOSE));
        displayASCIITree = arguments.isSet(ProtTestArgumentParser.PARAM_DISPLAY_ASCII_TREE);
        displayNewickTree = arguments.isSet(ProtTestArgumentParser.PARAM_DISPLAY_NEWICK_TREE);
        displayConsensusTree = arguments.isSet(ProtTestArgumentParser.PARAM_DISPLAY_CONSENSUS_TREE);
        if (arguments.exists(ProtTestArgumentParser.PARAM_DISPLAY_CONSENSUS_TREE)) {
            displayConsensusTree = true;
            try {
                consensusThreshold = Double.parseDouble(arguments.getValue(ProtTestArgumentParser.PARAM_DISPLAY_CONSENSUS_TREE));
            } catch  (NumberFormatException e) {
                throw new IllegalArgumentException("Consensus threshold must be a number between 0.5 (majority rule) and 1 (strict). You used " + arguments.getValue(ProtTestArgumentParser.PARAM_DISPLAY_CONSENSUS_TREE));
            }
            if (consensusThreshold < 0.5 || consensusThreshold > 1.0) {
                throw new IllegalArgumentException("Consensus threshold must be a number between 0.5 (majority rule) and 1 (strict). You used " + consensusThreshold);
            }
        }
        setAll(arguments.isSet(ProtTestArgumentParser.PARAM_ALL_FRAMEWORK_COMPARISON));
        if (arguments.exists(ProtTestArgumentParser.PARAM_OPTIMIZATION_STRATEGY)) {
            if (getTreeFile() != null) {
                strategyMode = OPTIMIZE_USER;
            } else {
                strategyMode = Integer.parseInt(arguments.getValue(ProtTestArgumentParser.PARAM_OPTIMIZATION_STRATEGY));
                // Check
                if (strategyMode == OPTIMIZE_USER &&
                        getTreeFile() == null) {
                    throw new IllegalArgumentException("User defined topology must be set");
                }
            }
        }
        if (arguments.exists(ProtTestArgumentParser.PARAM_DO_AIC)) {
            doAIC = arguments.isSet(ProtTestArgumentParser.PARAM_DO_AIC);
        }
        if (arguments.exists(ProtTestArgumentParser.PARAM_DO_BIC)) {
            doBIC = arguments.isSet(ProtTestArgumentParser.PARAM_DO_BIC);
        }
        if (arguments.exists(ProtTestArgumentParser.PARAM_DO_AICC)) {
            doAICc = arguments.isSet(ProtTestArgumentParser.PARAM_DO_AICC);
        }
        if (arguments.exists(ProtTestArgumentParser.PARAM_DO_DT)) {
            doDT = arguments.isSet(ProtTestArgumentParser.PARAM_DO_DT);
        }

        setSampleSize(ProtTestAlignment.calculateSampleSize(alignment));

        boolean existsMatrix = false;
        for (String matrix : arguments.getMatrices()) {
            if (arguments.isSet(matrix)) {
                addMatrix(matrix);
                existsMatrix = true;
            } else {
                removeMatrix(matrix);
            }
        }
        if (!existsMatrix) {
            throw new IllegalArgumentException("You must specify at least one model matrix");
        }

        // distributions
        addDistribution("Uniform");
        boolean plusG = false, plusIG = false;
        if (arguments.isSet(ProtTestArgumentParser.PARAM_PLUSI)) {
            addDistribution("+I");
        }
        if (arguments.exists(ProtTestArgumentParser.PARAM_PLUSG)) {
            addDistribution("+G");
            plusG = true;
        }
        if (arguments.exists(ProtTestArgumentParser.PARAM_PLUSIG)) {
            addDistribution("+I+G");
            plusIG = true;
        }
        if (plusG || plusIG) {
            try {
                setNumberOfCategories(Integer.parseInt(arguments.getValue(ProtTestArgumentParser.PARAM_NCAT)));
            } catch (NumberFormatException e) {
                throw new IllegalArgumentException("Invalid number of categories " + arguments.getValue(ProtTestArgumentParser.PARAM_PLUSG));
            }
        }
        if (arguments.isSet(ProtTestArgumentParser.PARAM_PLUSF)) {
            setPlusF(true);
        }
    }

    /**
     * Inits the Lists.
     */
    private void initLists() {
//        for(int i=0; i<ALL_MATRICES.length; i++) {
//            matrixes.addElement(ALL_MATRICES[i]);
//        }
        distributionsHash = new HashMap<String, Integer>();
        distributionsHash.put("Uniform", new Integer(Model.DISTRIBUTION_UNIFORM));
        distributionsHash.put("+I", new Integer(Model.DISTRIBUTION_INVARIABLE));
        distributionsHash.put("+G", new Integer(Model.DISTRIBUTION_GAMMA));
        distributionsHash.put("+I+G", new Integer(Model.DISTRIBUTION_GAMMA_INV));
    }

    /**
     * Display the command line usage of the application.
     */
    public static void usage() {
        println("-------------------------------------------------------------------------------------------------");
        println("Basic usage: ");
        println(" - Sequential version: ");
        println("        java -jar prottest-"+ProtTest.versionNumber+".jar -i align_file [OPTIONS]");
        println("        ./prottest3 -i align_file [OPTIONS]");
        println(" - Parallel version: ");
        println("        mpjrun.sh -wdir $PWD/ -np [NUM_PROCS] -jar prottest-"+ProtTest.versionNumber+".jar -i align_file [OPTIONS]");
        println("OPTIONS:");
        println(" -i alignment_filename");
        println("            Alignment input file (required)");
        println(" -t tree_filename");
        println("            Tree file       (optional) [default: NJ tree]");
        println(" -o output_filename");
        println("            Output file     (optional) [default: standard output]");
        println(" -log enabled/disabled");
        println("            Enables / Disables PhyML logging into log directory (see prottest.properties)");
        println(" -[matrix]");
        print(  "            Include matrix (Amino-acid) = "  );
        int count = 0;
        List<String> matrices = ProtTestFactory.getInstance().getApplicationGlobals().getSupportedMatrices();
        for (String matrix : matrices) {
            print(matrix + " ");
            if (count == (7)) {
                println("");
                print("	                                  ");
            }
            count++;
        }
        println("");
        println("                If you don't specify any matrix, all matrices displayed above will");
        println("                be included.");
        println(" -I");
        println("            Include models with a proportion of invariable sites");
        println(" -G");
        println("            Include models with rate variation among sites and number of categories");
        println(" -IG");
        println("            include models with both +I and +G properties");
        println(" -all-distributions");
        println("            Include models with rate variation among sites, number of categories and both");
        println(" -ncat number_of_categories");
        println("            Define number of categories for +G and +I+G models [default: " + DEFAULT_NCAT + "]");
        println(" -F");
        println("            Include models with empirical frequency estimation ");
        println(" -AIC");
        println("            Display models sorted by Akaike Information Criterion (AIC)");
        println(" -BIC");
        println("            Display models sorted by Bayesian Information Criterion (BIC)");
        println(" -AICC");
        println("            Display models sorted by Corrected Akaike Information Criterion (AICc)");
        println(" -DT");
        println("            Display models sorted by Decision Theory Criterion");
        println(" -all");
        println("            Displays a 7-framework comparison table");
        println(" -S optimization_strategy");
        println("            Optimization strategy mode: [default: " + DEFAULT_STRATEGY_MODE + "]");
        for (int i = 0; i < OPTIMIZE_NAMES.length; i++) {
            println("             		" + i + ": " + OPTIMIZE_NAMES[i]);
        }
        println(" -s moves");
        println("            Tree search operation for ML search: ");
        println("            NNI (fastest), SPR (slowest), BEST (best of NNI and SPR) [default: NNI]");
        println(" -t1      				");
        println("            Display best-model's newick tree [default: " + DEFAULT_DISPLAY_NEWICK_TREE + "]");
        println(" -t2      				");
        println("            Display best-model's ASCII tree  [default: " + DEFAULT_DISPLAY_ASCII_TREE + "]");
        println(" -tc consensus_threshold ");
        println("            Display consensus tree with the specified threshold, between 0.5 and 1.0");
        println("            [0.5 = majority rule consensus ; 1.0 = strict consensus]");
        println(" -threads number_or_threads			");
        println("            Number of threads requested to compute (only if MPJ is not used) [default: " +
                DEFAULT_THREADS + "]");
        println(" -verbose");
        println("            Verbose mode [default: " + DEFAULT_DEBUG + "]");
        println("-------------------------------------------------------------------------------------------------");
        println("Example: ");
        println("- Sequential version:");
        println("    java -jar prottest-"+ProtTest.versionNumber+".jar -i align_file -t tree_file -S 0 -all-distributions -F -AIC -BIC -tc 0.5 > output");
        println("- Parallel version:");
        println("    mpjrun.sh -wdir $PWD/ -np 2 -jar prottest-"+ProtTest.versionNumber+".jar -i align_file -t tree_file -S 0 -all-distributions -F -AIC -BIC -tc 0.5");
    }

    public void reportModelOptimization() {
        String tmp;
        tmp = "BioNJ";
        if (tree_file != null) {
            tmp = tree_file;
        }
        println("");
        println("ProtTest options");
        println("----------------");
        println("  Alignment file........... : " + align_file);
        println("  Tree..................... : " + tmp);
        println("  StrategyMode............. : " + OPTIMIZE_NAMES[strategyMode]);

        println("  Candidate models......... : ");
        print("    Matrices............... : ");
        for (int i = 0; i < matrices.size(); i++) {
            print(matrices.get(i) + " ");
        }
        println("");
        print("    Distributions.......... : ");
        for (String dist : distributionsHash.keySet()) {
            Integer value = (Integer) distributionsHash.get(dist);
            if (distributions.contains(value)) {
                print(dist + " ");
            }
        }
        println("");
        println("    Observed frequencies... : " + plusF);
        println("");
    }

    /**
     * Report the current options.
     */
    public void reportComplete() {
        reportModelOptimization();

        println("  Statistical framework");
        println("    Sort models according to....: ");
        StringBuilder sb = new StringBuilder();
        if (doAIC) {
            sb.append(" AIC ");
        }
        if (doBIC) {
            sb.append(" BIC ");
        }
        if (doAICc) {
            sb.append(" AICc ");
        }
        if (doDT) {
            sb.append(" DT ");
        }
        if (!(doAIC|doBIC|doAICc|doDT))
            sb.append(" lnL");
        println("      " + sb.toString());
        println("    Sample size.................: " + sampleSize);
        println("  Other options:");
        println("    Display best tree in ASCII..: " + displayASCIITree);
        println("    Display best tree in Newick.: " + displayNewickTree);
        println("    Display consensus tree......: " + displayConsensusTree);
        if (displayConsensusTree) {
            println("    Consensus threshold.........: " + consensusThreshold);
        }
        println("    Verbose.....................: " + debug);

        println("");
    }

    private static void print(String message) {
        info(message, ApplicationOptions.class);
    }

    private static void println(String message) {
        infoln(message, ApplicationOptions.class);
    }
}
src/main/java/es/uvigo/darwin/prottest/util/exception/ProtTestInternalException.java
public class ProtTestInternalException extends RuntimeException {

	/** The Constant serialVersionUID. */
	private static final long serialVersionUID = 20090728L;

	/**
	 * Instantiates a new prot test internal exception.
	 */
	public ProtTestInternalException() {}
	
	/**
	 * Instantiates a new prot test internal exception.
	 * 
	 * @param description the description
	 */
	public ProtTestInternalException(String description) {
		super(description);
	}

}
src/main/java/es/uvigo/darwin/prottest/util/collection/SingleModelCollection.java
public class SingleModelCollection extends ModelCollection {

	/** The Constant serialVersionUID. */
	private static final long serialVersionUID = 20090804L;

	/**
	 * Instantiates a new single model collection.
	 */
	public SingleModelCollection(Alignment alignment) {
		super(alignment);
	}
	
	/**
	 * Instantiates a new single model collection.
	 * 
	 * @param models the models
	 */
	public SingleModelCollection(Model[] models, Alignment alignment) {
		super(models, alignment);
	}

        /**
	 * Instantiates a new single model collection.
	 *
	 * @param models the models
	 */
	public SingleModelCollection(ModelCollection models) {
		super(models);
	}

}
src/main/java/es/uvigo/darwin/prottest/util/factory/ProtTestFactory.java
public class ProtTestFactory {

    /** The Constant PROTEIC. */
    public static final int PROTEIC = 1;
    /** The Constant NUCLEIC. */
    public static final int NUCLEIC = 2;
    /** The Constant MAX_SORT. */
    private static final int MAX_SORT = 2;
    /** The instance. */
    private static ProtTestFactory instance;
    /** The sort. */
    private int sort;
    /** Unique log handler. */
    private Handler logHandler;

    /**
     * Instantiates a new prot test factory.
     * 
     * @param sort the sort
     * 
     * @throws IllegalArgumentException the illegal argument exception
     */
    private ProtTestFactory(int sort)
            throws IllegalArgumentException {
        if (sort <= 0 || sort > MAX_SORT) {
            throw new IllegalArgumentException(
                    "Cannot create factory (unexistent sort : " + sort + ")");
        }
        this.sort = sort;
    }

    /**
     * Initialize.
     * 
     * @param args the args
     * 
     * @return the string[]
     */
    public static String[] initialize(String[] args) {
        if (instance == null) {
            int sort;
            List<String> argList = new ArrayList<String>(Arrays.asList(args));
            int index = argList.indexOf(ProtTestArgumentParser.ARG_TOKEN + ProtTestArgumentParser.PARAM_DATA_TYPE);
            if (index < 0) //set default
            {
                sort = PROTEIC;
            } else {
                String value = argList.get(index + 1);
                argList.remove(index + 1);
                argList.remove(index);
                args = argList.toArray(new String[0]);
                if (value.equals(ProtTestArgumentParser.DATA_TYPE_AMINOACID)) {
                    sort = PROTEIC;
                } else if (value.equals(ProtTestArgumentParser.DATA_TYPE_NUCLEOTIDE)) {
                    sort = NUCLEIC;
                } else {
                    throw new IllegalArgumentException("Invalid data type " + value);
                }
            }
            instance = new ProtTestFactory(sort);
            return args;
        } else {
            throw new ProtTestInternalException("ProtTestFactory was already initialized");
        }
    }

    /**
     * Gets the single instance of ProtTestFactory.
     * 
     * @return single instance of ProtTestFactory
     */
    public static ProtTestFactory getInstance() {
        if (instance == null) {
            initialize(new String[0]);
        }
//			throw new ProtTestInternalException("ProtTestFactory should be initialized");

        return instance;
    }

    /**
     * Creates a new ProtTest object.
     * 
     * @param args the args
     * @param options the options
     * 
     * @return the prot test argument parser
     */
    public ProtTestArgumentParser createProtTestArgumentParser(String[] args, ApplicationOptions options) 
        throws IllegalArgumentException{
        ProtTestArgumentParser mtap = null;
        switch (sort) {
            case PROTEIC:
                mtap = new AminoAcidArgumentParser(args, options);
                break;
            case NUCLEIC:
                throw new ProtTestInternalException("Unsupported operation: nucleic data");
        }
        return mtap;
    }

    /**
     * Gets the application globals.
     * 
     * @return the application globals
     */
    public ApplicationGlobals getApplicationGlobals() {
        ApplicationGlobals ap = null;
        String analyzer = APPLICATION_PROPERTIES.getProperty("analyzer");
        switch (sort) {
            case PROTEIC:
                if (analyzer.equals(ANALYZER_RAXML)) {
                    ap = new RaxmlAminoAcidApplicationGlobals();
                } else if (analyzer.equals(ANALYZER_PHYML)) {
                    ap = new AminoAcidApplicationGlobals();
                } else {
                    throw new ProtTestInternalException("Analyzer " + analyzer + " not supported by RunEstimator. Check your prottest.properties file");
                }
                break;
            case NUCLEIC:
                throw new ProtTestInternalException("Unsupported operation: nucleic data");
        }
        return ap;
    }

    /**
     * Creates a new ProtTest object.
     * 
     * @param matrix the matrix
     * @param distribution the distribution
     * @param modelProperties the model APPLICATION_PROPERTIES
     * 
     * @return the model
     */
    public Model createModel(String matrix, int distribution, Properties modelProperties,
            Alignment alignment, Tree tree, int ncat) {
        Model model = null;
        boolean plusF;
        if (modelProperties != null) {
            plusF = Boolean.parseBoolean(
                    modelProperties.getProperty(
                    AminoAcidModel.PROP_PLUS_F, "false"));
        } else {
            plusF = false;
        }
        switch (sort) {
            case PROTEIC:
                model = new AminoAcidModel(matrix, distribution, plusF,
                        alignment, tree, ncat);
                break;
            case NUCLEIC:
                throw new ProtTestInternalException("Unsupported operation: nucleic data");
        }
        return model;
    }

    /**
     * Creates a new ProtTest object.
     * 
     * @param options the options
     * @param model the model
     * 
     * @return the run estimator
     */
    public RunEstimator createRunEstimator(ApplicationOptions options, Model model) {
        return createRunEstimator(options, model, 1);
    }

    /**
     * Creates a new ProtTest object.
     * 
     * @param options the options
     * @param model the model
     * 
     * @return the run estimator
     */
    public RunEstimator createRunEstimator(ApplicationOptions options, Model model, int numberOfThreads) {
        RunEstimator runEstimator = null;
        String analyzer = APPLICATION_PROPERTIES.getProperty("analyzer");
        if (analyzer.equals(ANALYZER_PHYML)) {
            switch (sort) {
                case PROTEIC:
                    runEstimator = new PhyMLv3AminoAcidRunEstimator(options, model, numberOfThreads);
                    break;
                case NUCLEIC:
                    throw new ProtTestInternalException("Unsupported operation: nucleic data");
            }
        } else if (analyzer.equals(ANALYZER_RAXML)) {
            switch (sort) {
                case PROTEIC:
                    runEstimator = new RaxMLAminoAcidRunEstimator(options, model);
                    break;
                case NUCLEIC:
                    throw new ProtTestInternalException("Unsupported operation: nucleic data");
            }
        } else {
            throw new ProtTestInternalException("Analyzer " + analyzer + " not supported by RunEstimator");
        }
        return runEstimator;
    }

    public Handler createLogHandler() throws IOException {
        //   Log level is configurable:
        //   'info'    Only general information messages are logged (default)
        //   'fine'    General debug information is also logged
        //   'finer'   More complex debug information is logged
        //   'finest'  All activity is tracked

        if (logHandler == null) {

            String[] supportedLevels = {"INFO", "FINE", "FINER", "FINEST"};

            String logDirName = APPLICATION_PROPERTIES.getProperty("log_dir");
            String level = APPLICATION_PROPERTIES.getProperty("log_level", "info").toUpperCase();
            boolean supported = false;
            for (String testLevel : supportedLevels) {
                supported |= testLevel.equals(level);
            }

            if (logDirName != null && supported) {
                File logDir = new File(logDirName);
                if (!logDir.isAbsolute()) {
                	logDir = new File(ApplicationGlobals.PATH + File.separator + logDir);
                }
                if (!logDir.exists()) {
                    logDir.mkdirs();
                }

		SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
                //File logFile = new File(logDir.getAbsolutePath() + File.separator + "prottest3_" +
		//	sdf.format(Calendar.getInstance().getTime()));
		File logFile = File.createTempFile("prottest3_"+sdf.format(Calendar.getInstance().getTime())+"_",
			 ".log", logDir);
                FileOutputStream fos = new FileOutputStream(logFile);
                logHandler = new StreamHandler(fos, new ProtTestLogFormatter());
                logHandler.setLevel(Level.parse(level));
            }

        }
        return logHandler;

    }

    /**
     * Creates a new ProtTest object.
     *
     * @return the prints the framework
     */
    public PrintFramework createPrintFramework() {
        PrintFramework pf = null;

        switch (sort) {
            case PROTEIC:
                pf = new AminoAcidPrintFramework();
                break;
            case NUCLEIC:
                throw new ProtTestInternalException("Unsupported operation: nucleic data");
        }

        return pf;
    }
}
src/main/java/es/uvigo/darwin/prottest/exe/util/TemporaryFileManager.java
public class TemporaryFileManager {

	/** The Constant BASE_ALIGNMENT_FILE_NAME. */
	private static final String BASE_ALIGNMENT_FILE_NAME = "prottest_alignment_";
	
	/** The Constant BASE_TREE_FILE_NAME. */
	private static final String BASE_TREE_FILE_NAME = "prottest_tree_";
	
	/** The Constant BASE_LOG_FILE_NAME. */
	private static final String BASE_LOG_FILE_NAME = "prottest_";
	
	/** The unique instance. */
	private static TemporaryFileManager instance;
	
	/** The alignment temporary files. */
	private File[] alignmentTempFile;
	
	/** The log temporary files. */
	private File[] logTempFile;
	
	/** The tree temporary file. */
	private File treeTempFile;
	
	/** The thread pool synchronizer. */
	private ThreadPoolSynchronizer synchronizer;
	
	/** The alignment. */
	private Alignment alignment;
	
	/** The tree. */
	private Tree tree;
	
	/**
	 * Gets the alignment filename.
	 * 
	 * @param thread the thread
	 * 
	 * @return the alignment filename
	 */
	public String getAlignmentFilename(Thread thread) {
		int threadId = synchronizer.getThreadId(thread);
		return alignmentTempFile[threadId].getAbsolutePath();
	}
	
	/**
	 * Gets the log filename.
	 * 
	 * @param thread the thread
	 * 
	 * @return the log filename
	 */
	public String getLogFilename(Thread thread) {
		int threadId = synchronizer.getThreadId(thread);
		return logTempFile[threadId].getAbsolutePath();
	}
	
	/**
	 * Gets the tree filename for a thread.
	 * 
	 * @param thread the thread
	 * 
	 * @return the tree filename
	 */
	public String getTreeFilename(Thread thread) {
		if (tree == null)
			return null;
		return treeTempFile.getAbsolutePath();
	}
	
	/**
	 * Checks if is synchronized.
	 * 
	 * @return true, if is synchronized
	 */
	public static boolean isSynchronized() {
		return instance != null;
	}
	
	/**
	 * Sets the input tree. For each manager sync, the input tree should be set just 
	 * once, or all the trees must be the same (i.e., this method can be used to
	 * check consistency between processes) 
	 * 
	 * @param tree the new tree
	 * 
	 * @throws ProtTestInternalException when attempting to set different trees
	 */
	public void setTree(Tree tree) {
		if (this.tree == null) {
			convertTree(tree, treeTempFile);
			this.tree = tree;
		} else {
			if (!tree.equals(this.tree)) {
				throw new ProtTestInternalException("Attempting to set different trees");
			}
		}
			
	}
	
	/**
	 * Gets the input tree.
	 * 
	 * @return the tree
	 */
	public Tree getTree() {
		return tree;
	}
	
	/**
	 * Gets the input alignment.
	 * 
	 * @return the alignment
	 */
	public Alignment getAlignment() {
		return alignment;
	}
	
	/**
	 * Instantiates a new temporary file manager.
	 * 
	 * @param path the path
	 * @param alignment the alignment
	 * @param tree the tree
	 * @param poolSize the pool size
	 */
	private TemporaryFileManager(Alignment alignment, Tree tree, int poolSize) {
		
		alignmentTempFile = new File[poolSize];
		logTempFile = new File[poolSize];
		this.alignment = alignment;
		this.tree = tree;

		try {
		    for (int i = 0; i < poolSize; i++) {
		    	alignmentTempFile[i] = File.createTempFile(BASE_ALIGNMENT_FILE_NAME, null);
		    	alignmentTempFile[i].deleteOnExit();
		    	logTempFile[i] = File.createTempFile(BASE_LOG_FILE_NAME, null);
		    	logTempFile[i].deleteOnExit();
		        convertAlignment(alignment, alignmentTempFile[i]);
	        }
		    treeTempFile = File.createTempFile(BASE_TREE_FILE_NAME, null);
		    treeTempFile.deleteOnExit();
		    if (tree != null)
	        	convertTree(tree, treeTempFile);
		} catch (IOException e) {
			throw new ProtTestInternalException("Cannot create temporary files");
		}
	    ThreadPoolSynchronizer.synchronize(poolSize);
	    synchronizer = ThreadPoolSynchronizer.getInstance();
	}
	
	/**
	 * Gets the single instance of TemporaryFileManager.
	 * 
	 * @return single instance of TemporaryFileManager
	 */
	public static TemporaryFileManager getInstance() {
		if (instance == null) {
			throw new ProtTestInternalException("TemporaryFileManager out of sync");
		}
		return instance;
	}
	
	/**
	 * Synchronizes this manager.
	 * 
	 * @param alignment the input alignment
	 * @param tree the input tree
	 * @param poolSize the pool size
	 */
	public static void synchronize(Alignment alignment, Tree tree, int poolSize) {
		instance = new TemporaryFileManager(alignment, tree, poolSize);
	}
	
	/**
	 * Converts an alignment into a file.
	 * 
	 * @param alignment the input alignment
	 * @param outputFile the output file
	 * 
	 * @return true, if successful
	 */
	private boolean convertAlignment(Alignment alignment, File outputFile) {
		try {
			FileOutputStream fo = new FileOutputStream(outputFile);
			PrintWriter output              = new PrintWriter(fo);
			//AlignmentUtils.printSequential(alignment, output);
			AlignmentUtils.printInterleaved(alignment, output);
			output.flush();
			output.close();
		} catch (IOException e) {
			throw new ProtTestInternalException();
		}
		return true;
	}

	/**
	 * Converts a tree into a file.
	 * 
	 * @param tree the input tree
	 * @param outputFile the output file
	 * 
	 * @return true, if successful
	 */
	private boolean convertTree(Tree tree, File outputFile) {
		if (tree != null) {
			try {
				FileOutputStream fo = new FileOutputStream(outputFile);
				PrintWriter output  = new PrintWriter(fo);
				TreeUtils.printNH(tree, output);
				output.flush();
				output.close();
			} catch (IOException e) {
				throw new ProtTestInternalException();
			}
		}
		return true;
	}
	
}
src/main/java/es/uvigo/darwin/prottest/selection/printer/AminoAcidPrintFramework.java
public class AminoAcidPrintFramework extends PrintFramework {

	/**
	 * Instantiates a new amino acid print framework.
	 */
	public AminoAcidPrintFramework() {
		super();
	}
	
	/* (non-Javadoc)
	 * @see es.uvigo.darwin.prottest.selection.printer.PrintFramework#printRelativeImportance(java.io.PrintWriter)
	 */
	@Override
	void printRelativeImportance(InformationCriterion ic) {
		
		println("***********************************************");
		println("Relative importance of parameters");
		println("***********************************************");
		println("  alpha       (+G):  " + ProtTestFormattedOutput.space(3, ' ') + getDisplayValue(ic.getAlphaImportance(), PARAMETER_G, ic.isExistGammaModels()));
		println("  p-inv       (+I):  " + ProtTestFormattedOutput.space(3, ' ') + getDisplayValue(ic.getInvImportance(), PARAMETER_I, ic.isExistInvModels()));
		println("  alpha+p-inv (+I+G):" + ProtTestFormattedOutput.space(3, ' ') + getDisplayValue(ic.getAlphaInvImportance(), PARAMETER_IG, ic.isExistGammaInvModels()));
		println("  freqs       (+F):  " + ProtTestFormattedOutput.space(3, ' ') + getDisplayValue(ic.getFImportance(), PARAMETER_F, ic.isExistFModels()));
		println("");
	}

	/* (non-Javadoc)
	 * @see es.uvigo.darwin.prottest.selection.printer.PrintFramework#printModelAveragedEstimation(java.io.PrintWriter)
	 */
	@Override
	void printModelAveragedEstimation(InformationCriterion ic) {
		
		println("***********************************************");
		println("Model-averaged estimate of parameters");
		println("***********************************************");
		println("  alpha (+G):        " + ProtTestFormattedOutput.space(3,  ' ') + getDisplayValue(ic.getOverallAlpha(), PARAMETER_G, ic.isExistGammaModels()));
		println("  p-inv (+I):        " + ProtTestFormattedOutput.space(3,  ' ') + getDisplayValue(ic.getOverallInv(), PARAMETER_I, ic.isExistInvModels()));
		println("  alpha (+I+G):      " + ProtTestFormattedOutput.space(3,  ' ') + getDisplayValue(ic.getOverallAlphaInv(), PARAMETER_IG, ic.isExistGammaInvModels()));
		println("  p-inv (+I+G):      " + ProtTestFormattedOutput.space(3,  ' ') + getDisplayValue(ic.getOverallInvAlpha(), PARAMETER_IG, ic.isExistGammaInvModels()));
	}

}
src/main/java/es/uvigo/darwin/prottest/selection/DT.java
public class DT extends InformationCriterion
{
	
	private BIC bic;
	private TreeDistancesCache distancesCache;
	
	/**
	 * Instantiates a new Decision Theory Information Criterion.
	 * 
	 * @param models the models
	 * @param confidenceInterval the confidence interval
	 * @param sampleSize the sample size if different of the default
	 */
	public DT (ModelCollection models, double confidenceInterval, double sampleSize) 
		{
		super(models, confidenceInterval, sampleSize);
		Collections.sort(selectionModels);
		}
	
	/* (non-Javadoc)
	 * @see es.uvigo.darwin.prottest.selection.InformationCriterion#getSelectionModels(es.uvigo.darwin.prottest.util.collection.ModelIterator)
	 */
	protected List<SelectionModel> getSelectionModels(List<Model> models) {
		ModelCollection modelCollection = new SingleModelCollection(
                        models.toArray(new Model[0]),
                        alignment);
		bic = new BIC(modelCollection, confidenceInterval, sampleSize);
		distancesCache = TreeEuclideanDistancesCache.getInstance();
		
		List<SelectionModel> list = new ArrayList<SelectionModel>();
		for (Model model : models) {
			SelectionModel toAdd = new DTSelectionModel(
					model, 
					sampleSize,
					bic,
					distancesCache); 
			list.add( toAdd );
			hashModels.put(model, toAdd);
		}
		return list;
	}

	/* (non-Javadoc)
	 * @see es.uvigo.darwin.prottest.selection.InformationCriterion#getCriterionName()
	 */
	public String getCriterionName() {
		return "DT";
	}
}
src/main/java/es/uvigo/darwin/prottest/selection/AICc.java
public class AICc extends InformationCriterion
{
	
	/**
	 * Instantiates a new Corrected Akaike Information Criterion.
	 * 
	 * @param models the models
	 * @param confidenceInterval the confidence interval
	 * @param sampleSize the sample size if different of the default
	 */
	public AICc (ModelCollection models, double confidenceInterval, double sampleSize) 
		{
		super(models, confidenceInterval, sampleSize);
		
		Collections.sort(selectionModels);
		}
	
	/* (non-Javadoc)
	 * @see es.uvigo.darwin.prottest.selection.InformationCriterion#getSelectionModels(es.uvigo.darwin.prottest.util.collection.ModelIterator)
	 */
	protected List<SelectionModel> getSelectionModels(List<Model> models) {
		List<SelectionModel> list = new ArrayList<SelectionModel>();
		for (Model model : models) {
			SelectionModel toAdd = new AICcSelectionModel(
					model, 
					sampleSize); 
			list.add( toAdd );
			hashModels.put(model, toAdd);
		}
		return list;
	}

	/* (non-Javadoc)
	 * @see es.uvigo.darwin.prottest.selection.InformationCriterion#getCriterionName()
	 */
	public String getCriterionName() {
		return "AICc";
	}
}
src/main/java/es/uvigo/darwin/prottest/exe/RunEstimator.java
public interface RunEstimator extends Runnable {

	/**
	 * Gets the inner optimized model.
	 * 
	 * @return the model
	 */
	public Model getModel();
	
	/**
	 * Optimizes the parameters of the model.
	 * 
	 * @return true, if there is no error in the execution of the optimizer
	 */
	public boolean optimizeModel();
	
	/**
	 * Reports out the result of the optimization into the application loggers: 
         * the optimized parameters of the model.
	 */
	public void report();
	
	/* (non-Javadoc)
	 * @see es.uvigo.darwin.prottest.observer.ObservableModelUpdater#addObserver(es.uvigo.darwin.prottest.observer.ObservableModelUpdater)
	 */
	public void addObserver(ModelUpdaterObserver o);

}
src/main/java/es/uvigo/darwin/prottest/selection/BIC.java
public class BIC extends InformationCriterion
{
	
//	/**
//	 * Instantiates a new Bayesian Information Criterion.
//	 * 
//	 * @param models the models
//	 * @param confidenceInterval the confidence interval
//	 */
//	public BIC (ModelCollection models, double confidenceInterval) 
//		{
//		super(models, confidenceInterval);
//		
//		Collections.sort(selectionModels);
//		}
	
	/**
	 * Instantiates a new Bayesian Information Criterion.
	 * 
	 * @param models the models
	 * @param confidenceInterval the confidence interval
	 * @param sampleSize the sample size if different of the default
	 */
	public BIC (ModelCollection models, double confidenceInterval, double sampleSize) 
		{
		super(models, confidenceInterval, sampleSize);
		
		Collections.sort(selectionModels);
		}
	
	/* (non-Javadoc)
	 * @see es.uvigo.darwin.prottest.selection.InformationCriterion#getSelectionModels(es.uvigo.darwin.prottest.util.collection.ModelIterator)
	 */
	protected List<SelectionModel> getSelectionModels(List<Model> models) {
		List<SelectionModel> list = new ArrayList<SelectionModel>();
		for (Model model : models) {
			SelectionModel toAdd = new BICSelectionModel(
					model, 
					sampleSize); 
			list.add( toAdd );
			hashModels.put(model, toAdd);
		}
		return list;
	}

	/* (non-Javadoc)
	 * @see es.uvigo.darwin.prottest.selection.InformationCriterion#getCriterionName()
	 */
	public String getCriterionName() {
		return "BIC";
	}
}
src/main/java/es/uvigo/darwin/prottest/global/ApplicationGlobals.java
public abstract class ApplicationGlobals implements ProtTestConstants {

    /** The application APPLICATION_PROPERTIES. */
    public static final Properties APPLICATION_PROPERTIES;
    public static final String ENV_PATH;
    public static final String JAR_PATH = ProtTest.class.getProtectionDomain()
                                                  .getCodeSource()
                                                  .getLocation()
                                                  .getFile()
                                                  .replace("%20", " ");
    public static final String PATH;
    public static final String DEFAULT_SNAPSHOT_DIR = "snapshot/";
    
    static {
      ENV_PATH = System.getenv("PROTTEST_PATH");
      if (ENV_PATH != null)
        PATH = ENV_PATH;
      else {
        if (ProtTest.MPJ_RUN)
          PATH = System.getProperty("user.dir");
        else
          PATH = JAR_PATH.replaceFirst(new File(JAR_PATH).getName(), "");
      }
      APPLICATION_PROPERTIES = new Properties();
      try {
        FileInputStream prop = new FileInputStream(
            PATH + File.separator + "prottest.properties");
        APPLICATION_PROPERTIES.load(prop);
      } catch (IOException e) {
        System.err.println("Properties file (" + PATH + File.separator
            + "prottest.properties) cannot be resolved");
        System.exit(EXIT_NO_PROPERTIES);
      }
    }

    /**
     * Gets the supported matrices.
     * 
     * @return the supported matrices
     */
    public abstract List<String> getSupportedMatrices();

    /**
     * Gets the model name from matrix and distribution names. This
     * method is useful specially when a custom matrix is set, so
     * his internal name is not representative for the user.
     * 
     * @param matrix the matrix name
     * @param frequenciesDistribution the frequencies distribution name
     * 
     * @return the model name
     */
    public abstract String getModelName(String matrix, int frequenciesDistribution);
}
src/main/java/es/uvigo/darwin/prottest/util/logging/ProtTestLogger.java
@SuppressWarnings("rawtypes")
public class ProtTestLogger {

	public static final String DEFAULT_LOGGER_NAME = "default";
	public static final Level DEFAULT_LEVEL = Level.INFO;

	private static HashMap<String, ProtTestLogger> loggers;

	static {

		loggers = new HashMap<String, ProtTestLogger>();
		ProtTestLogger defaultLogger = new ProtTestLogger(DEFAULT_LOGGER_NAME);
		loggers.put(DEFAULT_LOGGER_NAME, defaultLogger);

	}

	private String loggerName;
	private Level loggerLevel;
	private List<Handler> handlers;
	private Handler stdHandler;
	private Handler lowLevelHandler = null;

	public static ProtTestLogger getLogger(String loggerName, boolean force) {
		if (!loggers.containsKey(loggerName)) {
			if (force)
				loggers.put(loggerName, new ProtTestLogger(loggerName));
			else
				return null;
		}
		return loggers.get(loggerName);
	}

	public static boolean exists(Class classLogger) {
		return getLogger(classLogger.getName(), false) != null;
	}

	public ProtTestLogger(String loggerName) {
		this.loggerName = loggerName;
		if (loggers.containsKey(loggerName))
			throw new ProtTestInternalException("ProtTestLogger with name "
					+ loggerName + " already exists. "
					+ " Use getLogger() instead");
		this.loggerLevel = Level.ALL;

		// Add standard output handler
		this.handlers = new ArrayList<Handler>();
		stdHandler = new StreamHandler(System.out, new ProtTestLogFormatter());
		stdHandler.setLevel(Level.OFF);
		handlers.add(stdHandler);
	}

	public static ProtTestLogger getDefaultLogger() {
		return getLogger(DEFAULT_LOGGER_NAME, true);
	}

	/**
	 * Check if a message of the given level would actually be logged by this
	 * logger. This check is based on the Loggers effective level, which may be
	 * inherited from its parent.
	 * 
	 * @param level
	 *            a message logging level
	 * @return true if the given message level is currently being logged.
	 */
	public boolean isLoggable(Level level) {
		if (level.intValue() < loggerLevel.intValue()
				|| loggerLevel.intValue() == Level.OFF.intValue()) {
			return false;
		}
		return true;
	}

	public void addHandler(Handler handler) {
		if (handler == null) {
			throw new NullPointerException();
		}
		this.handlers.add(handler);
	}

	public void addHandler(Handler[] newHandlers) {
		for (Handler handler : newHandlers) {
			if (handler == null) {
				throw new NullPointerException();
			}
			if (!handlers.contains(handler))
				this.handlers.add(handler);
		}
	}

	public Handler addHandler(OutputStream out) {
		return addHandler(out, DEFAULT_LEVEL);
	}

	public Handler addHandler(OutputStream out, Level level) {
		if (out == null) {
			throw new NullPointerException();
		}
		Handler handler = new StreamHandler(out, new ProtTestLogFormatter());
		handler.setLevel(level);
		this.handlers.add(handler);
		return handler;
	}

	public Handler addHandler(Writer out) {
		return addHandler(out, DEFAULT_LEVEL);
	}

	public Handler addHandler(final Writer out, Level level) {
		if (out == null) {
			throw new NullPointerException();
		}
		Handler handler = new StreamHandler(
		  new OutputStream()
		  {
			protected Writer _writer = out;
		    protected String _encoding;
		    private byte[] _buf=new byte[1];
		    
			@Override
		    public void close()
		        throws IOException
		    {
		        _writer.close();
		        _writer=null;
		        _encoding=null;
		    }
		    
		    @Override
		    public void flush()
		        throws IOException
		    {
		        _writer.flush();
		    }
		    
		    @Override
		    public void write(byte[] b) 
		        throws IOException
		    {
		        if (_encoding==null)
		            _writer.write(new String(b));
		        else
		            _writer.write(new String(b,_encoding));
		    }
		    
		    @Override
		    public void write(byte[] b, int off, int len)
		        throws IOException
		    {
		        if (_encoding==null)
		            _writer.write(new String(b,off,len));
		        else
		            _writer.write(new String(b,off,len,_encoding));
		    }
		    
		    public synchronized void write(int b)
		        throws IOException
		    {
		        _buf[0]=(byte)b;
		        write(_buf);
		    }
		},
				new ProtTestLogFormatter());
		handler.setLevel(level);
		this.handlers.add(handler);
		return handler;
	}

	public Handler addLowLevelHandler(final Writer out) {
		if (out == null) {
			throw new NullPointerException();
		}
		Handler handler = new StreamHandler(new OutputStream()
		  {
			protected Writer _writer = out;
		    protected String _encoding;
		    private byte[] _buf=new byte[1];
		    
			@Override
		    public void close()
		        throws IOException
		    {
		        _writer.close();
		        _writer=null;
		        _encoding=null;
		    }
		    
		    @Override
		    public void flush()
		        throws IOException
		    {
		        _writer.flush();
		    }
		    
		    @Override
		    public void write(byte[] b) 
		        throws IOException
		    {
		        if (_encoding==null)
		            _writer.write(new String(b));
		        else
		            _writer.write(new String(b,_encoding));
		    }
		    
		    @Override
		    public void write(byte[] b, int off, int len)
		        throws IOException
		    {
		        if (_encoding==null)
		            _writer.write(new String(b,off,len));
		        else
		            _writer.write(new String(b,off,len,_encoding));
		    }
		    
		    public synchronized void write(int b)
		        throws IOException
		    {
		        _buf[0]=(byte)b;
		        write(_buf);
		    }
		},
				new ProtTestLogFormatter());
		handler.setLevel(Level.ALL);
		lowLevelHandler = handler;
		return handler;
	}

	public Handler getLowLevelHandler() {
		return lowLevelHandler;
	}

	public boolean removeHandler(Handler handler) {
		return this.handlers.remove(handler);
	}

	public Handler[] getHandlers() {
		return handlers.toArray(new Handler[0]);
	}

	public void setStdHandlerLevel(Level newLevel) {
		if (newLevel == null) {
			throw new NullPointerException();
		}
		stdHandler.setLevel(newLevel);
	}

	public Level getStdHandlerLevel() {
		return stdHandler.getLevel();
	}

	public void setLevel(Level newLevel) {
		if (newLevel == null) {
			throw new NullPointerException();
		}
		loggerLevel = newLevel;
	}

	public Level getLevel() {
		return loggerLevel;
	}

	public void info(String text) {
		log(Level.INFO, text);
	}

	public void warning(String text) {
		log(Level.WARNING, text);
	}

	public void config(String text) {
		log(Level.CONFIG, text);
	}

	public void severe(String text) {
		log(Level.SEVERE, text);
	}

	public void fine(String text) {
		log(Level.FINE, text);
	}

	public void finer(String text) {
		log(Level.FINER, text);
	}

	public void finest(String text) {
		log(Level.FINEST, text);
	}

	public void infoln(String text) {
		log(Level.INFO, text + "\n");
		flush();
	}

	public void warningln(String text) {
		log(Level.WARNING, text + "\n");
		flush();
	}

	public void configln(String text) {
		log(Level.CONFIG, text + "\n");
		flush();
	}

	public void severeln(String text) {
		log(Level.SEVERE, text + "\n");
		flush();
	}

	public void fineln(String text) {
		log(Level.FINE, text + "\n");
		flush();
	}

	public void finerln(String text) {
		log(Level.FINER, text + "\n");
		flush();
	}

	public void finestln(String text) {
		log(Level.FINEST, text + "\n");
		flush();
	}

	public static void info(String text, Class loggingClass) {
		log(Level.INFO, text, loggingClass);
	}

	public static void warning(String text, Class loggingClass) {
		log(Level.WARNING, text, loggingClass);
	}

	public static void config(String text, Class loggingClass) {
		log(Level.CONFIG, text, loggingClass);
	}

	public static void severe(String text, Class loggingClass) {
		log(Level.SEVERE, text, loggingClass);
	}

	public static void fine(String text, Class loggingClass) {
		log(Level.FINE, text, loggingClass);
	}

	public static void finer(String text, Class loggingClass) {
		log(Level.FINER, text, loggingClass);
	}

	public static void finest(String text, Class loggingClass) {
		log(Level.FINEST, text, loggingClass);
	}

	public static void infoln(String text, Class loggingClass) {
		log(Level.INFO, text + "\n", loggingClass);
		flush(loggingClass);
	}

	public static void warningln(String text, Class loggingClass) {
		log(Level.WARNING, text + "\n", loggingClass);
		flush(loggingClass);
	}

	public static void configln(String text, Class loggingClass) {
		log(Level.CONFIG, text + "\n", loggingClass);
		flush(loggingClass);
	}

	public static void severeln(String text, Class loggingClass) {
		log(Level.SEVERE, text + "\n", loggingClass);
		flush(loggingClass);
	}

	public static void fineln(String text, Class loggingClass) {
		log(Level.FINE, text + "\n", loggingClass);
		flush(loggingClass);
	}

	public static void finerln(String text, Class loggingClass) {
		log(Level.FINER, text + "\n", loggingClass);
		flush(loggingClass);
	}

	public static void finestln(String text, Class loggingClass) {
		log(Level.FINEST, text + "\n", loggingClass);
		flush(loggingClass);
	}

	public static void log(Level level, String text, Class loggingClass) {
		// default logging + class logging
		getDefaultLogger().log(level, text);
		if (exists(loggingClass))
			getLogger(loggingClass.getName(), false).log(level, text);
	}

	public void log(Level level, String text) {
		log(new LogRecord(level, text));
	}

	public void log(LogRecord lr) {

		if (lr.getLevel().intValue() > loggerLevel.intValue()
				&& loggerLevel.intValue() != Level.OFF.intValue()) {

			for (Handler handler : handlers)
				handler.publish(lr);

		}

	}

	public static void lowLevelLog(String text) {
		if (getDefaultLogger().lowLevelHandler != null) {
			LogRecord lr = new LogRecord(Level.INFO, text + "\n");
			getDefaultLogger().lowLevelHandler.publish(lr);
			getDefaultLogger().lowLevelHandler.flush();
		}
	}

	public void flush() {
		for (Handler handler : handlers)
			handler.flush();
	}

	public static void flush(Class loggingClass) {
		getDefaultLogger().flush();
		if (exists(loggingClass))
			getLogger(loggingClass.getName(), false).flush();
	}
}
src/main/java/es/uvigo/darwin/prottest/util/logging/ProtTestLogger.java
@SuppressWarnings("rawtypes")
public class ProtTestLogger {

	public static final String DEFAULT_LOGGER_NAME = "default";
	public static final Level DEFAULT_LEVEL = Level.INFO;

	private static HashMap<String, ProtTestLogger> loggers;

	static {

		loggers = new HashMap<String, ProtTestLogger>();
		ProtTestLogger defaultLogger = new ProtTestLogger(DEFAULT_LOGGER_NAME);
		loggers.put(DEFAULT_LOGGER_NAME, defaultLogger);

	}

	private String loggerName;
	private Level loggerLevel;
	private List<Handler> handlers;
	private Handler stdHandler;
	private Handler lowLevelHandler = null;

	public static ProtTestLogger getLogger(String loggerName, boolean force) {
		if (!loggers.containsKey(loggerName)) {
			if (force)
				loggers.put(loggerName, new ProtTestLogger(loggerName));
			else
				return null;
		}
		return loggers.get(loggerName);
	}

	public static boolean exists(Class classLogger) {
		return getLogger(classLogger.getName(), false) != null;
	}

	public ProtTestLogger(String loggerName) {
		this.loggerName = loggerName;
		if (loggers.containsKey(loggerName))
			throw new ProtTestInternalException("ProtTestLogger with name "
					+ loggerName + " already exists. "
					+ " Use getLogger() instead");
		this.loggerLevel = Level.ALL;

		// Add standard output handler
		this.handlers = new ArrayList<Handler>();
		stdHandler = new StreamHandler(System.out, new ProtTestLogFormatter());
		stdHandler.setLevel(Level.OFF);
		handlers.add(stdHandler);
	}

	public static ProtTestLogger getDefaultLogger() {
		return getLogger(DEFAULT_LOGGER_NAME, true);
	}

	/**
	 * Check if a message of the given level would actually be logged by this
	 * logger. This check is based on the Loggers effective level, which may be
	 * inherited from its parent.
	 * 
	 * @param level
	 *            a message logging level
	 * @return true if the given message level is currently being logged.
	 */
	public boolean isLoggable(Level level) {
		if (level.intValue() < loggerLevel.intValue()
				|| loggerLevel.intValue() == Level.OFF.intValue()) {
			return false;
		}
		return true;
	}

	public void addHandler(Handler handler) {
		if (handler == null) {
			throw new NullPointerException();
		}
		this.handlers.add(handler);
	}

	public void addHandler(Handler[] newHandlers) {
		for (Handler handler : newHandlers) {
			if (handler == null) {
				throw new NullPointerException();
			}
			if (!handlers.contains(handler))
				this.handlers.add(handler);
		}
	}

	public Handler addHandler(OutputStream out) {
		return addHandler(out, DEFAULT_LEVEL);
	}

	public Handler addHandler(OutputStream out, Level level) {
		if (out == null) {
			throw new NullPointerException();
		}
		Handler handler = new StreamHandler(out, new ProtTestLogFormatter());
		handler.setLevel(level);
		this.handlers.add(handler);
		return handler;
	}

	public Handler addHandler(Writer out) {
		return addHandler(out, DEFAULT_LEVEL);
	}

	public Handler addHandler(final Writer out, Level level) {
		if (out == null) {
			throw new NullPointerException();
		}
		Handler handler = new StreamHandler(
		  new OutputStream()
		  {
			protected Writer _writer = out;
		    protected String _encoding;
		    private byte[] _buf=new byte[1];
		    
			@Override
		    public void close()
		        throws IOException
		    {
		        _writer.close();
		        _writer=null;
		        _encoding=null;
		    }
		    
		    @Override
		    public void flush()
		        throws IOException
		    {
		        _writer.flush();
		    }
		    
		    @Override
		    public void write(byte[] b) 
		        throws IOException
		    {
		        if (_encoding==null)
		            _writer.write(new String(b));
		        else
		            _writer.write(new String(b,_encoding));
		    }
		    
		    @Override
		    public void write(byte[] b, int off, int len)
		        throws IOException
		    {
		        if (_encoding==null)
		            _writer.write(new String(b,off,len));
		        else
		            _writer.write(new String(b,off,len,_encoding));
		    }
		    
		    public synchronized void write(int b)
		        throws IOException
		    {
		        _buf[0]=(byte)b;
		        write(_buf);
		    }
		},
				new ProtTestLogFormatter());
		handler.setLevel(level);
		this.handlers.add(handler);
		return handler;
	}

	public Handler addLowLevelHandler(final Writer out) {
		if (out == null) {
			throw new NullPointerException();
		}
		Handler handler = new StreamHandler(new OutputStream()
		  {
			protected Writer _writer = out;
		    protected String _encoding;
		    private byte[] _buf=new byte[1];
		    
			@Override
		    public void close()
		        throws IOException
		    {
		        _writer.close();
		        _writer=null;
		        _encoding=null;
		    }
		    
		    @Override
		    public void flush()
		        throws IOException
		    {
		        _writer.flush();
		    }
		    
		    @Override
		    public void write(byte[] b) 
		        throws IOException
		    {
		        if (_encoding==null)
		            _writer.write(new String(b));
		        else
		            _writer.write(new String(b,_encoding));
		    }
		    
		    @Override
		    public void write(byte[] b, int off, int len)
		        throws IOException
		    {
		        if (_encoding==null)
		            _writer.write(new String(b,off,len));
		        else
		            _writer.write(new String(b,off,len,_encoding));
		    }
		    
		    public synchronized void write(int b)
		        throws IOException
		    {
		        _buf[0]=(byte)b;
		        write(_buf);
		    }
		},
				new ProtTestLogFormatter());
		handler.setLevel(Level.ALL);
		lowLevelHandler = handler;
		return handler;
	}

	public Handler getLowLevelHandler() {
		return lowLevelHandler;
	}

	public boolean removeHandler(Handler handler) {
		return this.handlers.remove(handler);
	}

	public Handler[] getHandlers() {
		return handlers.toArray(new Handler[0]);
	}

	public void setStdHandlerLevel(Level newLevel) {
		if (newLevel == null) {
			throw new NullPointerException();
		}
		stdHandler.setLevel(newLevel);
	}

	public Level getStdHandlerLevel() {
		return stdHandler.getLevel();
	}

	public void setLevel(Level newLevel) {
		if (newLevel == null) {
			throw new NullPointerException();
		}
		loggerLevel = newLevel;
	}

	public Level getLevel() {
		return loggerLevel;
	}

	public void info(String text) {
		log(Level.INFO, text);
	}

	public void warning(String text) {
		log(Level.WARNING, text);
	}

	public void config(String text) {
		log(Level.CONFIG, text);
	}

	public void severe(String text) {
		log(Level.SEVERE, text);
	}

	public void fine(String text) {
		log(Level.FINE, text);
	}

	public void finer(String text) {
		log(Level.FINER, text);
	}

	public void finest(String text) {
		log(Level.FINEST, text);
	}

	public void infoln(String text) {
		log(Level.INFO, text + "\n");
		flush();
	}

	public void warningln(String text) {
		log(Level.WARNING, text + "\n");
		flush();
	}

	public void configln(String text) {
		log(Level.CONFIG, text + "\n");
		flush();
	}

	public void severeln(String text) {
		log(Level.SEVERE, text + "\n");
		flush();
	}

	public void fineln(String text) {
		log(Level.FINE, text + "\n");
		flush();
	}

	public void finerln(String text) {
		log(Level.FINER, text + "\n");
		flush();
	}

	public void finestln(String text) {
		log(Level.FINEST, text + "\n");
		flush();
	}

	public static void info(String text, Class loggingClass) {
		log(Level.INFO, text, loggingClass);
	}

	public static void warning(String text, Class loggingClass) {
		log(Level.WARNING, text, loggingClass);
	}

	public static void config(String text, Class loggingClass) {
		log(Level.CONFIG, text, loggingClass);
	}

	public static void severe(String text, Class loggingClass) {
		log(Level.SEVERE, text, loggingClass);
	}

	public static void fine(String text, Class loggingClass) {
		log(Level.FINE, text, loggingClass);
	}

	public static void finer(String text, Class loggingClass) {
		log(Level.FINER, text, loggingClass);
	}

	public static void finest(String text, Class loggingClass) {
		log(Level.FINEST, text, loggingClass);
	}

	public static void infoln(String text, Class loggingClass) {
		log(Level.INFO, text + "\n", loggingClass);
		flush(loggingClass);
	}

	public static void warningln(String text, Class loggingClass) {
		log(Level.WARNING, text + "\n", loggingClass);
		flush(loggingClass);
	}

	public static void configln(String text, Class loggingClass) {
		log(Level.CONFIG, text + "\n", loggingClass);
		flush(loggingClass);
	}

	public static void severeln(String text, Class loggingClass) {
		log(Level.SEVERE, text + "\n", loggingClass);
		flush(loggingClass);
	}

	public static void fineln(String text, Class loggingClass) {
		log(Level.FINE, text + "\n", loggingClass);
		flush(loggingClass);
	}

	public static void finerln(String text, Class loggingClass) {
		log(Level.FINER, text + "\n", loggingClass);
		flush(loggingClass);
	}

	public static void finestln(String text, Class loggingClass) {
		log(Level.FINEST, text + "\n", loggingClass);
		flush(loggingClass);
	}

	public static void log(Level level, String text, Class loggingClass) {
		// default logging + class logging
		getDefaultLogger().log(level, text);
		if (exists(loggingClass))
			getLogger(loggingClass.getName(), false).log(level, text);
	}

	public void log(Level level, String text) {
		log(new LogRecord(level, text));
	}

	public void log(LogRecord lr) {

		if (lr.getLevel().intValue() > loggerLevel.intValue()
				&& loggerLevel.intValue() != Level.OFF.intValue()) {

			for (Handler handler : handlers)
				handler.publish(lr);

		}

	}

	public static void lowLevelLog(String text) {
		if (getDefaultLogger().lowLevelHandler != null) {
			LogRecord lr = new LogRecord(Level.INFO, text + "\n");
			getDefaultLogger().lowLevelHandler.publish(lr);
			getDefaultLogger().lowLevelHandler.flush();
		}
	}

	public void flush() {
		for (Handler handler : handlers)
			handler.flush();
	}

	public static void flush(Class loggingClass) {
		getDefaultLogger().flush();
		if (exists(loggingClass))
			getLogger(loggingClass.getName(), false).flush();
	}
}
src/main/java/es/uvigo/darwin/prottest/util/exception/AlignmentParseException.java
public class AlignmentParseException extends ProtTestCheckedException {

    private static final String MESSAGE = "Alignment parse exception";
    /** The Constant serialVersionUID. */
    private static final long serialVersionUID = 20090728L;

    /**
     * Instantiates a new alignment format exception.
     */
    public AlignmentParseException() {
        super(MESSAGE);
    }

    /**
     * Instantiates a new alignment format exception.
     * 
     * @param description the description
     */
    public AlignmentParseException(String description) {
        super(MESSAGE + ": " + description);
    }
}
src/main/java/es/uvigo/darwin/prottest/util/exception/TreeFormatException.java
public class TreeFormatException extends ProtTestInternalException {

	/** The Constant serialVersionUID. */
	private static final long serialVersionUID = 20090728L;

	/**
	 * Instantiates a new tree format exception.
	 */
	public TreeFormatException() {}
	
	/**
	 * Instantiates a new tree format exception.
	 * 
	 * @param description the description
	 */
	public TreeFormatException(String description) {
		super(description);
	}

}
src/main/java/es/uvigo/darwin/prottest/observer/ObservableModelUpdater.java
public abstract class ObservableModelUpdater {

    /** Collection of observers */
    private Collection<ModelUpdaterObserver> modelUpdaterObservers;

    /**
     * Constructor for observable objects.
     */
    public ObservableModelUpdater() {
        modelUpdaterObservers = new ArrayList<ModelUpdaterObserver>();
    }

    /**
     * Adds a new observer.
     * 
     * @param o the observer
     */
    public void addObserver(ModelUpdaterObserver o) {
        modelUpdaterObservers.add(o);
    }

    /**
     * Removes an observer.
     * 
     * @param o the observer
     */
    public void removeObserver(ModelUpdaterObserver o) {
        modelUpdaterObservers.remove(o);
    }

    /**
     * Notifies all observers.
     */
    public void notifyObservers() {
        for (ModelUpdaterObserver o : modelUpdaterObservers) {
            o.update(this, null, null);
        }
    }

    /**
     * Notifies all observers with a model an options
     * 
     * @param model the substitution models
     * @param options the application options
     */
    public void notifyObservers(Model model, ApplicationOptions options) {
        for (ModelUpdaterObserver o : modelUpdaterObservers) {
            o.update(this, model, options);
        }
    }
}
src/main/java/es/uvigo/darwin/prottest/util/ProtTestAlignment.java
public abstract class ProtTestAlignment {

	/**
	 * Calculate sample size.
	 * 
	 * @param alignment
	 *            the alignment to calculate sample size
	 * @return the sample size
	 */
	public static double calculateSampleSize(Alignment alignment) {
		// For AICc and BIC frameworks
		return alignment.getSiteCount();
	}

	/**
	 * Calculate invariable sites.
	 * 
	 * @param alignment
	 *            the alignment
	 * @param verbose
	 *            the verbose
	 * 
	 * @return the int
	 */
	public static int calculateInvariableSites(Alignment alignment,
			boolean verbose) {
		// use this function to estimate a good starting value for the
		// InvariableSites distribution.
		int numSites = alignment.getSiteCount();
		int numSeqs = alignment.getSequenceCount();
		int inv = 0;
		int tmp;
		boolean tmpInv = true;
		for (int i = 0; i < numSites; i++) {
			tmp = indexOfChar(alignment.getData(0, i));
			tmpInv = true;
			for (int j = 0; j < numSeqs; j++) {
				if (indexOfChar(alignment.getData(j, i)) != tmp) { // if at
																	// least one
																	// difference
																	// in column
																	// i:
					j = numSeqs; // we exit this for.
					tmpInv = false; // i is not an invariable site.
				}
			}
			if (tmpInv)
				inv++;
		}
		if (verbose)
			System.out.println("Observed number of invariant sites: " + inv);
		return inv;
	}

	/**
	 * Gets the frequencies.
	 * 
	 * @param alignment
	 *            the alignment
	 * 
	 * @return the frequencies
	 */
	public static double[] getFrequencies(Alignment alignment) {
		int numSites = alignment.getSiteCount();
		int numSeqs = alignment.getSequenceCount();
		double freqs[] = new double[AMINOACID_NUM_STATES];
		int aas[] = new int[AMINOACID_NUM_STATES];
		int total_aas = 0;
		for (int i = 0; i < AMINOACID_NUM_STATES; i++)
			aas[i] = 0;

		for (int i = 0; i < numSites; i++)
			for (int j = 0; j < numSeqs; j++) {
				int index = indexOfChar(alignment.getData(j, i));
				if (index >= 0) {
					aas[index]++;
					total_aas++;
				}
			}

		for (int i = 0; i < AMINOACID_NUM_STATES; i++)
			freqs[i] = (double) aas[i] / (double) total_aas;

		return freqs;
	}

	/**
	 * Prints the frequencies.
	 * 
	 * @param freqs
	 *            the freqs
	 * @param out
	 *            the out
	 */
	public static void printFrequencies(double[] freqs, PrintWriter out) {

		out.println("Observed aminoacid frequencies:");
		for (int i = 0; i < AMINOACID_NUM_STATES; i++) {
			char aa = charOfIndex(i);
			out.print(" " + aa + ": "
					+ ProtTestFormattedOutput.getDecimalString(freqs[i], 3)
					+ "   ");
			if ((i + 1) % 5 == 0)
				out.println("");
		}
	}

	/**
	 * Char of index.
	 * 
	 * @param c
	 *            the c
	 * 
	 * @return the char
	 */
	public static char charOfIndex(int c) {
		char[] charSet = { 'A', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'K', 'L',
				'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'V', 'W', 'Y' };
		if (c >= 0 && c <= charSet.length)
			return charSet[c];
		else {
			return '?';
		}
	}

	/**
	 * Index of char.
	 * 
	 * @param c
	 *            the c
	 * 
	 * @return the int
	 */
	private static int indexOfChar(char c) {
		char[] charSet = { 'A', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'K', 'L',
				'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'V', 'W', 'Y' };
		for (int charIndex = 0; charIndex < charSet.length; charIndex++)
			if (charSet[charIndex] == c)
				return charIndex;

		return -1;
	}
}


Question: import static es.uvigo.darwin.prottest.global.ApplicationGlobals.*;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Properties;
import pal.alignment.Alignment;
import pal.tree.Tree;
import es.uvigo.darwin.prottest.exe.RunEstimator;
import es.uvigo.darwin.prottest.exe.util.TemporaryFileManager;
import es.uvigo.darwin.prottest.facade.util.ProtTestParameterVO;
import es.uvigo.darwin.prottest.facade.util.SelectionChunk;
import es.uvigo.darwin.prottest.global.options.ApplicationOptions;
import es.uvigo.darwin.prottest.model.Model;
import es.uvigo.darwin.prottest.observer.ObservableModelUpdater;
import es.uvigo.darwin.prottest.selection.AIC;
import es.uvigo.darwin.prottest.selection.AICc;
import es.uvigo.darwin.prottest.selection.BIC;
import es.uvigo.darwin.prottest.selection.DT;
import es.uvigo.darwin.prottest.selection.InformationCriterion;
import es.uvigo.darwin.prottest.selection.LNL;
import es.uvigo.darwin.prottest.selection.printer.AminoAcidPrintFramework;
import es.uvigo.darwin.prottest.selection.printer.PrintFramework;
import es.uvigo.darwin.prottest.util.ProtTestAlignment;
import es.uvigo.darwin.prottest.util.collection.ModelCollection;
import es.uvigo.darwin.prottest.util.collection.SingleModelCollection;
import es.uvigo.darwin.prottest.util.exception.AlignmentParseException;
import es.uvigo.darwin.prottest.util.exception.ProtTestInternalException;
import es.uvigo.darwin.prottest.util.exception.TreeFormatException;
import es.uvigo.darwin.prottest.util.factory.ProtTestFactory;
import es.uvigo.darwin.prottest.util.fileio.AlignmentReader;
import es.uvigo.darwin.prottest.util.logging.ProtTestLogger;
import java.io.StringWriter;
import static es.uvigo.darwin.prottest.util.logging.ProtTestLogger.*;
/*
Copyright (C) 2009  Diego Darriba

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 2 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, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
package es.uvigo.darwin.prottest.facade;





/**
 * An abstract implementation of the ProtTest facade.
 */
public abstract class ProtTestFacadeImpl
        extends ObservableModelUpdater
        implements ProtTestFacade {

//	/** The options. */
//	protected ApplicationOptions options;
    /** The Constant AIC. */
    public static final int AIC = SelectionChunk.AIC;
    /** The Constant BIC. */
    public static final int BIC = SelectionChunk.BIC;
    /** The Constant AICC. */
    public static final int AICC = SelectionChunk.AICC;
    /** The Constant DT. */
    public static final int DT = SelectionChunk.DT;
    /** The Constant LK. */
    public static final int LNL = SelectionChunk.LNL;

    /**
     * Instantiates a new prot test facade implementation.
     */
    public ProtTestFacadeImpl() {
    }

    /* (non-Javadoc)
     * @see es.uvigo.darwin.prottest.facade.ProtTestFacade#printModelsSorted(es.uvigo.darwin.prottest.selection.InformationCriterion, java.io.PrintWriter)
     */
    public void printModelsSorted(InformationCriterion informationCriterion) {

        PrintFramework printFramework = new AminoAcidPrintFramework();

        printFramework.printModelsSorted(informationCriterion);

    }

    /* (non-Javadoc)
     * @see es.uvigo.darwin.prottest.facade.ProtTestFacade#readAlignment(java.io.PrintWriter, java.lang.String, boolean)
     */
    public Alignment readAlignment(String filename, boolean debug)
            throws AlignmentParseException, FileNotFoundException, IOException {

        StringWriter sw = new StringWriter();
        Alignment alignment = AlignmentReader.readAlignment(new PrintWriter(sw), filename, debug);
        
        sw.flush();
        ProtTestLogger.getDefaultLogger().infoln(sw.toString());
        
        return alignment;
    }

    /* (non-Javadoc)
     * @see es.uvigo.darwin.prottest.facade.ProtTestFacade#readTree(java.io.PrintWriter, java.lang.String, boolean)
     */
    public Tree readTree(PrintWriter out, String filename, boolean debug)
            throws TreeFormatException, FileNotFoundException, IOException {

        return AlignmentReader.readTree(out, filename, debug);
    
    }

    /* (non-Javadoc)
     * @see es.uvigo.darwin.prottest.facade.ProtTestFacade#update(es.uvigo.darwin.prottest.observer.Observable, java.lang.Object)
     */
    public void update(ObservableModelUpdater o, Model model, ApplicationOptions options) {
        notifyObservers(model, options);
    }

    /* (non-Javadoc)
     * @see es.uvigo.darwin.prottest.facade.ProtTestFacade#computeInformationCriterion(pal.alignment.Alignment, es.uvigo.darwin.prottest.model.Model[], int, int, double)
     */
    public SelectionChunk computeInformationCriterion(Alignment alignment, Model[] models,
            int criterion,
            double confidenceInterval) {

        ModelCollection modelCollection = new SingleModelCollection(models, alignment);

        InformationCriterion informationCriterion;

        double calculatedSampleSize = ProtTestAlignment.calculateSampleSize(alignment);

        switch (criterion) {
            case AIC:
                informationCriterion = new AIC(modelCollection, confidenceInterval, calculatedSampleSize);
                break;
            case BIC:
                informationCriterion = new BIC(modelCollection, confidenceInterval, calculatedSampleSize);
                break;
            case AICC:
                informationCriterion = new AICc(modelCollection, confidenceInterval, calculatedSampleSize);
                break;
            case DT:
                informationCriterion = new DT(modelCollection, confidenceInterval, calculatedSampleSize);
                break;
            case LNL:
                informationCriterion = new LNL(modelCollection, confidenceInterval, calculatedSampleSize);
                break;
            default:
Answer:                 throw new ProtTestInternalException("Unexistent information criterion");