Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id 243CD200CC7 for ; Sun, 16 Jul 2017 22:18:09 +0200 (CEST) Received: by cust-asf.ponee.io (Postfix) id 2285B163EDA; Sun, 16 Jul 2017 20:18:09 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id 23C9A163ED7 for ; Sun, 16 Jul 2017 22:18:07 +0200 (CEST) Received: (qmail 20131 invoked by uid 500); 16 Jul 2017 20:18:07 -0000 Mailing-List: contact commits-help@groovy.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@groovy.apache.org Delivered-To: mailing list commits@groovy.apache.org Received: (qmail 20119 invoked by uid 99); 16 Jul 2017 20:18:07 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Sun, 16 Jul 2017 20:18:07 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 7B5D0E9438; Sun, 16 Jul 2017 20:18:05 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: jwagenleitner@apache.org To: commits@groovy.apache.org Date: Sun, 16 Jul 2017 20:18:05 -0000 Message-Id: <4e0b3f4b516849818106dbd78f28eeaf@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [1/2] groovy git commit: GROOVY-8218 @Sortable allows reversed natural ordering (closes #558) archived-at: Sun, 16 Jul 2017 20:18:09 -0000 Repository: groovy Updated Branches: refs/heads/master f89b21d4c -> 6a8232a5b GROOVY-8218 @Sortable allows reversed natural ordering (closes #558) Project: http://git-wip-us.apache.org/repos/asf/groovy/repo Commit: http://git-wip-us.apache.org/repos/asf/groovy/commit/96b8dd91 Tree: http://git-wip-us.apache.org/repos/asf/groovy/tree/96b8dd91 Diff: http://git-wip-us.apache.org/repos/asf/groovy/diff/96b8dd91 Branch: refs/heads/master Commit: 96b8dd91dea613f84d775be81cafb6adaf9417ac Parents: f89b21d Author: Paul-Julien Vauthier Authored: Sun Jun 4 14:31:48 2017 +0800 Committer: John Wagenleitner Committed: Sun Jul 16 12:01:56 2017 -0700 ---------------------------------------------------------------------- src/main/groovy/transform/Sortable.java | 31 ++++++++++++++++++++ .../codehaus/groovy/ast/tools/GeneralUtils.java | 17 +++++++++++ .../transform/SortableASTTransformation.java | 19 ++++++------ .../transform/SortableTransformTest.groovy | 23 +++++++++++++++ 4 files changed, 81 insertions(+), 9 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/groovy/blob/96b8dd91/src/main/groovy/transform/Sortable.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/transform/Sortable.java b/src/main/groovy/transform/Sortable.java index 45fe99f..b27013d 100644 --- a/src/main/groovy/transform/Sortable.java +++ b/src/main/groovy/transform/Sortable.java @@ -45,6 +45,7 @@ import org.codehaus.groovy.transform.GroovyASTTransformationClass; * priority will be according to the order given in the includes list) *
  • have three {@code Comparator} methods named {@code comparatorByFirst}, * {@code comparatorByLast} and {@code comparatorByBorn}
  • + *
  • sort by natural order by default, reversed natural order can be specified
  • * * The properties within the class must themselves be {@code Comparable} or {@code @Sortable}. *

    More examples:

    @@ -123,6 +124,30 @@ import org.codehaus.groovy.transform.GroovyASTTransformationClass; * assert sortedByMaxAttendees[2].beginDate < sortedByMaxAttendees[1].beginDate * * assert Course.declaredMethods.name.findAll { it.startsWith('comparatorBy') }.toSorted() == ['comparatorByMaxAttendees', 'comparatorByTitle'] + * + * //-------------------------------------------------------------------------- + * // Sorting by max attendees using reversed order + * import groovy.transform.Sortable + * import groovy.transform.ToString + * + * @Sortable(includes = ['points'], reversed = true) + * @ToString + * class LeaderBoardEntry { + * String team + * int points + * } + * + * + * final LeaderBoardEntry teamA = new LeaderBoardEntry(team: "Team A", points: 30) + * final LeaderBoardEntry teamB = new LeaderBoardEntry(team: "Team B", points: 80) + * final LeaderBoardEntry teamC = new LeaderBoardEntry(team: "Team C", points: 50) + * + * final List<LeaderBoardEntry> leaderBoard = [teamA, teamB, teamC].toSorted() + * + * assert leaderBoard.first().team == 'Team B' + * assert leaderBoard.last().team == 'Team A' + * assert leaderBoard.points == [80, 50, 30] + * * * * @author Andres Almiray @@ -145,4 +170,10 @@ public @interface Sortable { * Must not be used if 'includes' is used. */ String[] excludes() default {}; + + /** + * Set to true so that comparator uses reversed natural order. + * @since 2.5.0 + */ + boolean reversed() default false; } http://git-wip-us.apache.org/repos/asf/groovy/blob/96b8dd91/src/main/org/codehaus/groovy/ast/tools/GeneralUtils.java ---------------------------------------------------------------------- diff --git a/src/main/org/codehaus/groovy/ast/tools/GeneralUtils.java b/src/main/org/codehaus/groovy/ast/tools/GeneralUtils.java index c00a62d..9280a30 100644 --- a/src/main/org/codehaus/groovy/ast/tools/GeneralUtils.java +++ b/src/main/org/codehaus/groovy/ast/tools/GeneralUtils.java @@ -219,10 +219,27 @@ public class GeneralUtils { return result; } + /** + * Build a binary expression that compares two values + * @param lhv expression for the value to compare from + * @param rhv expression for the value value to compare to + * @return the expression comparing two values + */ public static BinaryExpression cmpX(Expression lhv, Expression rhv) { return new BinaryExpression(lhv, CMP, rhv); } + /** + * Build a binary expression that compares two values + * @param lhv expression for the value to compare from + * @param rhv expression for the value value to compare to + * @param reversed whether to use natural ordering or reversed natural ordering + * @return the expression comparing two values + */ + public static BinaryExpression cmpX(Expression lhv, Expression rhv, boolean reversed) { + return (reversed) ? cmpX(rhv, lhv) : cmpX(lhv, rhv); + } + public static ConstantExpression constX(Object val) { return new ConstantExpression(val); } http://git-wip-us.apache.org/repos/asf/groovy/blob/96b8dd91/src/main/org/codehaus/groovy/transform/SortableASTTransformation.java ---------------------------------------------------------------------- diff --git a/src/main/org/codehaus/groovy/transform/SortableASTTransformation.java b/src/main/org/codehaus/groovy/transform/SortableASTTransformation.java index c48f2a1..ab884a1 100644 --- a/src/main/org/codehaus/groovy/transform/SortableASTTransformation.java +++ b/src/main/org/codehaus/groovy/transform/SortableASTTransformation.java @@ -82,6 +82,7 @@ public class SortableASTTransformation extends AbstractASTTransformation { private void createSortable(AnnotationNode annotation, ClassNode classNode) { List includes = getMemberStringList(annotation, "includes"); List excludes = getMemberStringList(annotation, "excludes"); + boolean reversed = memberHasValue(annotation, "reversed", true); if (!checkIncludeExcludeUndefinedAware(annotation, excludes, includes, MY_TYPE_NAME)) return; if (!checkPropertyList(classNode, includes, "includes", annotation, MY_TYPE_NAME, false)) return; if (!checkPropertyList(classNode, excludes, "excludes", annotation, MY_TYPE_NAME, false)) return; @@ -97,11 +98,11 @@ public class SortableASTTransformation extends AbstractASTTransformation { ClassHelper.int_TYPE, params(param(newClass(classNode), OTHER)), ClassNode.EMPTY_ARRAY, - createCompareToMethodBody(properties) + createCompareToMethodBody(properties, reversed) )); for (PropertyNode property : properties) { - createComparatorFor(classNode, property); + createComparatorFor(classNode, property, reversed); } new VariableScopeVisitor(sourceUnit, true).visitClass(classNode); } @@ -112,7 +113,7 @@ public class SortableASTTransformation extends AbstractASTTransformation { } } - private static Statement createCompareToMethodBody(List properties) { + private static Statement createCompareToMethodBody(List properties, boolean reversed) { List statements = new ArrayList(); // if (this.is(other)) return 0; @@ -123,14 +124,14 @@ public class SortableASTTransformation extends AbstractASTTransformation { // return this.hashCode() <=> other.hashCode() statements.add(declS(varX(THIS_HASH, ClassHelper.Integer_TYPE), callX(varX("this"), "hashCode"))); statements.add(declS(varX(OTHER_HASH, ClassHelper.Integer_TYPE), callX(varX(OTHER), "hashCode"))); - statements.add(returnS(cmpX(varX(THIS_HASH), varX(OTHER_HASH)))); + statements.add(returnS(cmpX(varX(THIS_HASH), varX(OTHER_HASH), reversed))); } else { // int value = 0; statements.add(declS(varX(VALUE, ClassHelper.int_TYPE), constX(0))); for (PropertyNode property : properties) { String propName = property.getName(); // value = this.prop <=> other.prop; - statements.add(assignS(varX(VALUE), cmpX(propX(varX("this"), propName), propX(varX(OTHER), propName)))); + statements.add(assignS(varX(VALUE), cmpX(propX(varX("this"), propName), propX(varX(OTHER), propName), reversed))); // if (value != 0) return value; statements.add(ifS(neX(varX(VALUE), constX(0)), returnS(varX(VALUE)))); } @@ -143,7 +144,7 @@ public class SortableASTTransformation extends AbstractASTTransformation { return body; } - private static Statement createCompareMethodBody(PropertyNode property) { + private static Statement createCompareMethodBody(PropertyNode property, boolean reversed) { String propName = property.getName(); return block( // if (arg0 == arg1) return 0; @@ -153,11 +154,11 @@ public class SortableASTTransformation extends AbstractASTTransformation { // if (arg0 == null && arg1 != null) return 1; ifS(andX(equalsNullX(varX(ARG0)), notNullX(varX(ARG1))), returnS(constX(1))), // return arg0.prop <=> arg1.prop; - returnS(cmpX(propX(varX(ARG0), propName), propX(varX(ARG1), propName))) + returnS(cmpX(propX(varX(ARG0), propName), propX(varX(ARG1), propName), reversed)) ); } - private static void createComparatorFor(ClassNode classNode, PropertyNode property) { + private static void createComparatorFor(ClassNode classNode, PropertyNode property, boolean reversed) { String propName = property.getName(); String className = classNode.getName() + "$" + StringGroovyMethods.capitalize(propName) + "Comparator"; ClassNode superClass = makeClassSafeWithGenerics(AbstractComparator.class, classNode); @@ -170,7 +171,7 @@ public class SortableASTTransformation extends AbstractASTTransformation { ClassHelper.int_TYPE, params(param(newClass(classNode), ARG0), param(newClass(classNode), ARG1)), ClassNode.EMPTY_ARRAY, - createCompareMethodBody(property) + createCompareMethodBody(property, reversed) )); String fieldName = "this$" + StringGroovyMethods.capitalize(propName) + "Comparator"; http://git-wip-us.apache.org/repos/asf/groovy/blob/96b8dd91/src/test/org/codehaus/groovy/transform/SortableTransformTest.groovy ---------------------------------------------------------------------- diff --git a/src/test/org/codehaus/groovy/transform/SortableTransformTest.groovy b/src/test/org/codehaus/groovy/transform/SortableTransformTest.groovy index 6a0540e..9a80f57 100644 --- a/src/test/org/codehaus/groovy/transform/SortableTransformTest.groovy +++ b/src/test/org/codehaus/groovy/transform/SortableTransformTest.groovy @@ -166,4 +166,27 @@ class SortableTransformTest extends CompilableTestSupport { ''' assert message.contains("@Sortable cannot be applied to interface Foo") } + + void testReverseSorting() { + assertScript ''' + import groovy.transform.* + + @CompileStatic + @Canonical + @Sortable(includes = ['age'], reversed = true) + class Person { + String name + int age + } + + def persons = [ + new Person('PJ', 25), + new Person('Guillaume', 40) + ] + + assert persons*.age == [25, 40] + assert persons.sort()*.age == [40, 25] + assert persons.sort(false, Person.comparatorByAge())*.age == [40, 25] + ''' + } }