package gw.bizrules.pcf uses gw.api.locale.DisplayKey uses gw.api.path.Path uses gw.bizrules.diff.RuleCommandDefinitionMatcher uses gw.bizrules.diff.RuleMatchableCollectionDiff uses gw.bizrules.diff.RuleMatchableDiffPair uses gw.bizrules.diff.RuleVariableMatcher uses gw.bizrules.util.DiffItemUtils uses pcf.api.Location /** * Defines the controller used when displaying comparison between * {@link RuleVersion}s using a {@link DetailViewPanel DetailView} styled as a * "table". */ class RuleVersionComparisonController { /** * Default Html TD (row) style class name for side-by-side comparison layout. */ public static final var DEFAULT_TD_STYLE_CLASS : String = "bz-sxs-row" /** * Highlight Html TD (row) style class for side-by-side comparison layout. */ public static final var HILITE_TD_STYLE_CLASS: String = "bz-sxs-row-hilite" /** * The {@link RuleVersion}s whose {@link RuleVersion#getRule() Rule}s * are to be compared. */ final var _ruleVersions: List as readonly RuleVersions /** * The total number of columns that are layed out to display * the values of a single {@link RuleVersion} compared to another. */ var _totalColumnsPerRule: int as readonly TotalColumnsPerRule var _rulePageControllers: List as readonly RulePageControllers construct(ver1: RuleVersion, ver2: RuleVersion, totalColumnsPerRule: int, uiConfigs: Map, location: Location) { _ruleVersions = Collections.unmodifiableList({ver1, ver2}) _totalColumnsPerRule = totalColumnsPerRule if (location != null) { final var modes = RuleConditionLineHolder.SWITCHABLE_LINE_EXPRESSION_MODES_SUPERSET _rulePageControllers = Collections.unmodifiableList( _ruleVersions.map(\ruleVersion -> new RulePageController(ruleVersion, uiConfigs, location, modes))) } } property get ConditionRow(): RuleComparisonRow { var conditions = _rulePageControllers*.ConditionBuilderController.map(\ el -> el.RuleConditionType == TC_ALWAYSTRUE ? DisplayKey.get("BizRules.Import.Comparison.RuleCondition.None") : el.formattedDisplayText() ).toList() return new RuleComparisonRow(null, valuesDiffer(conditions), conditions) } property get CommandDefinitionDiffs(): List> { var leftCommands = _ruleVersions.first().Rule.OrderedRuleCommandDefinitions.toCollection() var rightComands = _ruleVersions.last().Rule.OrderedRuleCommandDefinitions.toCollection() return new RuleMatchableCollectionDiff(leftCommands, rightComands, new RuleCommandDefinitionMatcher()).Diffs.toList() .sort(\c1, c2 -> DiffItemUtils.isBefore(c1, c2, \it -> it.SequenceNumber)) } property get VariableDiffs(): List> { var leftVariables = _ruleVersions.first().Rule.getOrderedRuleVariables().toCollection() var rightVariables = _ruleVersions.last().Rule.getOrderedRuleVariables().toCollection() return new RuleMatchableCollectionDiff(leftVariables, rightVariables, new RuleVariableMatcher()).Diffs.toList() } /** * The total number of columns used to display differences between a number * of {@link RuleVersion}s. This is one column for the label plus the * {@link #getTotalColumnsPerRule() TotalColumnsPerRule} times the number of * {@code RuleVersion}s. */ property get TotalColumns(): int { return 1 /* label column */ + (TotalColumnsPerRule * RuleVersions.Count) /* columns for all rules */ } /** * Determine whether the values accessed through the specified path on the * {@link Rule}s of the {@link RuleVersion}s being compared differ. * * @param attributePath path from a {@link RuleVersion} to a value of its * {@link Rule} * @return whether the values at the path differ between the rule versions. */ function isDifferent(attributePath : Path) : boolean { return attributePath.getLeafValue(RuleVersions[0]) != attributePath.getLeafValue(RuleVersions[1]) } /** * Determine whether the arrays accessed through the specified path on the * {@link RuleVersion}s being compared differ based on the value of the * elements' values accessed through the specified attribute path. * * @param arrayPath path on a {@link RuleVersion} to an array value of its * {@link Rule} * @param attributePath path on the element {@link A} to an attribute value * @param the element type of the array accessed through the array path * @param the comparison attribute type of the array element * @return whether the arrays at the path differ between the rule versions * for the specified attribute. */ reified function isDifferent (arrayPath: Path, attributePath: Path): boolean { final var array1 = arrayPath.getLeafValue(RuleVersions[0]) final var array2 = arrayPath.getLeafValue(RuleVersions[1]) if (array1 == array2) { return false } final var resultArray1 = array1?:(new A[]{}) .map(\ e -> attributePath.getLeafValue(e)) final var resultArray2 = array2?:(new A[]{}) .map(\ e -> attributePath.getLeafValue(e)) if (resultArray1.Count != resultArray2.Count) { return true } for (var value1Entry in resultArray1.partition(\ value -> value).entrySet()) { if (value1Entry.Value.Count != resultArray2.countWhere(\ value -> value == value1Entry.Key)) { return false; } } return true } /** * Default Html TD (row) style class name for side-by-side comparison layout. */ property get DefaultTDStyleClass(): String { return DEFAULT_TD_STYLE_CLASS } /** * Highlight Html TD (row) style class for side-by-side comparison layout. */ property get HiliteTDStyleClass(): String { return HILITE_TD_STYLE_CLASS } /** * Determine the Html TD (row) style class to be used for side-by-side rule * comparison layout based on the values accessed through the specified path * on the {@link Rule}s of the {@link RuleVersion}s. If they differ, then a * highlighting row style should be used. Otherwise, a default style is used. * * @param attributePath path from a {@link RuleVersion} to a value of its * {@link Rule} * @return An Html TD style class to be used, highlighting if the values * differ or default if not. */ function getTDStyleClass(attributePath: Path): String { return isDifferent(attributePath) ? HiliteTDStyleClass : DefaultTDStyleClass } /** * Determine whether the arrays accessed through the specified path on the * {@link RuleVersion}s being compared differ based on the value of the * elements' values accessed through the specified attribute path. * * @param arrayPath path on a {@link RuleVersion} to an array value of its * {@link Rule} * @param attributePath path on the element {@link A} to an attribute value * @param the element type of the array accessed through the array path * @param the comparison attribute type of the array element * @return An Html TD style class to be used, highlighting if the values * differ or default if not. */ reified function getTDStyleClass (arrayPath: Path, attributePath: Path): String { return isDifferent(arrayPath, attributePath) ? HiliteTDStyleClass : DefaultTDStyleClass } protected reified function arrayValuesDiffer(arrays: List): boolean { var first = arrays.first() return not arrays.allMatch(\a -> first.disjunction(a).isEmpty()) } protected reified function valuesDiffer(values: List): boolean { var first = values.first() return not values.allMatch(\v -> v == first) } }