/**
   * Convert a type-safe UI CellValue into a type-safe Model CellValue
   *
   * @param column Model column from which data-type can be derived
   * @param cell UI CellValue to convert into Model CellValue
   * @return
   */
  public DTCellValue convertToDTModelCell(DTColumnConfig column, CellValue<?> cell) {
    DTDataTypes dt = getDataType(column);
    DTCellValue dtCell = null;

    switch (dt) {
      case BOOLEAN:
        dtCell = new DTCellValue((Boolean) cell.getValue());
        break;
      case DATE:
        dtCell = new DTCellValue((Date) cell.getValue());
        break;
      case NUMERIC:
        dtCell = new DTCellValue((BigDecimal) cell.getValue());
        break;
      default:
        dtCell = new DTCellValue((String) cell.getValue());
    }
    dtCell.setOtherwise(cell.isOtherwise());
    return dtCell;
  }
  // If the Decision Table model has been converted from the legacy text based
  // class then all values are held in the DTCellValue's StringValue. This
  // function attempts to set the correct DTCellValue property based on
  // the DTCellValue's data type.
  private void assertDTCellValue(DTDataTypes dataType, DTCellValue dcv) {
    // If already converted exit
    if (dcv.getDataType().equals(dataType)) {
      return;
    }

    String text = dcv.getStringValue();
    switch (dataType) {
      case BOOLEAN:
        dcv.setBooleanValue((text == null ? null : Boolean.valueOf(text)));
        break;
      case DATE:
        Date d = null;
        try {
          if (text != null) {
            if (DATE_CONVERTOR == null) {
              throw new IllegalArgumentException("DATE_CONVERTOR has not been initialised.");
            }
            d = DATE_CONVERTOR.parse(text);
          }
        } catch (IllegalArgumentException e) {
        }
        dcv.setDateValue(d);
        break;
      case NUMERIC:
        BigDecimal bd = null;
        try {
          if (text != null) {
            bd = new BigDecimal(text);
          }
        } catch (NumberFormatException e) {
        }
        dcv.setNumericValue(bd);
        break;
    }
  }
  /**
   * Update the Decision Table model with the data contained in the grid. The Decision Table does
   * not synchronise model data with UI data during user interaction with the UI. Consequentially
   * this should be called to refresh the Model with the UI when needed.
   */
  public void scrapeData() {

    // Copy data
    final DynamicData data = widget.getGridWidget().getData().getFlattenedData();
    final List<DynamicColumn<DTColumnConfig>> columns = widget.getGridWidget().getColumns();

    final int GRID_ROWS = data.size();
    List<List<DTCellValue>> grid = new ArrayList<List<DTCellValue>>();
    for (int iRow = 0; iRow < GRID_ROWS; iRow++) {
      DynamicDataRow dataRow = data.get(iRow);
      List<DTCellValue> row = new ArrayList<DTCellValue>();
      for (int iCol = 0; iCol < columns.size(); iCol++) {

        // Values put back into the Model are type-safe
        CellValue<?> cv = dataRow.get(iCol);
        DTColumnConfig column = columns.get(iCol).getModelColumn();
        DTCellValue dcv = cellValueFactory.convertToDTModelCell(column, cv);
        dcv.setOtherwise(cv.isOtherwise());
        row.add(dcv);
      }
      grid.add(row);
    }
    this.model.setData(grid);
  }
  /**
   * Make a CellValue applicable for the column
   *
   * @param column The model column
   * @param iRow Row coordinate for initialisation
   * @param iCol Column coordinate for initialisation
   * @param dcv The Model cell containing the value
   * @return A CellValue
   */
  public CellValue<? extends Comparable<?>> makeCellValue(
      DTColumnConfig column, int iRow, int iCol, DTCellValue dcv) {
    DTDataTypes dataType = getDataType(column);
    CellValue<? extends Comparable<?>> cell = null;

    // If this is a legacy Decision Table values are always String
    // so ensure that the appropriate DTCellValue field is populated
    assertDTCellValue(dataType, dcv);

    switch (dataType) {
      case BOOLEAN:
        cell = makeNewBooleanCellValue(iRow, iCol, dcv.getBooleanValue());
        break;
      case DATE:
        cell = makeNewDateCellValue(iRow, iCol, dcv.getDateValue());
        break;
      case NUMERIC:
        if (column instanceof RowNumberCol) {
          cell = makeNewRowNumberCellValue(iRow, iCol);
        } else {
          cell = makeNewNumericCellValue(iRow, iCol, dcv.getNumericValue());
          if (column instanceof AttributeCol) {
            AttributeCol at = (AttributeCol) column;
            if (at.getAttribute().equals(RuleAttributeWidget.SALIENCE_ATTR)) {
              if (at.isUseRowNumber()) {
                cell = makeNewRowNumberCellValue(iRow, iCol);
              }
            }
          }
        }
        break;
      default:
        cell = makeNewStringCellValue(iRow, iCol, dcv.getStringValue());
        if (column instanceof AttributeCol) {
          AttributeCol ac = (AttributeCol) column;
          if (ac.getAttribute().equals(RuleAttributeWidget.DIALECT_ATTR)) {
            cell = makeNewDialectCellValue(iRow, iCol, dcv.getStringValue());
          }
        }
    }

    if (dcv.isOtherwise()) {
      cell.addState(CellState.OTHERWISE);
    }

    return cell;
  }