//===================================================================== // File: Cutoff.java // Class: Cutoff // Package: AFLPcore // // Author: James J. Benham // Date: August 10, 1998 // Contact: james_benham@hmc.edu // // Genographer v1.0 - Computer assisted scoring of gels. // Copyright (C) 1998 Montana State University // // 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; version 2 // of the License. // // 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. // // The GNU General Public License is distributed in the file GPL //===================================================================== package AFLPcore; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; /** * This class represents different cutoffs for a lane. This is used by * ScoreFunction classes to determine how to score things. * Each cutoff consists of a starting position (in bp) and a number * of levels. Each level can be a different CutoffFunction. * The levels must have an order. The smallest levels should be those with the * smallest cutoff value. Since some functions are not linear, the ordering * will be based at the cutoff value at the start position. * * @see ScoreFunction * @see CutoffFunction * @see Lane * * @author James J. Benham * @version 1.0.0 * @date August 10, 1998 */ public class Cutoff extends SortableData implements Cloneable { public final static int AREA = 0; public final static int HEIGHT = 1; private int cutoffMode; private int numLevels; private double startPos; private CutoffFunction[] cutoffs; /** * Creates a new cutoff with the specified number of levels and starting * at the specified position. * * @param startPos the size in bp at which the cutoff first applies * @param numLevels the number of different cutoff supported by this level */ public Cutoff(double startPos, int numLevels) { this.startPos = startPos; this.numLevels = numLevels; cutoffMode = HEIGHT; cutoffs = new CutoffFunction[numLevels]; } /** * Gives the starting position for this cutoff. * * @return the start size, in bp. */ public final double getSearchKey() { return startPos; } /** * Gives the starting position for this cutoff. * * @return the start size, in bp. */ public final double getStartPos() { return startPos; } /** * Adjusts the starting position. Warning: if this is in a sorted * list, this must be used with caution since it could alter the order * that the elements should be in, but does not rearrange any list. */ public void setStartPos(double newPosition) { startPos = newPosition; } /** * Gives the number of different cutoff levels supported by this object. * Each level can be associated with different confidences. For example, * something above the first cutoff level would most likely be a peak, * but something over only the second cutoff level might be a peak. * * @return The maximum number of levels that can be supported. */ public int getNumLevels() { return numLevels; } /** * Sets the cutoff so that it applies either to peak height or to peak * area. The values for these are constants in this class. * * @param modeSelector either AREA or HEIGHT * * @exception IllegalArgumentException occurs if modeSelector * isn't one of the accepted values. */ public void setMode(int modeSelector) { if( (modeSelector != AREA) && (modeSelector != HEIGHT)) throw new IllegalArgumentException("Invalid cutoff mode."); cutoffMode = modeSelector; } /** * Gives the current cutoff mode, either height or area. * * @return either AREA or HEIGHT */ public int getMode() { return cutoffMode; } /** * Gives the cutoff function for the specified level. * * @param level the confidence level, must be >= 0 and < numLevels. * * @return the function used at the given level. * * @exception IllegalArgumentException occurs if level fails to * meet the above. */ public CutoffFunction getCutoffFunction(int level) { if( (level < 0) || (level >= numLevels)) throw new IllegalArgumentException("Cannot retrieve cutoff because " + "specified level is invalid: " + level); return cutoffs[level]; } /** * Sets the cutoff function for the specified level. * * @param func the function used to determine the cutoff. * @param level the confidence level, must be >= 0 and < numLevels. * * @exception IllegalArgumentException occurs if level fails to * meet the above. */ public void setCutoffFunction(CutoffFunction func, int level) { if( (level < 0) || (level >= numLevels)) throw new IllegalArgumentException("Cannot set cutoff function because"+ "specified level is invalid: " + level); cutoffs[level] = func; } /** * Gives the value for the height/area above which all peaks should * be counted with a confidence relative to the level. * * @param size the size in bp to determine the cutoff value for. * @param level the confidence level, must be >= 0 and < numLevels. * * @exception IllegalArgumentException occurs if level fails to * meet the above. */ public double getCutoff(double size, int level) { if( (level < 0) || (level >= numLevels)) throw new IllegalArgumentException("Cannot get cutoff value because " + "specified level is invalid: " + level); return (cutoffs[level].getCutoff(size)); } /** * Adds the specified function to this cutoff. Therefore, the number of * levels in the cutoff is increased accordingly. The new function * is added at a level so that the level ordering, lower values are * at lower levels, is maintained. * * @param newFunction the function to add to this cutoff, it's height * should already be set to the desired value. */ public void addFunction(CutoffFunction newFunction) { // find out where the new function belongs int index = 0; while( (index < numLevels) && (newFunction.getCutoff(startPos) > cutoffs[index].getCutoff(startPos))) index++; numLevels++; CutoffFunction newCutoffs[] = new CutoffFunction[numLevels]; // copy the stuff over. for(int i=0; i < index; i++) newCutoffs[i] = cutoffs[i]; newCutoffs[index] = newFunction; for(int i = (index+1); i < numLevels; i++) newCutoffs[i] = cutoffs[i-1]; cutoffs = newCutoffs; } /** * Removes the specified level from this cutoff. The number of levels in * the cutoff is decreased accordingly. * * @param level the level to remove. */ public void deleteFunction(int level) { numLevels--; CutoffFunction newCutoffs[] = new CutoffFunction[numLevels]; for(int i=0; i < level; i++) newCutoffs[i] = cutoffs[i]; for(int i= (level + 1); i < (numLevels + 1); i++) newCutoffs[i-1] = cutoffs[i]; cutoffs = newCutoffs; } /** * Gives an object where all of the data is the same as this one. * * @return a copy of this object. */ public Object clone() { Cutoff ct = new Cutoff(startPos, numLevels); ct.setMode(cutoffMode); for(int i=0; i < numLevels; i++) ct.setCutoffFunction((CutoffFunction) (cutoffs[i].clone()), i); return ct; } /** * Writes all of the information this class needs to store in order * to be recreated. This will be used for things like storing the * class in a file. Therefore, the write should output enough information * so that read can recreate the essential properties of this * class. * * @param out the destination to write the data to. * * @exception IOException occurs when a problem is encountered when * writing to the stream. */ public void write(DataOutputStream out) throws IOException { out.writeDouble(startPos); out.writeInt(cutoffMode); out.writeInt(numLevels); for(int i=0; i < numLevels; i++) { out.writeUTF(cutoffs[i].getName()); cutoffs[i].write(out); } } /** * Reads in the properties of this class from the specified input stream. * The stream data should have been created by write. This * will retrieve this classes state from the input stream. All of the * current data in this class will be replaced by the data from the * stream. * * @param in the input stream with the data for the class. * * @exception IOException occurs when a problem is encountered when * writing to the stream. */ public void read(DataInputStream in) throws IOException { startPos = in.readDouble(); cutoffMode = in.readInt(); numLevels = in.readInt(); cutoffs = new CutoffFunction[numLevels]; String name; CutoffFunction ctfn = new LinearCutoff(); for(int i=0; i < numLevels; i++) { name = in.readUTF(); try{ ctfn = (CutoffFunction) FeatureList.getCutoffMgr().get(name); } catch(java.util.NoSuchElementException e) { throw new IOException("Could not get cutoff function named " + name); } ctfn = (CutoffFunction) ctfn.clone(); ctfn.read(in); cutoffs[i] = ctfn; } } }