// Find the right-most index for a Condition column
 private int findConditionColumnIndex(ConditionCol col) {
   int index = 0;
   boolean bMatched = false;
   List<DynamicColumn<DTColumnConfig>> columns = widget.getGridWidget().getColumns();
   for (int iCol = 0; iCol < columns.size(); iCol++) {
     DynamicColumn<DTColumnConfig> column = columns.get(iCol);
     DTColumnConfig modelColumn = column.getModelColumn();
     if (modelColumn instanceof RowNumberCol) {
       index = iCol;
     } else if (modelColumn instanceof DescriptionCol) {
       index = iCol;
     } else if (modelColumn instanceof MetadataCol) {
       index = iCol;
     } else if (modelColumn instanceof AttributeCol) {
       index = iCol;
     } else if (modelColumn instanceof ConditionCol) {
       if (isEquivalentConditionColumn((ConditionCol) modelColumn, col)) {
         index = iCol;
         bMatched = true;
       } else if (!bMatched) {
         index = iCol;
       }
     }
   }
   return index;
 }
  /**
   * Update an ActionSetFieldCol 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 ActionSetFieldCol origColumn, final ActionSetFieldCol 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 column's binding forces an update and redraw if FactField
    // is different; otherwise only need to update and redraw if the
    // FieldType has changed
    if (!isEqualOrNull(origColumn.getBoundName(), editColumn.getBoundName())) {
      if (!isEqualOrNull(origColumn.getFactField(), editColumn.getFactField())) {
        bRedrawColumn = true;
        updateCellsForDataType(editColumn, column);
      }

    } else if (!isEqualOrNull(origColumn.getFactField(), editColumn.getFactField())) {
      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();
                }
              });
    }
  }
  public void setColumnVisibility(DTColumnConfig modelColumn, boolean isVisible) {
    if (modelColumn == null) {
      throw new IllegalArgumentException("modelColumn cannot be null");
    }

    DynamicColumn<DTColumnConfig> col = getDynamicColumn(modelColumn);
    widget.setColumnVisibility(col.getColumnIndex(), isVisible);
  }
 // Retrieves the DynamicColumn relating to the Model column or null if it
 // cannot be found
 private DynamicColumn<DTColumnConfig> getDynamicColumn(DTColumnConfig modelCol) {
   DynamicColumn<DTColumnConfig> column = null;
   List<DynamicColumn<DTColumnConfig>> columns = widget.getGridWidget().getColumns();
   for (DynamicColumn<DTColumnConfig> dc : columns) {
     if (dc.getModelColumn().equals(modelCol)) {
       column = dc;
       break;
     }
   }
   return column;
 }
 // Find the right-most index for a Metadata column
 private int findMetadataColumnIndex() {
   int index = 0;
   List<DynamicColumn<DTColumnConfig>> columns = widget.getGridWidget().getColumns();
   for (int iCol = 0; iCol < columns.size(); iCol++) {
     DynamicColumn<DTColumnConfig> column = columns.get(iCol);
     DTColumnConfig modelColumn = column.getModelColumn();
     if (modelColumn instanceof RowNumberCol) {
       index = iCol;
     } else if (modelColumn instanceof DescriptionCol) {
       index = iCol;
     } else if (modelColumn instanceof MetadataCol) {
       index = iCol;
     }
   }
   return index;
 }
 // Ensure the values in a column are within the Value List
 private boolean updateCellsForOptionValueList(
     final DTColumnConfig editColumn, final DynamicColumn<DTColumnConfig> column) {
   boolean bRedrawRequired = false;
   DynamicData data = widget.getGridWidget().getData();
   List<String> vals = Arrays.asList(model.getValueList(editColumn, sce));
   column.setCell(cellFactory.getCell(editColumn));
   int iCol = column.getColumnIndex();
   for (int iRow = 0; iRow < data.size(); iRow++) {
     DynamicDataRow row = data.get(iRow);
     if (!vals.contains(row.get(iCol).getValue())) {
       row.get(iCol).setValue(null);
       bRedrawRequired = true;
     }
   }
   return bRedrawRequired;
 }
  // Ensure the Column cell type and corresponding values are correct
  private void updateCellsForDataType(
      final DTColumnConfig editColumn, final DynamicColumn<DTColumnConfig> column) {

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

    DynamicData data = widget.getGridWidget().getData();
    column.setCell(cellFactory.getCell(editColumn));
    for (int iRow = 0; iRow < data.size(); iRow++) {
      DynamicDataRow row = data.get(iRow);
      row.set(
          column.getColumnIndex(),
          cellValueFactory.makeCellValue(editColumn, iRow, column.getColumnIndex()));
    }
  }
  public void updateSystemControlledColumnValues() {

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

    for (DynamicColumn<DTColumnConfig> col : columns) {

      DTColumnConfig modelColumn = col.getModelColumn();

      if (modelColumn instanceof RowNumberCol) {
        updateRowNumberColumnValues(data, col.getColumnIndex());

      } else if (modelColumn instanceof AttributeCol) {

        // Update Salience values
        AttributeCol attrCol = (AttributeCol) modelColumn;
        if (attrCol.getAttribute().equals(RuleAttributeWidget.SALIENCE_ATTR)) {
          if (attrCol.isUseRowNumber()) {
            updateSalienceColumnValues(data, col.getColumnIndex(), attrCol.isReverseOrder());
          }

          // Ensure Salience cells are rendered with the correct Cell
          col.setCell(cellFactory.getCell(attrCol));
          col.setSystemControlled(attrCol.isUseRowNumber());
          col.setSortable(!attrCol.isUseRowNumber());
        }
      }
    }
  }
  /**
   * Update the Decision Table model with the columns contained in the grid. The Decision Table
   * controls indexing of new columns to preserve grouping of column types. If the order of columns
   * is important to client-code this can be called to ensure columns within the model are
   * synchronised with the Decision Table.
   */
  public void scrapeColumns() {

    // Clear existing definition
    model.getMetadataCols().clear();
    model.getAttributeCols().clear();
    model.getConditionCols().clear();
    model.getActionCols().clear();

    RowNumberCol rnCol = null;
    DescriptionCol descCol = null;

    // Extract column information
    for (DynamicColumn<DTColumnConfig> column : widget.getGridWidget().getColumns()) {
      DTColumnConfig modelCol = column.getModelColumn();
      if (modelCol instanceof RowNumberCol) {
        rnCol = (RowNumberCol) modelCol;
        model.setRowNumberCol(rnCol);

      } else if (modelCol instanceof DescriptionCol) {
        descCol = (DescriptionCol) modelCol;
        model.setDescriptionCol(descCol);

      } else if (modelCol instanceof MetadataCol) {
        MetadataCol tc = (MetadataCol) modelCol;
        model.getMetadataCols().add(tc);

      } else if (modelCol instanceof AttributeCol) {
        AttributeCol tc = (AttributeCol) modelCol;
        model.getAttributeCols().add(tc);

      } else if (modelCol instanceof ConditionCol) {
        ConditionCol tc = (ConditionCol) modelCol;
        model.getConditionCols().add(tc);

      } else if (modelCol instanceof ActionCol) {
        ActionCol tc = (ActionCol) modelCol;
        model.getActionCols().add(tc);
      }
      modelCol.setWidth(column.getWidth());
      modelCol.setHideColumn(!column.isVisible());
    }
  }
  // Insert a new model column at the specified index
  private void insertColumnBefore(DTColumnConfig modelColumn, int index, boolean bRedraw) {

    // Create new column for grid
    DynamicColumn<DTColumnConfig> column =
        new DynamicColumn<DTColumnConfig>(modelColumn, cellFactory.getCell(modelColumn), index);
    column.setVisible(!modelColumn.isHideColumn());
    DynamicColumn<DTColumnConfig> columnBefore = widget.getGridWidget().getColumns().get(index);

    // Create column data
    DynamicData data = widget.getGridWidget().getData();
    List<CellValue<? extends Comparable<?>>> columnData =
        new ArrayList<CellValue<? extends Comparable<?>>>();
    for (int iRow = 0; iRow < data.size(); iRow++) {
      DTCellValue dcv = new DTCellValue(modelColumn.getDefaultValue());
      CellValue<?> cell = cellValueFactory.makeCellValue(modelColumn, iRow, index, dcv);
      columnData.add(cell);
    }

    // Add column and data to grid
    widget.insertColumnBefore(columnBefore, column, columnData, bRedraw);
  }
  // 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);
    }
  }
  /**
   * 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();
                }
              });
    }
  }
  /**
   * Set the Decision Table's data. This removes all existing columns from the Decision Table and
   * re-creates them based upon the provided data.
   *
   * @param data
   */
  public void setModel(TypeSafeGuidedDecisionTable model) {
    if (model == null) {
      throw new IllegalArgumentException("model cannot be null");
    }

    this.model = model;
    this.cellFactory = new DecisionTableCellFactory(sce, widget.getGridWidget(), this.model);
    this.cellValueFactory = new DecisionTableCellValueFactory(sce, this.model);

    // Date converter is injected so a GWT compatible one can be used here and another in testing
    DecisionTableCellValueFactory.injectDateConvertor(GWTDateConverter.getInstance());

    // Setup command to recalculate System Controlled values when rows are added\deleted
    widget
        .getGridWidget()
        .getData()
        .setOnRowChangeCommand(
            new Command() {

              public void execute() {
                updateSystemControlledColumnValues();
              }
            });

    widget.getGridWidget().getData().clear();
    widget.getGridWidget().getColumns().clear();

    // Dummy rows because the underlying DecoratedGridWidget expects there
    // to be enough rows to receive the columns data
    final DynamicData data = widget.getGridWidget().getData();
    for (int iRow = 0; iRow < model.getData().size(); iRow++) {
      data.addRow();
    }

    // Static columns, Row#
    int colIndex = 0;
    DTColumnConfig colStatic;
    DynamicColumn<DTColumnConfig> columnStatic;
    colStatic = model.getRowNumberCol();
    columnStatic =
        new DynamicColumn<DTColumnConfig>(
            colStatic, cellFactory.getCell(colStatic), colIndex, true, false);
    columnStatic.setWidth(24);
    widget.appendColumn(columnStatic, makeColumnData(colStatic, colIndex++), false);

    // Static columns, Description
    colStatic = model.getDescriptionCol();
    columnStatic =
        new DynamicColumn<DTColumnConfig>(colStatic, cellFactory.getCell(colStatic), colIndex);
    widget.appendColumn(columnStatic, makeColumnData(colStatic, colIndex++), false);

    // Initialise CellTable's Metadata columns
    for (DTColumnConfig col : model.getMetadataCols()) {
      DynamicColumn<DTColumnConfig> column =
          new DynamicColumn<DTColumnConfig>(col, cellFactory.getCell(col), colIndex);
      column.setVisible(!col.isHideColumn());
      widget.appendColumn(column, makeColumnData(col, colIndex++), false);
    }

    // Initialise CellTable's Attribute columns
    for (DTColumnConfig col : model.getAttributeCols()) {
      DynamicColumn<DTColumnConfig> column =
          new DynamicColumn<DTColumnConfig>(col, cellFactory.getCell(col), colIndex);
      column.setVisible(!col.isHideColumn());
      column.setSystemControlled(col.isUseRowNumber());
      column.setSortable(!col.isUseRowNumber());
      widget.appendColumn(column, makeColumnData(col, colIndex++), false);
    }

    // Initialise CellTable's Condition columns
    assertConditionColumnGrouping(model);
    for (DTColumnConfig col : model.getConditionCols()) {
      DynamicColumn<DTColumnConfig> column =
          new DynamicColumn<DTColumnConfig>(col, cellFactory.getCell(col), colIndex);
      column.setVisible(!col.isHideColumn());
      widget.appendColumn(column, makeColumnData(col, colIndex++), false);
    }

    // Initialise CellTable's Action columns
    for (DTColumnConfig col : model.getActionCols()) {
      DynamicColumn<DTColumnConfig> column =
          new DynamicColumn<DTColumnConfig>(col, cellFactory.getCell(col), colIndex);
      column.setVisible(!col.isHideColumn());
      widget.appendColumn(column, makeColumnData(col, colIndex++), false);
    }

    // Ensure System Controlled values are correctly initialised
    updateSystemControlledColumnValues();

    // Draw header first as the size of child Elements depends upon it
    widget.getHeaderWidget().redraw();
    widget.getSidebarWidget().redraw();

    // Schedule redraw of grid after sizes of child Elements have been set
    Scheduler.get()
        .scheduleFinally(
            new ScheduledCommand() {

              public void execute() {
                widget.getGridWidget().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());
 }