com.github.jessemull.microflexbigdecimal.plate.WellPrecursor Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of microflex-bigdecimal Show documentation
Show all versions of microflex-bigdecimal Show documentation
Microplate library for parsing wet lab data.
The newest version!
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/* ---------------------- Package ----------------------- */
package com.github.jessemull.microflexbigdecimal.plate;
/* -------------------- Dependencies -------------------- */
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import com.google.common.base.Preconditions;
/**
* This class represents a well in a microplate. It contains the the logic to
* convert row letters to integers and vice-versa, enforces the correct format
* for well IDs and holds a list of data set values. The well object does not
* check for wells outside a specified range. This logic is housed within the
* plate object.
*
* All the classes in the microplate library are designed to be flexible in
* order to accommodate data in a variety of formats. The well object constructor
* accepts well IDs in each of the following formats:
*
*
* Row
* Column
* Input
*
* Integer
* Integer
* Row = 1 Column = 2
*
*
* String
* Integer
* Row = "1" Column = 2
*
*
* String
* Integer
* Row = "A" Column = 2
*
*
* Integer
* String
* Row = 1 Column = "2"
*
*
* String
* String
* Row = "A" Column = "2"
*
*
* String
* String
* Row = "1" Column = "2"
*
*
* String
* String
* "A2" Input must be [A-Za-z]+[0-9]+
*
*
*
* The Well class also implements both hash code and equals functions in order to
* prevent duplicate wells within a single plate object.
*
* The well constructor is passed a flag holding the numerical data type. Once
* set, the numerical data type cannot be changed. The MicroFlex library supports
* wells containing all primitive numerical types for input and output as well
* as two immutable types: BigDecimal and BigInteger.
*
* @author Jesse L. Mull
* @update Updated Oct 18, 2016
* @address http://www.jessemull.com
* @email [email protected]
*/
public abstract class WellPrecursor implements Comparable> {
/* --------------------------- Public Fields ---------------------------- */
public static final int DOUBLE = 0;
public static final int INTEGER = 1;
public static final int BIGDECIMAL = 2;
public static final int BIGINTEGER = 3;
/* --------------------------- Private Fields --------------------------- */
private int row; // Well row
private int column; // Well column
private int type; // Numerical data type
private int ALPHA_BASE = 26; // Number of char types available for the row ID
private String digits = "\\d+$"; // Numerals only regex
private String letters = "^[A-Z]+"; // Letters only regex
private String alphaOnly = "^[A-Za-z]+[0-9]+$"; // Alphanumeric characters only
private Pattern digitsPattern = Pattern.compile(digits); // Numeral only pattern
private Pattern lettersPattern = Pattern.compile(letters); // Letters only pattern
private Pattern alphaOnlyPattern = Pattern.compile(alphaOnly); // Alphanumeric characters only pattern
/* ---------------------------- Constructors ---------------------------- */
/**
* Creates a new Well object from row and column integers.
* @param int the well row
* @param int the well column
*/
public WellPrecursor(int type, int row, int column) {
this.validateType(type);
this.validateIndices(row, column);
this.type = type;
this.row = row;
this.column = column;
}
/**
* Creates a new Well object using the numerical type flag, row string and
* column number.
* @param int the numerical data type
* @param String the well row
* @param int the well column
*/
public WellPrecursor(int type, String row, int column) {
try {
this.row = Integer.decode(row);
} catch(NumberFormatException e) {
this.row = parseRow(row);
}
this.column = column;
this.type = type;
validateIndices(this.row, this. column);
}
/**
* Creates a new Well object using the numerical type flag, row number and
* column string.
* @param int the numerical data type
* @param int the well row
* @param String the well column
* @throws IllegalArgumentException invalid column value
*/
public WellPrecursor(int type, int row, String column) {
this.row = row;
this.type = type;
try {
this.column = Integer.decode(column);
} catch(Exception e) {
throw new IllegalArgumentException("Illegal column value: " + column);
}
validateIndices(this.row, this. column);
}
/**
* Creates a new Well object using the numerical type flag, row and column
* strings.
* @param int the numerical data type
* @param String the well row
* @param String the well column
* @throws IllegalArgumentException invalid column value
*/
public WellPrecursor(int type, String row, String column) {
try {
this.row = Integer.decode(row);
} catch(NumberFormatException e) {
this.row = parseRow(row);
}
try {
this.column = Integer.decode(column);
} catch(Exception e) {
throw new IllegalArgumentException("Illegal column value: " + column);
}
this.type = type;
validateIndices(this.row, this. column);
}
/**
* Creates a new Well object from a string holding the column and row values.
* The string must be in the format [a-ZA-Z]+[0-9]+
* @param int the numerical data type
* @param String the well index
*/
public WellPrecursor(int type, String wellID) {
this.type = type;
String upper = wellID.toUpperCase().trim();
Matcher digitsMatcher = digitsPattern.matcher(upper);
Matcher lettersMatcher = lettersPattern.matcher(upper);
Matcher alphasOnlyMatcher = alphaOnlyPattern.matcher(upper);
/* Alphas matcher enforces the correct format for the well ID */
if(alphasOnlyMatcher.find()) {
lettersMatcher.find();
digitsMatcher.find();
this.row = parseRow(lettersMatcher.group(0).trim());
try {
this.column = Integer.decode(digitsMatcher.group(0));
} catch(NumberFormatException e) {
throw new IllegalArgumentException("Invalid column ID: " + digitsMatcher.group(0).trim());
}
} else {
throw new IllegalArgumentException("Invalid well index: " + wellID);
}
validateIndices(this.row, this. column);
}
/**
* Clones a well without invoking clone.
* @param WellPrecursor the well to clone
*/
public WellPrecursor(WellPrecursor well) {
this.type = well.type();
this.row = well.row();
this.column = well.column();
}
/* --------------------------- Private Methods -------------------------- */
/**
* Converts a row ID to an integer value.
* @param String the row as a string
* @return the row as an integer value
*/
private int parseRow(String rowString) {
int rowInt = 0;
int baseIndex = 1;
String upper = rowString.toUpperCase().trim();
Matcher lettersMatcher = lettersPattern.matcher(upper);
if(lettersMatcher.find()) {
String letters = lettersMatcher.group(0);
rowInt = letters.charAt(letters.length() - 1) - 65;
for(int i = letters.length() - 2; i >= 0; i--) {
rowInt += (letters.charAt(i) - 65 + 1) * Math.pow(ALPHA_BASE, baseIndex++);
}
return rowInt;
} else {
throw new IllegalArgumentException("Invalid row ID: " + row);
}
}
/**
* Returns the well index as a letter(s) number pair.
* @return the index
*/
public String index() {
return this.rowString() + this.column;
}
/**
* Returns the numerical data type as an integer value.
* @return numerical data type integer
*/
public int type() {
return this.type;
}
/**
* Returns the numerical data type as a String.
* @return numerical data type String
*/
public String typeString() {
switch(this.type) {
case 0: return "Double";
case 1: return "Integer";
case 2: return "BigDecimal";
case 3: return "BigInteger";
default: return "Undefined numerical data type.";
}
}
/**
* Returns the column number.
* @return the column number
*/
public int column() {
return this.column;
}
/**
* Returns the row number.
* @return the row number
*/
public int row() {
return this.row;
}
/**
* Returns the alpha base value.
* @return the alpha base value
*/
public int alphaBase() {
return this.ALPHA_BASE;
}
/**
* Returns the row ID.
* @return row ID
*/
public String rowString() {
int rowInt = this.row;
String rowString = "";
while (rowInt >= 0) {
rowString = (char) (rowInt % ALPHA_BASE + 65) + rowString;
rowInt = (rowInt / ALPHA_BASE) - 1;
}
return rowString;
}
/**
* Returns row ID plus column number.
* @return row ID plus column number
*/
public String toString() {
String array = "[";
int index;
for(index = 0; index < this.data().size() - 1; index++) {
array += this.data().get(index) + ", ";
}
array += this.data().get(index) + "]";
return rowString() + this.column + " " + array;
}
/**
* Checks that the row and column integer values have a positive sign.
* @param int the row value
* @param int the column value
*/
private void validateIndices(int row, int column) {
Preconditions.checkArgument(row >= 0, "Invalid row index: %s. Row value must be a positive value.", row);
Preconditions.checkArgument(column > 0, "Invalid column index: %s. Column value must be greater than zero.", column);
}
/**
* Checks type flag for invalid values.
* @param int the numerical data type
*/
private void validateType(int type) {
if(type < 0 || type > 3) {
throw new IllegalArgumentException("Invalid numerical data type: " + type + ".");
}
}
/**
* Hash code uses the row, column and ALPHA_BASE fields.
* @return the hash code
*/
public int hashCode() {
return new HashCodeBuilder(17, 37).
append(this.row).
append(this.column).
append(this.ALPHA_BASE).
toHashCode();
}
/**
* Wells are ordered based on row and column number.
* @param WellPrecursor the object for comparison
* @return this == well --> 0
* this > well --> 1
* this < well --> -1
*/
public int compareTo(WellPrecursor well) throws ClassCastException {
if(this.equals(well)) {
return 0;
}
if(this.row() > well.row()) {
return 1;
} else if(this.row() != well.row()) {
return -1;
}
if(this.column() > well.column()) {
return 1;
} else if(this.column() != well.column()) {
return -1;
}
return 0;
}
public abstract List data();
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy