/* * Copyright 2012 Red Hat, Inc. and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.drools.workbench.screens.guided.dtable.client.widget.table.model.synchronizers.impl; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import javax.enterprise.context.Dependent; import org.drools.workbench.models.datamodel.oracle.OperatorsOracle; import org.drools.workbench.models.datamodel.rule.BaseSingleFieldConstraint; import org.drools.workbench.models.datamodel.util.PortablePreconditions; import org.drools.workbench.models.guided.dtable.shared.model.BRLConditionColumn; import org.drools.workbench.models.guided.dtable.shared.model.BRLConditionVariableColumn; import org.drools.workbench.models.guided.dtable.shared.model.BaseColumn; import org.drools.workbench.models.guided.dtable.shared.model.BaseColumnFieldDiff; import org.drools.workbench.models.guided.dtable.shared.model.BaseColumnFieldDiffImpl; import org.drools.workbench.models.guided.dtable.shared.model.CompositeColumn; import org.drools.workbench.models.guided.dtable.shared.model.ConditionCol52; import org.drools.workbench.models.guided.dtable.shared.model.DTCellValue52; import org.drools.workbench.models.guided.dtable.shared.model.LimitedEntryCol; import org.drools.workbench.models.guided.dtable.shared.model.Pattern52; import org.drools.workbench.screens.guided.dtable.client.widget.table.events.gwt.BoundFactsChangedEvent; import org.drools.workbench.screens.guided.dtable.client.widget.table.model.synchronizers.ModelSynchronizer; import org.drools.workbench.screens.guided.dtable.client.widget.table.utilities.ColumnUtilities; import static org.drools.workbench.screens.guided.dtable.client.widget.table.model.synchronizers.impl.ConditionColumnSynchronizer.PatternConditionMetaData; @Dependent public class ConditionColumnSynchronizer extends BaseColumnSynchronizer<PatternConditionMetaData, PatternConditionMetaData, BaseColumnSynchronizer.ColumnMetaData> { public static class PatternConditionMetaData extends BaseColumnSynchronizer.ColumnMetaDataImpl { private final Pattern52 pattern; public PatternConditionMetaData(final Pattern52 pattern, final ConditionCol52 column) { super(column); this.pattern = PortablePreconditions.checkNotNull("pattern", pattern); } public Pattern52 getPattern() { return pattern; } } @Override public boolean handlesAppend(final MetaData metaData) { return handlesUpdate(metaData); } @Override public void append(final PatternConditionMetaData metaData) { //Check operation is supported if (!handlesAppend(metaData)) { return; } final Pattern52 pattern = metaData.getPattern(); final ConditionCol52 column = (ConditionCol52) metaData.getColumn(); //Add pattern if it does not already exist if (!model.getConditions().contains(pattern)) { model.getConditions().add(pattern); //Signal patterns changed event final BoundFactsChangedEvent bfce = new BoundFactsChangedEvent(rm.getLHSBoundFacts()); eventBus.fireEvent(bfce); } pattern.getChildColumns().add(column); synchroniseAppendColumn(column); } @Override public boolean handlesUpdate(final MetaData metaData) { return metaData instanceof PatternConditionMetaData; } @Override public List<BaseColumnFieldDiff> update(final PatternConditionMetaData originalMetaData, final PatternConditionMetaData editedMetaData) { //Check operation is supported if (!(handlesUpdate(originalMetaData) && handlesUpdate(editedMetaData))) { return Collections.emptyList(); } //Get differences between original and edited column final Pattern52 originalPattern = originalMetaData.getPattern(); final Pattern52 editedPattern = editedMetaData.getPattern(); final ConditionCol52 originalColumn = (ConditionCol52) originalMetaData.getColumn(); final ConditionCol52 editedColumn = (ConditionCol52) editedMetaData.getColumn(); final List<BaseColumnFieldDiff> patternDiffs = originalPattern.diff(editedPattern); final List<BaseColumnFieldDiff> columnDiffs = originalColumn.diff(editedColumn); final List<BaseColumnFieldDiff> diffs = new ArrayList<BaseColumnFieldDiff>(); if (patternDiffs != null) { diffs.addAll(patternDiffs); } if (columnDiffs != null) { diffs.addAll(columnDiffs); } //Changes to the Pattern create the new column and remove the old column final boolean isNewPattern = isNewPattern(editedPattern); final boolean isUpdatedPattern = BaseColumnFieldDiffImpl.hasChanged(Pattern52.FIELD_BOUND_NAME, diffs); if (isNewPattern || isUpdatedPattern) { append(editedMetaData); copyColumnData(originalColumn, editedColumn, diffs); delete(originalMetaData); return diffs; } //Changes to the Condition, but Pattern remains unchanged update(originalColumn, editedColumn); final boolean isHideUpdated = BaseColumnFieldDiffImpl.hasChanged(ConditionCol52.FIELD_HIDE_COLUMN, diffs); final boolean isHeaderUpdated = BaseColumnFieldDiffImpl.hasChanged(ConditionCol52.FIELD_HEADER, diffs); final boolean isFactTypeUpdated = BaseColumnFieldDiffImpl.hasChanged(Pattern52.FIELD_FACT_TYPE, diffs); final boolean isFactFieldUpdated = BaseColumnFieldDiffImpl.hasChanged(ConditionCol52.FIELD_FACT_FIELD, diffs); final boolean isFieldTypeUpdated = BaseColumnFieldDiffImpl.hasChanged(ConditionCol52.FIELD_FIELD_TYPE, diffs); final boolean isConstraintValueTypeUpdated = BaseColumnFieldDiffImpl.hasChanged(ConditionCol52.FIELD_CONSTRAINT_VALUE_TYPE, diffs); if (isFactTypeUpdated || isFactFieldUpdated || isFieldTypeUpdated || isConstraintValueTypeUpdated) { clearColumnData(originalColumn); } else { cleanColumnData(originalColumn, editedColumn, diffs); } synchroniseUpdateColumn(originalColumn); if (isHideUpdated) { setColumnVisibility(originalColumn, originalColumn.isHideColumn()); } if (isHeaderUpdated) { setColumnHeader(originalColumn, originalColumn.getHeader()); } return diffs; } @Override public boolean handlesDelete(final MetaData metaData) { if (!(metaData instanceof ColumnMetaData)) { return false; } return ((ColumnMetaData) metaData).getColumn() instanceof ConditionCol52; } @Override public void delete(final ColumnMetaData metaData) { //Check operation is supported if (!handlesDelete(metaData)) { return; } final ConditionCol52 column = (ConditionCol52) metaData.getColumn(); final int columnIndex = model.getExpandedColumns().indexOf(column); final Pattern52 pattern = model.getPattern(column); pattern.getChildColumns().remove(column); //Remove pattern if it contains zero conditions if (pattern.getChildColumns().isEmpty()) { model.getConditions().remove(pattern); //Signal patterns changed event to Decision Table Widget final BoundFactsChangedEvent bfce = new BoundFactsChangedEvent(rm.getLHSBoundFacts()); eventBus.fireEvent(bfce); } synchroniseDeleteColumn(columnIndex); } @Override public boolean handlesMoveColumnsTo(final List<? extends MetaData> metaData) throws ModelSynchronizer.MoveColumnVetoException { for (MetaData md : metaData) { if (!(md instanceof MoveColumnToMetaData)) { return false; } if (!(((MoveColumnToMetaData) md).getColumn() instanceof ConditionCol52)) { return false; } } return true; } @Override public void moveColumnsTo(final List<MoveColumnToMetaData> metaData) throws ModelSynchronizer.MoveColumnVetoException { //Check operation is supported if (!handlesMoveColumnsTo(metaData)) { return; } if (isBRLFragment(metaData)) { doMoveBRLFragment(metaData); } else if (isPattern(metaData)) { doMovePattern(metaData); } else if (isSingleCondition(metaData)) { doMoveSingleCondition(metaData.get(0)); } else { throw new ModelSynchronizer.MoveColumnVetoException(); } } private boolean isBRLFragment(final List<MoveColumnToMetaData> metaData) { if (!metaData.stream().allMatch((c) -> c.getColumn() instanceof BRLConditionVariableColumn)) { return false; } final MoveColumnToMetaData md = metaData.get(0); final BRLConditionVariableColumn srcModelColumn = (BRLConditionVariableColumn) md.getColumn(); final BRLConditionColumn srcModelPattern = model.getBRLColumn(srcModelColumn); return srcModelPattern.getChildColumns().size() == metaData.size(); } private boolean isPattern(final List<MoveColumnToMetaData> metaData) { if (!metaData.stream().allMatch((c) -> c.getColumn() instanceof ConditionCol52)) { return false; } final MoveColumnToMetaData md = metaData.get(0); final ConditionCol52 srcModelColumn = (ConditionCol52) md.getColumn(); final Pattern52 srcModelPattern = model.getPattern(srcModelColumn); return srcModelPattern.getChildColumns().size() == metaData.size(); } private boolean isSingleCondition(final List<MoveColumnToMetaData> metaData) { if (metaData.size() != 1) { return false; } if (metaData.get(0).getColumn() instanceof BRLConditionVariableColumn) { return false; } return metaData.get(0).getColumn() instanceof ConditionCol52; } private void doMoveBRLFragment(final List<MoveColumnToMetaData> metaData) throws ModelSynchronizer.MoveColumnVetoException { final MoveColumnToMetaData md = metaData.get(0); final BRLConditionVariableColumn srcModelColumn = (BRLConditionVariableColumn) md.getColumn(); final BRLConditionColumn srcModelBRLFragment = model.getBRLColumn(srcModelColumn); if (srcModelBRLFragment == null) { throw new ModelSynchronizer.MoveColumnVetoException(); } final List<BRLConditionVariableColumn> srcModelBRLFragmentColumns = srcModelBRLFragment.getChildColumns(); final int srcModelPatternConditionColumnCount = srcModelBRLFragmentColumns.size(); if (srcModelPatternConditionColumnCount == 0) { throw new ModelSynchronizer.MoveColumnVetoException(); } if (srcModelPatternConditionColumnCount != metaData.size()) { throw new ModelSynchronizer.MoveColumnVetoException(); } final int tgtColumnIndex = md.getTargetColumnIndex(); final int tgtPatternIndex = findTargetPatternIndex(md); final List<BaseColumn> allModelColumns = model.getExpandedColumns(); moveModelData(tgtColumnIndex, allModelColumns.indexOf(srcModelBRLFragmentColumns.get(0)), allModelColumns.indexOf(srcModelBRLFragmentColumns.get(0)) + srcModelPatternConditionColumnCount - 1); model.getConditions().remove(srcModelBRLFragment); model.getConditions().add(tgtPatternIndex, srcModelBRLFragment); } private void doMovePattern(final List<MoveColumnToMetaData> metaData) throws ModelSynchronizer.MoveColumnVetoException { final MoveColumnToMetaData md = metaData.get(0); final ConditionCol52 srcModelColumn = (ConditionCol52) md.getColumn(); final Pattern52 srcModelPattern = model.getPattern(srcModelColumn); if (srcModelPattern == null) { throw new ModelSynchronizer.MoveColumnVetoException(); } final List<ConditionCol52> srcModelPatternConditionColumns = srcModelPattern.getChildColumns(); final int srcModelPatternConditionColumnCount = srcModelPatternConditionColumns.size(); if (srcModelPatternConditionColumnCount == 0) { throw new ModelSynchronizer.MoveColumnVetoException(); } if (srcModelPatternConditionColumnCount != metaData.size()) { throw new ModelSynchronizer.MoveColumnVetoException(); } final int tgtColumnIndex = md.getTargetColumnIndex(); final int tgtPatternIndex = findTargetPatternIndex(md); final List<BaseColumn> allModelColumns = model.getExpandedColumns(); moveModelData(tgtColumnIndex, allModelColumns.indexOf(srcModelPatternConditionColumns.get(0)), allModelColumns.indexOf(srcModelPatternConditionColumns.get(0)) + srcModelPatternConditionColumnCount - 1); model.getConditions().remove(srcModelPattern); model.getConditions().add(tgtPatternIndex, srcModelPattern); } //Move a single Condition column; it must remain within the bounds of it's parent Pattern's columns private void doMoveSingleCondition(final MoveColumnToMetaData metaData) throws ModelSynchronizer.MoveColumnVetoException { final ConditionCol52 modelColumn = (ConditionCol52) metaData.getColumn(); final Pattern52 modelPattern = model.getPattern(modelColumn); if (modelPattern == null) { throw new ModelSynchronizer.MoveColumnVetoException(); } final List<ConditionCol52> modelPatternConditionColumns = modelPattern.getChildColumns(); final int modelPatternConditionColumnCount = modelPatternConditionColumns.size(); if (modelPatternConditionColumnCount == 0) { throw new ModelSynchronizer.MoveColumnVetoException(); } final List<BaseColumn> allModelColumns = model.getExpandedColumns(); final int minColumnIndex = allModelColumns.indexOf(modelPatternConditionColumns.get(0)); final int maxColumnIndex = allModelColumns.indexOf(modelPatternConditionColumns.get(modelPatternConditionColumnCount - 1)); final int targetColumnIndex = metaData.getTargetColumnIndex(); final int sourceColumnIndex = metaData.getSourceColumnIndex(); if (targetColumnIndex < minColumnIndex || targetColumnIndex > maxColumnIndex) { throw new ModelSynchronizer.MoveColumnVetoException(); } moveModelData(targetColumnIndex, sourceColumnIndex, sourceColumnIndex); modelPatternConditionColumns.remove(modelColumn); modelPatternConditionColumns.add(targetColumnIndex - minColumnIndex, modelColumn); } private int findTargetPatternIndex(final MoveColumnToMetaData md) throws ModelSynchronizer.MoveColumnVetoException { int tgtPatternIndex = -1; final int tgtColumnIndex = md.getTargetColumnIndex(); final List<BaseColumn> allModelColumns = model.getExpandedColumns(); final List<CompositeColumn<? extends BaseColumn>> allModelConditions = model.getConditions(); for (int patternIndex = 0; patternIndex < allModelConditions.size(); patternIndex++) { final CompositeColumn<? extends BaseColumn> cc = allModelConditions.get(patternIndex); final List<? extends BaseColumn> children = cc.getChildColumns(); if (children == null || children.isEmpty()) { continue; } final BaseColumn firstChild = children.get(0); final BaseColumn lastChild = children.get(children.size() - 1); final int firstChildIndex = allModelColumns.indexOf(firstChild); final int lastChildIndex = allModelColumns.indexOf(lastChild); if (tgtColumnIndex >= firstChildIndex && tgtColumnIndex <= lastChildIndex) { tgtPatternIndex = patternIndex; break; } } if (tgtPatternIndex < 0) { throw new ModelSynchronizer.MoveColumnVetoException(); } return tgtPatternIndex; } private boolean isNewPattern(final Pattern52 editedPattern) { if (model.getConditions() == null || model.getConditions().isEmpty()) { return true; } boolean isNewPattern = true; for (CompositeColumn<? extends BaseColumn> column : model.getConditions()) { if (column instanceof Pattern52) { final Pattern52 existingPattern = (Pattern52) column; if (existingPattern.getBoundName().equals(editedPattern.getBoundName())) { isNewPattern = false; break; } } } return isNewPattern; } private void copyColumnData(final ConditionCol52 originalColumn, final ConditionCol52 editedColumn, final List<BaseColumnFieldDiff> diffs) { final boolean isFactTypeUpdated = BaseColumnFieldDiffImpl.hasChanged(Pattern52.FIELD_FACT_TYPE, diffs); final boolean isFactFieldUpdated = BaseColumnFieldDiffImpl.hasChanged(ConditionCol52.FIELD_FACT_FIELD, diffs); final boolean isConstraintValueTypeUpdated = BaseColumnFieldDiffImpl.hasChanged(ConditionCol52.FIELD_CONSTRAINT_VALUE_TYPE, diffs); // If the FactType, FieldType and ConstraintValueType are unchanged we can copy cell values from the old column into the new if (!(isFactTypeUpdated || isFactFieldUpdated || isConstraintValueTypeUpdated)) { final int originalColumnIndex = model.getExpandedColumns().indexOf(originalColumn); final int editedColumnIndex = model.getExpandedColumns().indexOf(editedColumn); for (int rowIndex = 0; rowIndex < model.getData().size(); rowIndex++) { final List<DTCellValue52> modelRow = model.getData().get(rowIndex); final DTCellValue52 modelCell = modelRow.get(originalColumnIndex); modelRow.set(editedColumnIndex, modelRow.get(originalColumnIndex)); //BaseGridData is sparsely populated; only add values if needed. if (modelCell.hasValue()) { uiModel.setCell(rowIndex, editedColumnIndex, gridWidgetCellFactory.convertCell(modelCell, editedColumn, cellUtilities, columnUtilities)); } } } } private void cleanColumnData(final ConditionCol52 originalColumn, final ConditionCol52 editedColumn, final List<BaseColumnFieldDiff> diffs) { final boolean isOperatorUpdated = BaseColumnFieldDiffImpl.hasChanged(ConditionCol52.FIELD_OPERATOR, diffs); final boolean isValueListUpdated = BaseColumnFieldDiffImpl.hasChanged(ConditionCol52.FIELD_VALUE_LIST, diffs); //Clear "otherwise" if the column cannot accept them if (isOperatorUpdated && !ColumnUtilities.canAcceptOtherwiseValues(editedColumn)) { removeOtherwiseStates(originalColumn); } //Clear comma-separated values if the column cannot accept them if (isOperatorUpdated && !canAcceptCommaSeparatedValues(editedColumn)) { cellUtilities.removeCommaSeparatedValue(editedColumn.getDefaultValue()); removeCommaSeparatedValues(originalColumn); } // Update column's cell content if the Optional Value list has changed if (isValueListUpdated) { updateCellsForOptionValueList(originalColumn, editedColumn); } } //Remove Otherwise state from column cells private void removeOtherwiseStates(final BaseColumn column) { final int columnIndex = this.model.getExpandedColumns().indexOf(column); for (List<DTCellValue52> row : this.model.getData()) { final DTCellValue52 dcv = row.get(columnIndex); dcv.setOtherwise(false); } } // Check whether the given column can accept comma-separated values private boolean canAcceptCommaSeparatedValues(final ConditionCol52 column) { //Check column contains literal values if (column.getConstraintValueType() != BaseSingleFieldConstraint.TYPE_LITERAL) { return false; } //Check operator is supported final List<String> ops = Arrays.asList(OperatorsOracle.EXPLICIT_LIST_OPERATORS); return ops.contains(column.getOperator()); } //Convert comma-separated values to the first in the list private void removeCommaSeparatedValues(final BaseColumn column) { final int columnIndex = this.model.getExpandedColumns().indexOf(column); for (List<DTCellValue52> row : this.model.getData()) { final DTCellValue52 dcv = row.get(columnIndex); cellUtilities.removeCommaSeparatedValue(dcv); } } private void update(final ConditionCol52 originalColumn, final ConditionCol52 editedColumn) { originalColumn.setConstraintValueType(editedColumn.getConstraintValueType()); originalColumn.setFactField(editedColumn.getFactField()); originalColumn.setFieldType(editedColumn.getFieldType()); originalColumn.setHeader(editedColumn.getHeader()); originalColumn.setOperator(editedColumn.getOperator()); originalColumn.setValueList(editedColumn.getValueList()); originalColumn.setDefaultValue(editedColumn.getDefaultValue()); originalColumn.setHideColumn(editedColumn.isHideColumn()); originalColumn.setParameters(editedColumn.getParameters()); originalColumn.setBinding(editedColumn.getBinding()); if (originalColumn instanceof LimitedEntryCol && editedColumn instanceof LimitedEntryCol) { ((LimitedEntryCol) originalColumn).setValue(((LimitedEntryCol) editedColumn).getValue()); } } }