/* * Copyright (c) 2009 The Jackson Laboratory * * This software was developed by Gary Churchill's Lab at The Jackson * Laboratory (see http://research.jax.org/faculty/churchill). * * This is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This software 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 software. If not, see <http://www.gnu.org/licenses/>. */ package org.jax.qtl.scan.gui; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.util.ArrayList; import java.util.List; import java.util.logging.Logger; import org.jax.qtl.scan.ScanOneResult; import org.jax.qtl.scan.gui.ScanOneInterval.IntervalPoint; import org.jax.qtl.scan.gui.ScanOneInterval.IntervalShape; import org.jax.qtl.scan.gui.ScanOneInterval.IntervalType; import org.jax.r.RCommand; import org.jax.r.RCommandParameter; import org.jax.r.RMethodInvocationCommand; import org.jax.r.RUtilities; import org.jax.r.jriutilities.JRIUtilityFunctions; import org.jax.r.jriutilities.RInterface; import org.jax.r.jriutilities.SilentRCommand; import org.rosuda.JRI.REXP; /** * Class for building the R command for calculating * intervals on scanone results * @author <A HREF="mailto:keith.sheppard@jax.org">Keith Sheppard</A> */ public class ScanOneIntervalCommandBuilder { /** * our logger */ private static final Logger LOG = Logger.getLogger( ScanOneIntervalCommandBuilder.class.getName()); private static final String BAYESIAN_METHOD_NAME = "bayesint"; private static final String LOD_METHOD_NAME = "lodint"; /** * The property name used when {@link #setIntervalType(IntervalType)} * is called * @see #addPropertyChangeListener(PropertyChangeListener) */ public static final String INTERVAL_TYPE_PROPERTY_NAME = "intervalType"; private volatile IntervalType intervalType = IntervalType.BAYESIAN_CREDIBLE; /** * The property name used when {@link #setScanOneResult(ScanOneResult)} * is called * @see #addPropertyChangeListener(PropertyChangeListener) */ public static final String SCAN_ONE_RESULT_PROPERTY_NAME = "scanOneResult"; private static final String SCAN_ONE_RESULT_PARAMETER_NAME = "results"; private volatile ScanOneResult scanOneResult; /** * The property name used when {@link #setChromosomeNames(String[])} is * called * @see #addPropertyChangeListener(PropertyChangeListener) */ public static final String CHROMOSOME_NAMES_PROPERTY_NAME = "chromosomeNames"; private static final String CHROMOSOME_NAME_PARAMETER_NAME = "chr"; private volatile String[] chromosomeNames; /** * The property name used when {@link #setProbabilityCoverage(double)} is * called * @see #addPropertyChangeListener(PropertyChangeListener) */ public static final String PROBABILITY_COVERAGE_PROPERTY_NAME = "probabilityCoverage"; private static final String PROBABILITY_COVERAGE_PARAMETER_NAME = "prob"; private volatile double probabilityCoverage = 0.95; /** * The property name used when {@link #setLodDrop(double)} is called * @see #addPropertyChangeListener(PropertyChangeListener) */ public static final String LOD_DROP_PROPERTY_NAME = "lodDrop"; private static final String LOD_DROP_PARAMETER_NAME = "drop"; private volatile double lodDrop = 1.5; private final PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this); /** * The property name used when {@link #setLodColumnIndex(int)} is called */ private static final String LOD_COLUMN_INDEX_PROPERTY_NAME = "lodColumnIndex"; private static final String LOD_COLUMN_INDEX_PARAMETER_NAME = "lodcolumn"; private volatile int lodColumnIndex; /** * The offset we need to use when indexing the result of an interval * command */ private final static int INTERVAL_RESULT_LOD_COLUMN_INDEX_OFFSET = 3; /** * Adds a new listener to this command builder. The given listener will * be notified whenever one of the setters for this builder is called * @param listener * the new listener */ public void addPropertyChangeListener(PropertyChangeListener listener) { this.propertyChangeSupport.addPropertyChangeListener(listener); } /** * Remove the given listener from this command builder * @param listener * the listener to remove */ public void removePropertyChangeListener(PropertyChangeListener listener) { this.propertyChangeSupport.removePropertyChangeListener(listener); } /** * Setter for the zero-based LOD column index * @param lodColumnIndex * the lodColumnIndex to set */ public void setLodColumnIndex(int lodColumnIndex) { this.lodColumnIndex = lodColumnIndex; this.propertyChangeSupport.firePropertyChange( LOD_COLUMN_INDEX_PROPERTY_NAME, null, Integer.valueOf(lodColumnIndex)); } /** * Getter for the zero-based LOD column index * @return * the lodColumnIndex */ public int getLodColumnIndex() { return this.lodColumnIndex; } /** * Getter for the interval type. This effects which R function is used. * @return * the interval type */ public IntervalType getIntervalType() { return this.intervalType; } /** * Setter for the interval type * @param intervalType * the new value for the interval type */ public void setIntervalType(IntervalType intervalType) { synchronized(this) { this.intervalType = intervalType; } this.propertyChangeSupport.firePropertyChange( INTERVAL_TYPE_PROPERTY_NAME, null, intervalType); } /** * Getter for the scan one result that the command builder is using * @return * the scan one result that we're using */ public ScanOneResult getScanOneResult() { return this.scanOneResult; } /** * Setter for the scan one result that this command builder should use * @param scanOneResult * the new scan one result that we should use */ public void setScanOneResult(ScanOneResult scanOneResult) { this.scanOneResult = scanOneResult; this.propertyChangeSupport.firePropertyChange( SCAN_ONE_RESULT_PROPERTY_NAME, null, scanOneResult); } /** * Getter for the chromosome names that we should calculate * a credible interval for * @return * the chromosome names */ public String[] getChromosomeNames() { return this.chromosomeNames; } /** * Setter for the chromosome names that this command builder will * calculate credible intervals for * @param chromosomeNames * the chromosome names (zero based) */ public void setChromosomeNames(String[] chromosomeNames) { this.chromosomeNames = chromosomeNames; this.propertyChangeSupport.firePropertyChange( CHROMOSOME_NAMES_PROPERTY_NAME, null, chromosomeNames); } /** * Getter for the probability coverage that we should use for * the credible intervals * @return * the probability coverage to use (range is 0 to 1) */ public double getProbabilityCoverage() { return this.probabilityCoverage; } /** * Setter for the probability coverage that we should use for * the credible intervals * @param probabilityCoverage * the new probability coverage to use (range is 0 to 1) */ public void setProbabilityCoverage(double probabilityCoverage) { this.probabilityCoverage = probabilityCoverage; this.propertyChangeSupport.firePropertyChange( PROBABILITY_COVERAGE_PROPERTY_NAME, null, Double.valueOf(probabilityCoverage)); } /** * Getter for the lod drop parameter (only used when * the method is set to {@link IntervalType#LOD_SUPPORT}) * @return * the lod drop parameter value */ public double getLodDrop() { return this.lodDrop; } /** * Setter for the LOD drop parameter * @param lodDrop the lodDrop to set */ public void setLodDrop(double lodDrop) { this.lodDrop = lodDrop; this.propertyChangeSupport.firePropertyChange( LOD_DROP_PROPERTY_NAME, null, Double.valueOf(lodDrop)); } /** * Get the scanone intervals * @param rInterface * the R interface * @return * the scanone intervals */ public List<ScanOneInterval> getScanOneIntervals(RInterface rInterface) { List<RCommand> commands = this.getCommands(); if(commands == null) { return null; } else { List<ScanOneInterval> intervals = new ArrayList<ScanOneInterval>( commands.size()); for(RCommand command: commands) { // TODO this is pretty inefficient since we're invoking // the command 3 times REXP chromosomeExpression = rInterface.evaluateCommand( new SilentRCommand(command.getCommandText() + "$chr")); REXP positionCmExpression = rInterface.evaluateCommand( new SilentRCommand(command.getCommandText() + "$pos")); REXP lodExpression = rInterface.evaluateCommand( new SilentRCommand( command.getCommandText() + "[ , " + (this.lodColumnIndex + INTERVAL_RESULT_LOD_COLUMN_INDEX_OFFSET) + "]")); // convert expressions String[] chromosomeNames = JRIUtilityFunctions.extractStringArrayFromFactor( chromosomeExpression); double[] positionsInCm = positionCmExpression.asDoubleArray(); double[] lods = lodExpression.asDoubleArray(); if(chromosomeNames.length != 3 || positionsInCm.length != 3 || lods.length != 3) { LOG.warning( "expexted all interval arrays to be of length 3: " + "chromosomeNames=" + chromosomeNames.length + ", positionsInCm=" + positionsInCm.length + ", lods=" + lods.length); return null; } else { IntervalShape intervalShape = new IntervalShape( new IntervalPoint( positionsInCm[0], lods[0]), new IntervalPoint( positionsInCm[1], lods[1]), new IntervalPoint( positionsInCm[2], lods[2])); double intervalConstraint = this.intervalType == IntervalType.BAYESIAN_CREDIBLE ? this.probabilityCoverage : this.lodDrop; ScanOneInterval currInterval = new ScanOneInterval( this.intervalType, intervalShape, intervalConstraint, chromosomeNames[0]); intervals.add(currInterval); } } return intervals; } } /** * Get the list of commands that results from all of the properties * set on this builder. The commands will be returned in order * corresponding to the order of the chromosome names set in * {@link #setChromosomeNames(String[])} * @return * the list of commands (or null if we can't create a valid * command) */ public synchronized List<RCommand> getCommands() { List<RCommandParameter> commandsParamsWithoutChromosome = this.getCommandParametersWithoutChromosomeName(); List<RCommandParameter> chromosomeNameParameters = this.getChromosomeNameParameters(); if(commandsParamsWithoutChromosome != null && chromosomeNameParameters != null) { List<RCommand> rCommands = new ArrayList<RCommand>( chromosomeNameParameters.size()); for(RCommandParameter currChromoParam: chromosomeNameParameters) { List<RCommandParameter> currFullParameterList = new ArrayList<RCommandParameter>( commandsParamsWithoutChromosome); currFullParameterList.add(currChromoParam); String methodName = this.intervalType == IntervalType.BAYESIAN_CREDIBLE ? BAYESIAN_METHOD_NAME : LOD_METHOD_NAME; RCommand currCommand = new RMethodInvocationCommand( methodName, currFullParameterList); rCommands.add(currCommand); } return rCommands; } else { return null; } } /** * Get the command parameters without a chromosome name parameter (since we * can have many chromosome names) * @return * the parameters minus the chromosome names parameter */ private List<RCommandParameter> getCommandParametersWithoutChromosomeName() { List<RCommandParameter> commands = new ArrayList<RCommandParameter>(); ScanOneResult scanOneResult = this.scanOneResult; if(scanOneResult != null) { commands.add(new RCommandParameter( SCAN_ONE_RESULT_PARAMETER_NAME, scanOneResult.getAccessorExpressionString())); } else { return null; } IntervalType intervalType = this.intervalType; if(intervalType == IntervalType.BAYESIAN_CREDIBLE) { double probabilityCoverage = this.probabilityCoverage; if(probabilityCoverage > 0.0 && probabilityCoverage < 1.0) { commands.add(new RCommandParameter( PROBABILITY_COVERAGE_PARAMETER_NAME, Double.toString(probabilityCoverage))); } else { return null; } } else { double lodDrop = this.lodDrop; if(lodDrop > 0.0) { commands.add(new RCommandParameter( LOD_DROP_PARAMETER_NAME, Double.toString(lodDrop))); } else { return null; } } commands.add(new RCommandParameter( LOD_COLUMN_INDEX_PARAMETER_NAME, Integer.toString(this.lodColumnIndex + 1))); return commands; } /** * Each one of these parameters should be a part of a separate command. * @return * a list of chromosome name parameters */ private List<RCommandParameter> getChromosomeNameParameters() { String[] chromosomeNames = this.chromosomeNames; if(chromosomeNames != null && chromosomeNames.length > 0) { List<RCommandParameter> chromosomeNameParameters = new ArrayList<RCommandParameter>(chromosomeNames.length); for(String currChromosomeName: chromosomeNames) { chromosomeNameParameters.add(new RCommandParameter( CHROMOSOME_NAME_PARAMETER_NAME, RUtilities.javaStringToRString(currChromosomeName))); } return chromosomeNameParameters; } else { return null; } } }