//===================================================================== // File: Bin.java // Class: Bin // 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; import java.util.Hashtable; import java.util.Enumeration; /** * This class represents a bin, which is simply a region defined in a gel. * It also contains a ScoreFunction. This is responsible for * interpreting the data inside of the bin. The bin also contains the * label assigned to each lane by the score function. * * @see ScoreFunction * * @author James J. Benham * @version 1.0.0 * @date August 10, 1998 */ public class Bin extends SortableData { private static double DEFAULT_RANGE = 2; private double location; private double range; private String name; private String scoreInfo[]; private Hashtable scoreLookup; private ScoreFunction scoreMethod; private boolean scored; /** * Creates a new bin with the specified parameters. * * @param loc the location, in bp, of the bin on the gel. */ public Bin(double loc) { this(loc, DEFAULT_RANGE); } /** * Creates a new bin with the specified parameters. * * @param loc the location, in bp, of the bin on the gel. * @param range the range of the bin, agan in bp. */ public Bin(double loc, double range) { this(loc, range, new SegregatingScore() ); } /** * Creates a new bin with the specified parameters. * * @param loc the location, in bp, of the bin on the gel. * @param range the range of the bin, agan in bp. * @param score the method used to score the bin. */ public Bin(double loc, double range, ScoreFunction score) { location = loc; this.range = range; scoreMethod = score; name = ""; scoreLookup = new Hashtable(30); scored = false; } /** * Gives the value used to search/sort bins. * * @return the bins location */ public final double getSearchKey() { return location; } /** * Gives the method used to score a bin. That is to say, how the bin * interprets the presense/absence/other of peaks in a bin. For example, * one could call all those with a peak 'A' and those without 'B'. The * score method could count and return the number of 'A's and the number * of 'B's. * * @return The method used to score the bin. * * @see Bin#setScoreMethod */ public ScoreFunction getScoreMethod() { return scoreMethod; } /** * Sets the scoring method to the one specified. * * @param scorer The method to use on this bin. * * @see Bin#getScoreMethod */ public void setScoreMethod(ScoreFunction scorer) { scoreMethod = scorer; scored = false; } /** * Scores this bin using the scoring method set and the lanes specified. * * @param lanes the lanes to include in the bin. */ public void score(DataList lanes) { if(scoreMethod == null) throw new MissingParameterError("No score method set!"); double min = location - range; double max = location + range; // determine if we should call the peaks again. // should also determine if the peaks should be cleared first, to // remove old peaks. Removing can also be handled by the peak // locating algorithm. boolean reLabel = true; if(reLabel) { // Retrieve the default method. PeakLocate peakFind = ((PeakLocate) FeatureList.getPeakMgr().getDefault()); peakFind.findPeak(lanes, min, max); } // ===============Now score each one================= Lane ln; String label; for(int i=0; i < lanes.size(); i++) { ln = (Lane) lanes.dataAt(i); label = scoreMethod.score(ln.cutoffUnder(max), ln.getPeaksInRange(min, max)); scoreLookup.put(ln, label); } scoreOverall(lanes); scored = true; } /** * Sets the overall scoring information for the bin. The manner in * which each lane was scored is given to the score method currently * set. It also gives a list of all of the peaks of interest to * the scoring method. "Of interest" is defined by the score function * by marking certain peaks. For example, the score method could count * the number of A's and B's as well as provide a mean for the peak * location. * * @param lanes the lanes to include in the overall scoring of this bin * * @see Bin#getScoreInfo * @see ScoreFunction * @see Peak#isMarked */ public void scoreOverall(DataList lanes) { double min = location - range; double max = location + range; //===========Make a list of all the interesting peaks========== DataList markedPeaks = new DataList(); DataList peaks; Peak pk; for(int i=0; i < lanes.size(); i++) { peaks = ((Lane) lanes.dataAt(i)).getPeaksInRange(min, max); for(int j=0; j < peaks.size(); j++) { pk = (Peak) peaks.dataAt(j); if(pk.isMarked()) markedPeaks.addData(pk); } } //============= Change all of the scores into an array ========= String labels[]; // the names in an array // Get the names from the Hashtable Enumeration namesEnum = scoreLookup.elements(); // Create an array of the approriate size labels = new String[scoreLookup.size()]; // Copy the names from the Enumeration into the array for(int i=0; i < scoreLookup.size(); i++) { labels[i] = (String) namesEnum.nextElement(); } scoreInfo = scoreMethod.getInfo(labels, markedPeaks); } /** * Gives the string representing how the given lane was scored. * * @param ln the lane to retrieve the score for * * @return the score found by the scoring method * * @see Bin#getScoreMethod * @see Bin#setScoreMethod */ public String getScore(Lane ln) { // do some error checking here? return (String) scoreLookup.get(ln); } /** * Changes the scoring for the specified lane. Only the value in the * table is changed by this method, so in future, it could be * replaced if the table is changed and it will not affect any of the * peaks. Appropriate values can be retrieved from the * ScoreFuction set for this bin. * * @param ln the lane to change * @param value the new score for the lane, which should be one of the * choices offered by the current score method. * * @see Bin#getScoreMethod * @see ScoreFunction#getChoices */ public void setScore(Lane ln, String value) { scoreLookup.put(ln, value); } /** * Gives the overall score info for the bin. The information is * determined from from the peaks used to score the bin and the label * assigned to each lane in the bin. Each entry in the array should * be a separate line. The informatin could be as simple as the * number of lanes, or it could be something like the mean location * of all the peaks in the bin. * * @return an array containing information on the bin scoring. * * @see ScoreFunction * @see Bin#scoreOverall */ public String[] getScoreInfo() { return scoreInfo; } /** * Used to tell whether or not the score method has been * called on this bins current configuration. For example, if the bin * is moved, it needs to be rescored. * * @return true if the bin still has a valid scoring. */ public boolean isScored() { return scored; } /** * Sets the bin so that the current scoring is either accepted as valid * or invalidated. * * @param scored true if the current scoring is to be valid. */ public void setScore(boolean scored) { this.scored = scored; } /** * Gives the position of this bin in the gel. * * @return the location specified in bp. */ public double getLocation() { return location; } /** * Sets the position/location of this bin to the one specified. * * @param location the new position for the bin */ public void setLocation(double location) { this.location = location; scored = false; // invalidate scoring } /** * Moves the bin to a new location based on it's present location. * * @param adjustment the distance to move the bin, which may be either * positive or negative. */ public void adjustLocation(double adjustment) { location = location + adjustment; scored = false; // invalidate scoring } /** * Gives the range of the bin. The range of the bin is the distance from * the location to the edge of the bin. Therefore, the bin is 2*range * bp wide. * * @return the range as described above */ public double getRange() { return range; } /** * Sets the range of the bin to the specified value. The range of the bin * is the distance from the location to the edge of the bin. Therefore, the * bin is 2*range bp wide. * * @param range the new value for the range as described above */ public void setRange(double range) { this.range = range; scored = false; // invalidate scoring } /** * Changes the range relative to the current value of the range. The range * of the bin is the distance from the location to the edge of the bin. * Therefore, the bin is 2*range bp wide. * * @param adjustment the value to increment the range by. This value * can be either positive or negative. */ public void adjustRange(double adjustment) { range = range + adjustment; scored = false; // invalidate scoring } /** * Sets the name of the bin to the specified value. By default, a bin * has a name of "", the empty String. * * @param name the new name for the bin. */ public void setName(String name) { this.name = name; } /** * Gives the name of this bin. By default, a bin has a name of "", the * empty String. In this case, the location of the bin is a much better * identifier. * * @return the name of the bin. */ public String getName() { return name; } /** * Produces a new bin identical to this one, except it has not been * scored. * * @return a new bin with the same properties as this one. */ public Object clone() { Bin temp = new Bin(location, range, scoreMethod); temp.setName(name); return temp; } /** * 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(location); out.writeDouble(range); out.writeUTF(name); out.writeBoolean(scored); out.writeUTF(scoreMethod.getName()); scoreMethod.write(out); // Only write the scoring info out when the bin has been scored, // otherwise we're writing worthless info. if(scored) { // write out the score info if(scoreInfo == null) out.writeInt(0); else { out.writeInt(scoreInfo.length); for(int i=0; i < scoreInfo.length; i++) out.writeUTF(scoreInfo[i]); } //get keys from the hashtable by retrieving the values and then // maing pairs. out.writeInt(scoreLookup.size()); java.util.Enumeration keys = scoreLookup.keys(); Object key; for(int i=0; i < scoreLookup.size(); i++) { key = keys.nextElement(); // writes out the lane index out.writeInt(key.hashCode()); // Now write out the value for that lane out.writeUTF( (String) scoreLookup.get(key)); } } } /** * 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. * *

