// Remove Otherwise state from column cells
  private void removeOtherwiseStates(final DynamicColumn<DTColumnConfig> column) {

    // Grouping needs to be removed
    if (widget.getGridWidget().getData().isMerged()) {
      widget.getGridWidget().toggleMerging();
    }

    DynamicData data = widget.getGridWidget().getData();
    for (int iRow = 0; iRow < data.size(); iRow++) {
      DynamicDataRow row = data.get(iRow);
      CellValue<?> cv = row.get(column.getColumnIndex());
      cv.removeState(CellState.OTHERWISE);
    }
  }
  /**
   * Mark a cell as containing the magical "otherwise" value. The magical "otherwise" value has the
   * meaning of all values other than those explicitly defined for this column.
   */
  public void makeOtherwiseCell() {

    MergableGridWidget<DTColumnConfig> grid = widget.getGridWidget();
    List<CellValue<?>> selections = grid.getSelectedCells();
    CellValue<?> cell = selections.get(0);

    if (canAcceptOtherwiseValues(cell)) {

      // Set "otherwise" property on cell
      for (CellValue<?> cv : selections) {
        cv.addState(CellState.OTHERWISE);
      }
      grid.update(null);
    }
  }
  /**
   * 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;
  }
  /**
   * 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);
  }
  /**
   * Update a Condition column
   *
   * @param origCol The existing column in the grid
   * @param editColumn A copy (not clone) of the original column containing the modified values
   */
  public void updateColumn(final ConditionCol origColumn, final ConditionCol editColumn) {
    if (origColumn == null) {
      throw new IllegalArgumentException("origColumn cannot be null");
    }
    if (editColumn == null) {
      throw new IllegalArgumentException("editColumn cannot be null");
    }

    boolean bRedrawColumn = false;
    boolean bRedrawHeader = false;
    DynamicColumn<DTColumnConfig> column = getDynamicColumn(origColumn);

    // Update column's visibility
    if (origColumn.isHideColumn() != editColumn.isHideColumn()) {
      setColumnVisibility(origColumn, !editColumn.isHideColumn());
    }

    // Change in operator
    if (!isEqualOrNull(origColumn.getOperator(), editColumn.getOperator())) {
      bRedrawHeader = true;

      // Clear otherwise if column cannot accept them
      if (!canAcceptOtherwiseValues(editColumn)) {
        removeOtherwiseStates(column);
        bRedrawColumn = true;
      }
    }

    if (!isEqualOrNull(origColumn.getBoundName(), editColumn.getBoundName())) {
      // Change in bound name requires column to be repositioned
      bRedrawHeader = true;
      addColumn(editColumn, false);
      DynamicColumn<DTColumnConfig> origCol = getDynamicColumn(origColumn);
      DynamicColumn<DTColumnConfig> editCol = getDynamicColumn(editColumn);
      int origColIndex = widget.getGridWidget().getColumns().indexOf(origCol);
      int editColIndex = widget.getGridWidget().getColumns().indexOf(editCol);

      // If the FactType, FieldType and ConstraintValueType are unchanged
      // we can copy cell values from the old column into the new
      if (isEqualOrNull(origColumn.getFactType(), editColumn.getFactType())
          && isEqualOrNull(origColumn.getFactField(), editColumn.getFactField())
          && origColumn.getConstraintValueType() == editColumn.getConstraintValueType()) {

        final DynamicData data = widget.getGridWidget().getData();
        for (int iRow = 0; iRow < data.size(); iRow++) {
          DynamicDataRow row = data.get(iRow);
          CellValue<?> oldCell = row.get(origColIndex);
          CellValue<?> newCell = row.get(editColIndex);
          newCell.setValue(oldCell.getValue());
        }
      }

      // Delete old column and redraw
      widget.deleteColumn(origCol);
      editColIndex = Math.min(widget.getGridWidget().getColumns().size() - 1, editColIndex);
      if (editColIndex > origColIndex) {
        int temp = origColIndex;
        origColIndex = editColIndex;
        editColIndex = temp;
      }
      widget.getGridWidget().redrawColumns(editColIndex, origColIndex);

    } else if (!isEqualOrNull(origColumn.getFactType(), editColumn.getFactType())
        || !isEqualOrNull(origColumn.getFactField(), editColumn.getFactField())
        || origColumn.getConstraintValueType() != editColumn.getConstraintValueType()) {

      // Update column's Cell type
      bRedrawColumn = true;
      updateCellsForDataType(editColumn, column);
    }

    // Update column's cell content if the Optional Value list has changed
    if (!isEqualOrNull(origColumn.getValueList(), editColumn.getValueList())) {
      bRedrawColumn = updateCellsForOptionValueList(editColumn, column);
    }

    // Update column header in Header Widget
    if (!origColumn.getHeader().equals(editColumn.getHeader())) {
      bRedrawHeader = true;
    }

    // Copy new values into original column definition
    populateModelColumn(origColumn, editColumn);

    if (bRedrawColumn) {
      int maxColumnIndex = widget.getGridWidget().getColumns().size() - 1;
      widget.getGridWidget().redrawColumns(column.getColumnIndex(), maxColumnIndex);
    }
    if (bRedrawHeader) {
      // Schedule redraw event after column has been redrawn
      Scheduler.get()
          .scheduleFinally(
              new ScheduledCommand() {
                public void execute() {
                  widget.getHeaderWidget().redraw();
                }
              });
    }
  }
 /**
  * Check whether the given Cell can accept "otherwise" values
  *
  * @param cell
  * @return true if the Cell can accept "otherwise" values
  */
 protected boolean canAcceptOtherwiseValues(CellValue<?> cell) {
   Coordinate c = cell.getCoordinate();
   MergableGridWidget<DTColumnConfig> grid = widget.getGridWidget();
   DynamicColumn<DTColumnConfig> column = grid.getColumns().get(c.getCol());
   return canAcceptOtherwiseValues(column.getModelColumn());
 }