package edu.ualberta.med.biobank.model; import java.text.MessageFormat; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Table; import javax.persistence.Transient; import edu.ualberta.med.biobank.common.exception.BiobankCheckException; import edu.ualberta.med.biobank.common.util.RowColPos; import edu.ualberta.med.biobank.common.wrappers.ContainerLabelingSchemeWrapper; // TODO: should be an enum? Maybe make types that require java code, but put parameters and names into the database? @Entity @Table(name = "CONTAINER_LABELING_SCHEME") public class ContainerLabelingScheme extends AbstractBiobankModel { private static final long serialVersionUID = 1L; public static final String SBS_ROW_LABELLING_PATTERN = "ABCDEFGHIJKLMNOP"; public static final String CBSR_2_CHAR_LABELLING_PATTERN = "ABCDEFGHJKLMNPQRSTUVWXYZ"; public static String BOX81_LABELLING_PATTERN = "ABCDEFGHJ"; //$NON-NLS-1$ public static final String TWO_CHAR_LABELLING_PATTERN = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; //$NON-NLS-1$ private String name; private Integer minChars; private Integer maxChars; private Integer maxRows; private Integer maxCols; private Integer maxCapacity; @Column(name = "NAME", length = 50, unique = true) public String getName() { return this.name; } public void setName(String name) { this.name = name; } @Column(name = "MIN_CHARS") public Integer getMinChars() { return this.minChars; } public void setMinChars(Integer minChars) { this.minChars = minChars; } @Column(name = "MAX_CHARS") public Integer getMaxChars() { return this.maxChars; } public void setMaxChars(Integer maxChars) { this.maxChars = maxChars; } @Column(name = "MAX_ROWS") public Integer getMaxRows() { return this.maxRows; } public void setMaxRows(Integer maxRows) { this.maxRows = maxRows; } @Column(name = "MAX_COLS") public Integer getMaxCols() { return this.maxCols; } public void setMaxCols(Integer maxCols) { this.maxCols = maxCols; } @Column(name = "MAX_CAPACITY") public Integer getMaxCapacity() { return this.maxCapacity; } public void setMaxCapacity(Integer maxCapacity) { this.maxCapacity = maxCapacity; } /** * Get the string corresponding to the given RowColPos and using the SBS * standard. 2:1 will return C2. */ public static String rowColToSbs(RowColPos rcp) { return "" + SBS_ROW_LABELLING_PATTERN.charAt(rcp.getRow()) //$NON-NLS-1$ + (rcp.getCol() + 1); } /** * Convert a position in row*column to two letter (in the CBSR way) * * @throws Exception * * @throws BiobankCheckException */ public static String rowColToCbsrTwoChar(RowColPos rcp, int totalRows, int totalCols) { int pos1, pos2, index; int lettersLength = CBSR_2_CHAR_LABELLING_PATTERN.length(); if (totalRows == 1) { index = rcp.getCol(); } else if (totalCols == 1) { index = rcp.getRow(); } else { index = totalRows * rcp.getCol() + rcp.getRow(); } pos1 = index / lettersLength; pos2 = index % lettersLength; if (pos1 >= 0 && pos2 >= 0) { return String.valueOf(CBSR_2_CHAR_LABELLING_PATTERN.charAt(pos1)) + String.valueOf(CBSR_2_CHAR_LABELLING_PATTERN.charAt(pos2)); } return null; } /** * Convert a position in row*column to two char numeric. */ public static String rowColToTwoCharNumeric(RowColPos rcp, int totalRows) { return String.format("%02d", rcp.getRow() + totalRows * rcp.getCol() //$NON-NLS-1$ + 1); } /** * Convert a position in row*column to Dewar labelling (AA, BB, CC...). */ public static String rowColToDewar(RowColPos rcp, Integer colCapacity) { int pos = rcp.getCol() + (colCapacity * rcp.getRow()); String letter = String.valueOf(SBS_ROW_LABELLING_PATTERN.charAt(pos)); return letter + letter; } /** * Get the string corresponding to the given RowColPos and using the SBS * standard. 2:1 will return C2. */ private static String rowColtoCbsrSbs(RowColPos rcp) { return "" + BOX81_LABELLING_PATTERN.charAt(rcp.getRow()) //$NON-NLS-1$ + (rcp.getCol() + 1); } /** * Convert a position in row*column to two letter (in the CBSR way) * * @throws Exception * * @throws BiobankCheckException */ public static String rowColToTwoChar(RowColPos rcp, int totalRows, int totalCols) { int pos1, pos2, index; int lettersLength = TWO_CHAR_LABELLING_PATTERN.length(); if (totalRows == 1) { index = rcp.getCol(); } else if (totalCols == 1) { index = rcp.getRow(); } else { index = totalRows * rcp.getCol() + rcp.getRow(); } pos1 = index / lettersLength; pos2 = index % lettersLength; if (pos1 >= 0 && pos2 >= 0) { return String.valueOf(TWO_CHAR_LABELLING_PATTERN.charAt(pos1)) + String.valueOf(TWO_CHAR_LABELLING_PATTERN.charAt(pos2)); } return null; } /** * Get the 2 char string corresponding to a RowColPos position given the * container capacity */ public static String getPositionString(RowColPos rcp, Integer childLabelingSchemeId, Integer rowCapacity, Integer colCapacity) { switch (childLabelingSchemeId) { case 1: // SBS standard return rowColToSbs(rcp); case 2: // CBSR 2 char alphabetic return rowColToCbsrTwoChar(rcp, rowCapacity, colCapacity); case 3: // 2 char numeric return rowColToTwoCharNumeric(rcp, rowCapacity); case 4: // dewar return rowColToDewar(rcp, colCapacity); case 5: // CBSR SBS return rowColtoCbsrSbs(rcp); case 6: // 2 char alphabetic return rowColToTwoChar(rcp, rowCapacity, colCapacity); } return null; } /** * Get the rowColPos corresponding to the given SBS standard 2 or 3 char * string position. Could be A2 or F12. */ public RowColPos sbsToRowCol(String pos) throws Exception { if ((pos.length() != getMinChars()) && (pos.length() != getMaxChars())) { throw new Exception("binPos has an invalid length: " + pos); //$NON-NLS-1$ } int row = SBS_ROW_LABELLING_PATTERN.indexOf(pos.charAt(0)); int col = Integer.parseInt(pos.substring(1)) - 1; return new RowColPos(row, col); } /** * get the RowColPos in the given container corresponding to the given label * AB and will return 1:0. */ public RowColPos cbsrTwoCharToRowCol(String label, int rowCap, int colCap, String containerTypeName) throws Exception { int len = label.length(); if ((len != getMinChars()) && (len != getMaxChars())) { throw new Exception( MessageFormat.format("Label should be {0} characters.", getMinChars())); } int index1 = CBSR_2_CHAR_LABELLING_PATTERN.indexOf(label .charAt(len - 2)); int index2 = CBSR_2_CHAR_LABELLING_PATTERN.indexOf(label .charAt(len - 1)); if ((index1 < 0) || (index2 < 0)) { throw new Exception( "Invalid characters in label. Are they in upper case?"); } int pos = index1 * CBSR_2_CHAR_LABELLING_PATTERN.length() + index2; if (pos >= rowCap * colCap) { String maxValue = ContainerLabelingSchemeWrapper .rowColToCbsrTwoChar(new RowColPos(rowCap - 1, colCap - 1), rowCap, colCap); String msgStart = MessageFormat .format("Label {0} does not exist in this scheme", label); if (containerTypeName != null) { msgStart = MessageFormat.format( "Label {0} does not exist in {1}", label, containerTypeName); } String msgMax = MessageFormat.format( "Max value is {0}. (Max row: {1}. Max col: {2}.)", maxValue, rowCap, colCap); throw new BiobankCheckException(msgStart + " " + msgMax); } Integer row = pos % rowCap; Integer col = pos / rowCap; RowColPos rowColPos = new RowColPos(row, col); return rowColPos; } /** * Get the RowColPos in the given container corresponding to the given label * using the 2 char numeric labelling. */ public RowColPos twoCharNumericToRowCol(String label, int totalRows) throws Exception { String errorMsg = MessageFormat .format("Label {0} is incorrect: it should be 2 characters", label); int len = label.length(); if ((len != getMinChars()) && (len != getMaxChars())) { throw new Exception(errorMsg); } try { int pos = Integer.parseInt(label) - 1; // has remove 1 because the two char numeric starts at 1 Integer row = pos % totalRows; Integer col = pos / totalRows; RowColPos rowColPos = new RowColPos(row, col); return rowColPos; } catch (NumberFormatException nbe) { throw new Exception(errorMsg); } } /** * Get the RowColPos in the given container corresponding to the given label * using the dewar labelling. * * @throws Exception */ public RowColPos dewarToRowCol(String label, int totalCol) throws Exception { int len = label.length(); if ((len != getMinChars()) && (len != getMaxChars())) { throw new Exception( MessageFormat.format("Label should be {0} characters.", getMinChars())); } if (label.charAt(0) != label.charAt(1)) { throw new Exception("Label should be double letter (BB)."); } // letters are double (BB). need only one int letterPosition = SBS_ROW_LABELLING_PATTERN.indexOf(label.charAt(0)); Integer row = letterPosition / totalCol; Integer col = letterPosition % totalCol; RowColPos rowColPos = new RowColPos(row, col); return rowColPos; } /** * Get the rowColPos corresponding to the given CBSR SBS 2 char string * position. Could be A2 or F9. (CBSR SBS skip I and O) */ public RowColPos cbsrSbsToRowCol(String pos) throws Exception { if ((pos.length() != getMinChars()) && (pos.length() != getMaxChars())) { throw new Exception("binPos has an invalid length: " + pos); } int row = BOX81_LABELLING_PATTERN.indexOf(pos.charAt(0)); int col = Integer.parseInt(pos.substring(1)) - 1; return new RowColPos(row, col); } /** * get the RowColPos in the given container corresponding to the given label * AB and will return 1:0. */ public RowColPos twoCharToRowCol(String label, int rowCap, int colCap, String containerTypeName) throws Exception { int len = label.length(); if ((len != getMinChars()) && (len != getMaxChars())) { throw new Exception( MessageFormat.format("Label should be {0} characters.", getMinChars())); } int index1 = TWO_CHAR_LABELLING_PATTERN.indexOf(label.charAt(len - 2)); int index2 = TWO_CHAR_LABELLING_PATTERN.indexOf(label.charAt(len - 1)); if ((index1 < 0) || (index2 < 0)) { throw new Exception( "Invalid characters in label. Are they in upper case?"); } int pos = index1 * TWO_CHAR_LABELLING_PATTERN.length() + index2; if (pos >= rowCap * colCap) { String maxValue = ContainerLabelingSchemeWrapper .rowColToCbsrTwoChar(new RowColPos(rowCap - 1, colCap - 1), rowCap, colCap); String msgStart = MessageFormat .format("Label {0} does not exist in this scheme.", label); if (containerTypeName != null) msgStart = MessageFormat.format( "Label {0} does not exist in {1}", label, containerTypeName); String msgMax = MessageFormat.format( "Max value is {0}. (Max row: {1}. Max col: {2}.)", maxValue, rowCap, colCap); throw new BiobankCheckException(msgStart + " " + msgMax); //$NON-NLS-1$ } Integer row = pos % rowCap; Integer col = pos / rowCap; RowColPos rowColPos = new RowColPos(row, col); return rowColPos; } /** * Get the RowColPos position corresponding to the string position given the * container capacity */ public RowColPos getRowColFromPositionString(String position, Integer rowCapacity, Integer colCapacity) throws Exception { switch (getId()) { case 1: // SBS standard return sbsToRowCol(position); case 2: // CBSR 2 char alphabetic return cbsrTwoCharToRowCol(position, rowCapacity, colCapacity, null); case 3: // 2 char numeric return twoCharNumericToRowCol(position, rowCapacity); case 4: // Dewar return dewarToRowCol(position, colCapacity); case 5: // Box81 return cbsrSbsToRowCol(position); case 6: // 2 char alphabetic return twoCharToRowCol(position, rowCapacity, colCapacity, null); } return null; } @Transient public boolean canLabel(Capacity capacity) { boolean canLabel = true; if (capacity.getRowCapacity() == null || capacity.getColCapacity() == null) { return false; } if (canLabel && getMaxRows() != null) { canLabel &= capacity.getRowCapacity() <= getMaxRows(); } if (canLabel && getMaxCols() != null) { canLabel &= capacity.getColCapacity() <= getMaxCols(); } if (canLabel && getMaxCapacity() != null) { int max = capacity.getRowCapacity() * capacity.getColCapacity(); canLabel &= max <= getMaxCapacity(); } return canLabel; } }