Warning: this method cannot read in the scoring values * because of the way that they are stored, specifically by lane. * However, the lanes are not available to this method, so the information * cannot be stored. A class calling this method should make provisions * to take care of the rest of the data stream. The method * setScore can be used to set the value, it takes a * lane value pair and records it. The stream will be left at the * start of the values. The first thing will be an integer giving the * number of entries stored in the stream. It will be followed by * pairs of laneIndexies and values. The laneIndexies are integers and * the values are UTF-encoded Strings. * * @param in the input stream with the data for the class. * * @exception IOException occurs when a problem is encountered when * writing to the stream. * * @see Bin#setScore */ public void read(DataInputStream in) throws IOException { location = in.readDouble(); range = in.readDouble(); name = in.readUTF(); scored = in.readBoolean(); String scoreName = in.readUTF(); try { scoreMethod = (ScoreFunction) FeatureList.getScoreMgr().get(scoreName); scoreMethod = (ScoreFunction) scoreMethod.clone(); scoreMethod.read(in); }catch(java.util.NoSuchElementException e) { throw new IOException("The scoring method " + scoreName + " could not" + " be found."); } // if the bin isn't scored, then this stuff wasn't written because it // would have been worthless, so obviously we don't need to read it in. if(scored) { int length = in.readInt(); if(length == 0) scoreInfo = null; else { scoreInfo = new String[length]; for(int i=0; i < length; i++) scoreInfo[i] = in.readUTF(); } // read in the hashtable pairs. // This can't be done here because we need the lane object to act // as a key. We don't have the lanes. Just rely on the calling // class to take care of it. Yuck! // We can make a new hashtable though. We wouldn't want to mix // old stuff with new scoreLookup = new Hashtable(30); } else { // if it's not scored, set stuff scoreInfo = null; scoreLookup = new Hashtable(30); } } }