All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hfg.automation.PlateLayout Maven / Gradle / Ivy

There is a newer version: 20240423
Show newest version
package com.hfg.automation;


import java.util.*;

import com.hfg.util.CompareUtil;
import com.hfg.util.StringUtil;
import com.hfg.util.collection.CollectionUtil;
import com.hfg.xml.XMLNode;
import com.hfg.xml.XMLTag;

//------------------------------------------------------------------------------
/**
 Rules/pattern for the allocation of samples or controls into a sample plate.
 
@author J. Alex Taylor, hairyfatguy.com
*/ //------------------------------------------------------------------------------ // com.hfg XML/HTML Coding Library // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library 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 // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // J. Alex Taylor, President, Founder, CEO, COO, CFO, OOPS hairyfatguy.com // [email protected] //------------------------------------------------------------------------------ public class PlateLayout implements Comparator, Comparable { private PlateType mPlateType; private String mPlateNamePrefix = sDefaultPlateNamePrefix; private Order mOrder = sDefaultOrder; private WellRef mFirstLocation = sDefaultFirstLocation; private Continuation mFirstLocationContinuation = sDefaultContinuation; private WellRef mLastLocation; private Continuation mLastLocationContinuation = sDefaultContinuation; private Set mPositiveControlLocations; private Set mNegativeControlLocations; private Set mBlankLocations; private Set mLocationsToKeepEmpty; private List mAllWellRefs; private List mRemainingRandomWellRefs; public static enum Order { byCol, byRow, byQuadrantColRow, byQuadrantRowCol, random } public static enum Continuation { firstPlateOnly, allPlates } private static Order sDefaultOrder = Order.byCol; private static WellRef sDefaultFirstLocation = new WellRef("A01"); private static Continuation sDefaultContinuation = Continuation.allPlates; private static String sDefaultPlateNamePrefix = "PLATE_"; //########################################################################### // CONSTRUCTORS //########################################################################### //--------------------------------------------------------------------------- public PlateLayout(PlateType inPlateType) { mPlateType = inPlateType; mLastLocation = new WellRef().setColIndex(mPlateType.getColumns()).setRowIndex(mPlateType.getRows()); } //-------------------------------------------------------------------------- public PlateLayout(XMLNode inXML) { inXML.verifyTagName(AutomationXML.PLATE_LAYOUT); mPlateType = (PlateType.valueOf(inXML.getAttributeValue(AutomationXML.PLATE_TYPE_ATT))); setPlateNamePrefix(inXML.getAttributeValue(AutomationXML.PLATE_NAME_PREFIX_ATT)); if (inXML.hasAttribute(AutomationXML.WELL_ORDER_ATT)) { setOrder(Order.valueOf(inXML.getAttributeValue(AutomationXML.WELL_ORDER_ATT))); } if (inXML.hasAttribute(AutomationXML.FIRST_WELL_ATT)) { setFirstSampleWellRef(new WellRef(inXML.getAttributeValue(AutomationXML.FIRST_WELL_ATT)), Continuation.valueOf(inXML.getAttributeValue(AutomationXML.CONTINUATION_ATT))); } if (inXML.hasAttribute(AutomationXML.LAST_WELL_ATT)) { setLastSampleWellRef(new WellRef(inXML.getAttributeValue(AutomationXML.LAST_WELL_ATT)), Continuation.valueOf(inXML.getAttributeValue(AutomationXML.CONTINUATION_ATT))); } XMLNode positiveControlWellsTag = inXML.getOptionalSubtagByName(AutomationXML.POSITIVE_CONTROL_WELLS); if (positiveControlWellsTag != null) { String[] wellRefs = positiveControlWellsTag.getContent().split("[\\s,]+"); for (String wellRef : wellRefs) { specifyPositiveControlWell(new WellRef(wellRef)); } } XMLNode negativeControlWellsTag = inXML.getOptionalSubtagByName(AutomationXML.NEGATIVE_CONTROL_WELLS); if (negativeControlWellsTag != null) { String[] wellRefs = negativeControlWellsTag.getContent().split("[\\s,]+"); for (String wellRef : wellRefs) { specifyNegativeControlWell(new WellRef(wellRef)); } } XMLNode blankWellsTag = inXML.getOptionalSubtagByName(AutomationXML.BLANK_WELLS); if (blankWellsTag != null) { String[] wellRefs = blankWellsTag.getContent().split("[\\s,]+"); for (String wellRef : wellRefs) { specifyBlankWell(new WellRef(wellRef)); } } XMLNode wellsToKeepEmptyTag = inXML.getOptionalSubtagByName(AutomationXML.WELLS_TO_KEEP_EMPTY); if (wellsToKeepEmptyTag != null) { String[] wellRefs = wellsToKeepEmptyTag.getContent().split("[\\s,]+"); for (String wellRef : wellRefs) { specifyWellToKeepEmpty(new WellRef(wellRef)); } } } //########################################################################### // PUBLIC METHODS //########################################################################### //-------------------------------------------------------------------------- public XMLNode toXMLNode() { XMLNode node = new XMLTag(AutomationXML.PLATE_LAYOUT); node.setAttribute(AutomationXML.PLATE_TYPE_ATT, getPlateType()); node.setAttribute(AutomationXML.PLATE_NAME_PREFIX_ATT, mPlateNamePrefix); node.setAttribute(AutomationXML.WELL_ORDER_ATT, getOrder()); if (getFirstSampleWellRef() != null) { node.setAttribute(AutomationXML.FIRST_WELL_ATT, getFirstSampleWellRef()); node.setAttribute(AutomationXML.CONTINUATION_ATT, mFirstLocationContinuation); } if (getLastSampleWellRef() != null) { node.setAttribute(AutomationXML.LAST_WELL_ATT, getLastSampleWellRef()); node.setAttribute(AutomationXML.CONTINUATION_ATT, mLastLocationContinuation); } if (CollectionUtil.hasValues(getPositiveControlWells())) { XMLNode subtag = new XMLTag(AutomationXML.POSITIVE_CONTROL_WELLS); subtag.setContent(StringUtil.join(getPositiveControlWells(), ", ")); node.addSubtag(subtag); } if (CollectionUtil.hasValues(getNegativeControlWells())) { XMLNode subtag = new XMLTag(AutomationXML.NEGATIVE_CONTROL_WELLS); subtag.setContent(StringUtil.join(getNegativeControlWells(), ", ")); node.addSubtag(subtag); } if (CollectionUtil.hasValues(getBlankLocations())) { XMLNode subtag = new XMLTag(AutomationXML.BLANK_WELLS); subtag.setContent(StringUtil.join(getBlankLocations(), ", ")); node.addSubtag(subtag); } if (CollectionUtil.hasValues(getWellsToKeepEmpty())) { XMLNode subtag = new XMLTag(AutomationXML.WELLS_TO_KEEP_EMPTY); subtag.setContent(StringUtil.join(getWellsToKeepEmpty(), ", ")); node.addSubtag(subtag); } return node; } //--------------------------------------------------------------------------- @Override public int hashCode() { int hashCode = getPlateType().hashCode(); hashCode += 31 * getOrder().hashCode(); if (getFirstSampleWellRef() != null) { hashCode += 31 * getFirstSampleWellRef().hashCode(); } if (getLastSampleWellRef() != null) { hashCode += 31 * getLastSampleWellRef().hashCode(); } // TODO: Use other fields return hashCode; } //--------------------------------------------------------------------------- @Override public boolean equals(Object inObj2) { return (0 == compareTo(inObj2)); } //--------------------------------------------------------------------------- @Override public int compareTo(Object inObj2) { int result = -1; if (inObj2 != null && inObj2 instanceof PlateLayout) { result = 0; if (this != inObj2) { PlateLayout plateLayout2 = (PlateLayout) inObj2; result = CompareUtil.compare(getPlateType(), plateLayout2.getPlateType()); if (0 == result) { result = CompareUtil.compare(getOrder(), plateLayout2.getOrder()); } if (0 == result) { result = CompareUtil.compare(getFirstSampleWellRef(), plateLayout2.getFirstSampleWellRef()); } if (0 == result) { result = CompareUtil.compare(getLastSampleWellRef(), plateLayout2.getLastSampleWellRef()); } // TODO: Use other fields } } return result; } //--------------------------------------------------------------------------- @Override public int compare(Well inWell1, Well inWell2) { int result = 0; if (! inWell1.equals(inWell2)) { result = compare(inWell1.getRef(), inWell2.getRef()); } return result; } //--------------------------------------------------------------------------- public int compare(WellRef inWellRef1, WellRef inWellRef2) { int result = 0; if (! inWellRef1.equals(inWellRef2)) { if (getOrder().equals(Order.byCol)) { result = CompareUtil.compare(inWellRef1.getRowIndex() + (inWellRef1.getColIndex() - 1) * getPlateType().getRows(), inWellRef2.getRowIndex() + (inWellRef2.getColIndex() - 1) * getPlateType().getRows()); } else if (getOrder().equals(Order.byRow)) { result = CompareUtil.compare(inWellRef1.getColIndex() + (inWellRef1.getRowIndex() - 1) * getPlateType().getColumns(), inWellRef2.getColIndex() + (inWellRef2.getRowIndex() - 1) * getPlateType().getColumns()); } } return result; } //--------------------------------------------------------------------------- public List allocateSamples(List inSamples, PlateFactory inPlateFactory) { List plates = null; T currentPlate = null; WellRef lastWellRef = null; if (CollectionUtil.hasValues(inSamples)) { plates = new ArrayList<>((inSamples.size() / getPlateType().size()) + 1); for (Comparable sample : inSamples) { if (null == currentPlate) { currentPlate = (T) inPlateFactory.createPlate(getPlateType()).setLayout(this).setName(mPlateNamePrefix + (plates.size() + 1)); plates.add(currentPlate); lastWellRef = null; } WellRef wellRef; if (null == lastWellRef && ! getOrder().equals(Order.random)) { if (! mFirstLocationContinuation.equals(Continuation.allPlates) && plates.size() > 1) { wellRef = sDefaultFirstLocation; } else { wellRef = getFirstSampleWellRef(); } } else { // Determine the next well to allocate wellRef = nextWellRef(lastWellRef); } currentPlate.allocateWell(wellRef).addSample(sample); if ((! mOrder.equals(Order.random) && getLastSampleWellRef() != null && wellRef.equals(getLastSampleWellRef()) && (mLastLocationContinuation.equals(Continuation.allPlates) || 1 == plates.size())) || currentPlate.isFull()) { currentPlate = null; if (mOrder.equals(Order.random)) { mRemainingRandomWellRefs = null; } } lastWellRef = wellRef; } } return plates; } //--------------------------------------------------------------------------- public PlateType getPlateType() { return mPlateType; } //--------------------------------------------------------------------------- public PlateLayout setPlateNamePrefix(String inValue) { mPlateNamePrefix = inValue; return this; } //--------------------------------------------------------------------------- public PlateLayout setOrder(Order inValue) { mOrder = inValue; if (mOrder.equals(Order.random)) { mFirstLocation = null; } return this; } //--------------------------------------------------------------------------- public Order getOrder() { return mOrder; } //--------------------------------------------------------------------------- public PlateLayout setFirstSampleWellRef(WellRef inValue, Continuation inContinuation) { mFirstLocation = inValue; mFirstLocationContinuation = inContinuation; return this; } //--------------------------------------------------------------------------- public WellRef getFirstSampleWellRef() { return mFirstLocation; } //--------------------------------------------------------------------------- public PlateLayout setLastSampleWellRef(WellRef inValue, Continuation inContinuation) { mLastLocation = inValue; mLastLocationContinuation = inContinuation; return this; } //--------------------------------------------------------------------------- public PlateLayout specifyWells(WellRange inValues, WellType inType) { switch (inType) { case EMPTY: if (null == mLocationsToKeepEmpty) { mLocationsToKeepEmpty = new HashSet<>(10); } mLocationsToKeepEmpty.addAll(Arrays.asList(inValues.toArray())); break; case BLANK: if (null == mBlankLocations) { mBlankLocations = new HashSet<>(10); } mBlankLocations.addAll(Arrays.asList(inValues.toArray())); break; case POSITIVE_CONTROL: if (null == mPositiveControlLocations) { mPositiveControlLocations = new HashSet<>(10); } mPositiveControlLocations.addAll(Arrays.asList(inValues.toArray())); break; case NEGATIVE_CONTROL: if (null == mNegativeControlLocations) { mNegativeControlLocations = new HashSet<>(10); } mNegativeControlLocations.addAll(Arrays.asList(inValues.toArray())); break; default: throw new RuntimeException(inType + " wells not yest supported for this method!"); } return this; } //--------------------------------------------------------------------------- public WellRef getLastSampleWellRef() { return mLastLocation; } //--------------------------------------------------------------------------- public PlateLayout specifyBlankWell(WellRef inValue) { if (null == mBlankLocations) { mBlankLocations = new HashSet<>(5); } mBlankLocations.add(inValue); return this; } //--------------------------------------------------------------------------- public Set getBlankLocations() { return mBlankLocations; } //--------------------------------------------------------------------------- public PlateLayout specifyWellToKeepEmpty(WellRef inValue) { if (null == mLocationsToKeepEmpty) { mLocationsToKeepEmpty = new HashSet<>(5); } mLocationsToKeepEmpty.add(inValue); return this; } //--------------------------------------------------------------------------- public Set getWellsToKeepEmpty() { return mLocationsToKeepEmpty; } //--------------------------------------------------------------------------- public PlateLayout specifyPositiveControlWell(WellRef inValue) { if (null == mPositiveControlLocations) { mPositiveControlLocations = new HashSet<>(5); } mPositiveControlLocations.add(inValue); return this; } //--------------------------------------------------------------------------- public Set getPositiveControlWells() { return mPositiveControlLocations; } //--------------------------------------------------------------------------- public PlateLayout specifyNegativeControlWell(WellRef inValue) { if (null == mNegativeControlLocations) { mNegativeControlLocations = new HashSet<>(5); } mNegativeControlLocations.add(inValue); return this; } //--------------------------------------------------------------------------- public Set getNegativeControlWells() { return mNegativeControlLocations; } //--------------------------------------------------------------------------- public List getWellRefs() { return getWellRefs(true); } //--------------------------------------------------------------------------- public List getWellRefs(boolean inReturnOnlySampleWells) { List wellRefs = new ArrayList<>(getPlateType().size()); WellRef wellRef = null; mRemainingRandomWellRefs = null; if (mOrder.equals(Order.random)) { wellRef = nextWellRef(null, inReturnOnlySampleWells); wellRefs.add(wellRef); } /* else { wellRef = getFirstSampleWellRef(); } wellRefs.add(wellRef); */ while ((wellRef = nextWellRef(wellRef, inReturnOnlySampleWells)) != null) { wellRefs.add(wellRef); if (mOrder.equals(Order.random) && ! CollectionUtil.hasValues(mRemainingRandomWellRefs)) { break; } } return wellRefs; } //--------------------------------------------------------------------------- public WellRef getWellRefFromIndex(int inIndex) { if (null == mAllWellRefs) { mAllWellRefs = getWellRefs(false); } return mAllWellRefs.get(inIndex); } //--------------------------------------------------------------------------- public ListIterator wellRefIterator() { return getWellRefs().listIterator(); } //--------------------------------------------------------------------------- private WellRef nextWellRef(WellRef inPrevWellRef) { return nextWellRef(inPrevWellRef, true); } //--------------------------------------------------------------------------- private WellRef nextWellRef(WellRef inPrevWellRef, boolean inReturnOnlySampleWells) { if (mOrder.equals(Order.random) && null == mRemainingRandomWellRefs) { mRemainingRandomWellRefs = new ArrayList<>(getPlateType().size()); for (int row = 1; row <= getPlateType().getRows(); row++) { for (int col = 1; col <= getPlateType().getColumns(); col++) { WellRef wellRef = new WellRef().setColIndex(col).setRowIndex(row); if ((null == mLocationsToKeepEmpty || ! mLocationsToKeepEmpty.contains(wellRef)) && (null == mBlankLocations || ! mBlankLocations.contains(wellRef)) && (null == mPositiveControlLocations || ! mPositiveControlLocations.contains(wellRef)) && (null == mNegativeControlLocations || mNegativeControlLocations.contains(wellRef))) { mRemainingRandomWellRefs.add(wellRef); } } } Collections.shuffle(mRemainingRandomWellRefs); } WellRef prevWellRef = inPrevWellRef; WellRef wellRef = null; while (null == wellRef) { if (mOrder.equals(Order.random)) { if (! CollectionUtil.hasValues(mRemainingRandomWellRefs)) { break; } wellRef = mRemainingRandomWellRefs.remove(0); } else { if (null == prevWellRef) { wellRef = getFirstSampleWellRef(); } else { int col = prevWellRef.getColIndex(); int row = prevWellRef.getRowIndex(); switch (mOrder) { case byCol: if (row == getPlateType().getRows()) { row = 1; col++; } else { row++; } break; case byRow: if (col == getPlateType().getColumns()) { col = 1; row++; } else { col++; } break; case byQuadrantColRow: if (row == getPlateType().getRows()/2) { if (col == getPlateType().getColumns()) { row++; col = 1; } else { row = 1; col++; } } else if (row == getPlateType().getRows()) { row = 1 + getPlateType().getRows()/2; col++; } else if (col == getPlateType().getColumns() && row == getPlateType().getRows()/2) { row++; col = 1; } else { row++; } break; case byQuadrantRowCol: if (col == getPlateType().getColumns()/2) { if (row == getPlateType().getRows()/2 || row == getPlateType().getRows()) { row -= getPlateType().getRows()/2 - 1; col++; } else { row++; col = 1; } } else if (col == getPlateType().getColumns()) { if (row == getPlateType().getRows()/2) { col = 1; } else { col = 1 + getPlateType().getColumns() / 2; } row++; } else { col++; } break; } if (col > getPlateType().getColumns() || row > getPlateType().getRows()) { break; } wellRef = new WellRef().setColIndex(col).setRowIndex(row); } // Was this location reserved? if (inReturnOnlySampleWells) { if ((mLocationsToKeepEmpty != null && mLocationsToKeepEmpty.contains(wellRef)) || (mBlankLocations != null && mBlankLocations.contains(wellRef)) || (mPositiveControlLocations != null && mPositiveControlLocations.contains(wellRef)) || (mNegativeControlLocations != null && mNegativeControlLocations.contains(wellRef))) { prevWellRef = wellRef; wellRef = null; } } } } return wellRef; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy