//=====================================================================
// 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;
}
}
}