package gw.rating.rtm.matchop uses gw.api.database.Query uses gw.rating.rtm.domain.OrderedPersistenceAdapter uses java.util.regex.Pattern @ReadOnly class StatelessExactMatch extends StatelessMatchOperator { var _multi : boolean as readonly MultiSelect construct(matchOp : RateTableMatchOp) { super(matchOp) _multi = matchOp.Params.first().MultiSelect initConverter() } override property get Sortable() : boolean { return !MultiSelect } /** * Multiselect columns do not compare consistently, particularly when compared with a single value * For now, we assume that any single value has higher priority than a multi value, causing it to be * placed higher in the list when normalizing * While this will help some limited things to work properly, it is not a general solution. It is recommended * that any sorting expectations are explicitly removed when using multiselect column. */ function compareRowValues(a : Comparable, b : Comparable) : int { if (a == null) { return 1 } if (b == null) { return -1 } if (!MultiSelect) return a.compareTo(b) var stringA = a as String var stringB = b as String // With two multi select cells, there's a slight risk of comparing: ",aValue," to "aValue" While the strings // are different, the values are the same. Comparing the unpacked collections is safer var valuesA = RateTableColumn.unpackMultiSelectString(stringA) var valuesB = RateTableColumn.unpackMultiSelectString(stringB) if (valuesA.equals(valuesB)) { return 0 } // Sort cells with multiple values lower than single values as our sort routines will expand and resort them if (valuesA.size() > 1) { return 1 } if (valuesB.size() > 1) { return -1 } if (valuesA.size() == 1 and valuesB.size() == 1) { return valuesA.first().compareTo(valuesB.first()) } return a.compareTo(b) } override function compare(bean : OrderedPersistenceAdapter, arg : Comparable) : int { var cellValue = bean.get(this.ArgIndex) if (cellValue == null) return 0 if (MultiSelect) { if (isMultiMatch(cellValue as String, arg)) { return 0 } } return arg.compareTo(cellValue) * -1 // reverse compareTo because arg can be a non primitive comparable } private function isMultiMatch(cellValue : String, arg : Comparable) : boolean { var inListPattern = Pattern.compile(RateTableColumn.MULTI_VALUE_SEPARATOR + arg + RateTableColumn.MULTI_VALUE_SEPARATOR); var inListMatcher = inListPattern.matcher( cellValue ); if (inListMatcher.find()) return true; var wholeValuePattern = Pattern.compile("^" + arg + "$") var wholeValueMatcher = wholeValuePattern.matcher( cellValue) return wholeValueMatcher.find() } override function getScore(bean : OrderedPersistenceAdapter, arg : Comparable) : Comparable { return compare(bean, arg) == 0 ? MATCH_SCORE : NONMATCH_SCORE } override function filter(query : Query , arg : Comparable) { if (MultiSelect) { var searchString = RateTableColumn.MULTI_VALUE_SEPARATOR + arg + RateTableColumn.MULTI_VALUE_SEPARATOR query.or(\ andRestriction -> andRestriction .or(\ restriction -> { restriction.compare(ColumnName, Equals, arg) }) .or(\ restriction -> { restriction.contains(ColumnName, searchString, false) })) } else { query.compare(ColumnName, Equals, arg) } } }