1616import java .util .Collections ;
1717import java .util .List ;
1818import java .util .Optional ;
19+ import java .util .Objects ;
20+ import java .util .function .Consumer ;
1921
2022import net .sf .jsqlparser .expression .Expression ;
2123import net .sf .jsqlparser .expression .operators .relational .ExpressionList ;
2224import net .sf .jsqlparser .expression .operators .relational .ParenthesedExpressionList ;
2325
2426public class GroupByElement implements Serializable {
27+ public enum SortDirection {
28+ ASC , DESC
29+ }
30+
31+ private final List <SortDirection > groupBySortDirections = new ArrayList <>();
2532 private ExpressionList <Expression > groupByExpressions = new ExpressionList <>();
2633 private List <ExpressionList <Expression >> groupingSets = new ArrayList <>();
2734 // postgres rollup is an ExpressionList
@@ -45,6 +52,9 @@ public ExpressionList<Expression> getGroupByExpressions() {
4552 }
4653
4754 public void setGroupByExpressions (ExpressionList <Expression > groupByExpressions ) {
55+ if (this .groupByExpressions != groupByExpressions ) {
56+ groupBySortDirections .clear ();
57+ }
4858 this .groupByExpressions = groupByExpressions ;
4959 }
5060
@@ -68,35 +78,83 @@ public void addGroupingSet(ExpressionList<Expression> list) {
6878 this .groupingSets .add (list );
6979 }
7080
81+ /** Returns the explicit direction at a grouping-list position, or null if omitted. */
82+ public SortDirection getGroupBySortDirection (int index ) {
83+ Objects .checkIndex (index , groupByExpressions .size ());
84+ return index < groupBySortDirections .size () ? groupBySortDirections .get (index ) : null ;
85+ }
86+
87+ /** Directions belong to list positions. Replacing the expression list clears them. */
88+ public void setGroupBySortDirection (int index , SortDirection direction ) {
89+ Objects .checkIndex (index , groupByExpressions .size ());
90+ while (groupBySortDirections .size () <= index ) {
91+ groupBySortDirections .add (null );
92+ }
93+ groupBySortDirections .set (index , direction );
94+ }
95+
96+ public boolean hasGroupBySortDirections () {
97+ if (groupByExpressions != null && !groupBySortDirections .isEmpty ()) {
98+ for (int i = 0 ; i < groupByExpressions .size (); i ++) {
99+ if (getGroupBySortDirection (i ) != null ) {
100+ return true ;
101+ }
102+ }
103+ }
104+ return false ;
105+ }
106+
71107 @ Override
72- @ SuppressWarnings ({"PMD.CyclomaticComplexity" })
73108 public String toString () {
74- StringBuilder b = new StringBuilder ();
75- b .append ("GROUP BY " );
109+ StringBuilder builder = new StringBuilder ();
110+ appendTo (builder , builder ::append , builder ::append );
111+ return builder .toString ();
112+ }
76113
114+ /** Shares clause layout while letting a deparser visit each expression. */
115+ public void appendTo (StringBuilder builder , Consumer <ExpressionList <?>> listRenderer ,
116+ Consumer <Expression > expressionRenderer ) {
117+ builder .append ("GROUP BY " );
77118 if (groupByExpressions != null ) {
78- b .append (groupByExpressions );
119+ if (hasGroupBySortDirections ()) {
120+ appendOrderedExpressions (builder , expressionRenderer );
121+ } else {
122+ listRenderer .accept (groupByExpressions );
123+ }
79124 }
80-
81- int i = 0 ;
82125 if (!groupingSets .isEmpty ()) {
83- if (b .charAt (b .length () - 1 ) != ' ' ) {
84- b .append (' ' );
126+ if (builder .charAt (builder .length () - 1 ) != ' ' ) {
127+ builder .append (' ' );
85128 }
86- b .append ("GROUPING SETS (" );
87- for (ExpressionList <?> expressionList : groupingSets ) {
88- b .append (i ++ > 0 ? ", " : "" ).append (Select .getStringList (
89- expressionList ,
90- true , expressionList instanceof ParenthesedExpressionList ));
129+ builder .append ("GROUPING SETS (" );
130+ for (int i = 0 ; i < groupingSets .size (); i ++) {
131+ builder .append (i > 0 ? ", " : "" );
132+ listRenderer .accept (groupingSets .get (i ));
91133 }
92- b .append (")" );
134+ builder .append (")" );
93135 }
94-
95136 if (isMysqlWithRollup ()) {
96- b .append (" WITH ROLLUP" );
137+ builder .append (" WITH ROLLUP" );
97138 }
139+ }
98140
99- return b .toString ();
141+ private void appendOrderedExpressions (StringBuilder builder ,
142+ Consumer <Expression > expressionRenderer ) {
143+ boolean brackets = groupByExpressions instanceof ParenthesedExpressionList <?>;
144+ if (brackets ) {
145+ builder .append ('(' );
146+ }
147+ for (int i = 0 ; i < groupByExpressions .size (); i ++) {
148+ builder .append (i > 0 ? ", " : "" );
149+ expressionRenderer .accept (groupByExpressions .get (i ));
150+ SortDirection direction = getGroupBySortDirection (i );
151+ if (direction != null ) {
152+ builder .append (' ' ).append (direction );
153+ }
154+ }
155+ if (brackets ) {
156+ builder .append (')' );
157+ }
100158 }
101159
102160 public GroupByElement withGroupByExpressions (ExpressionList <Expression > groupByExpressions ) {
@@ -115,9 +173,9 @@ public GroupByElement addGroupByExpressions(Expression... groupByExpressions) {
115173
116174 public GroupByElement addGroupByExpressions (
117175 Collection <? extends Expression > groupByExpressions ) {
118- ExpressionList collection =
119- Optional .ofNullable (getGroupByExpressions ()).orElseGet (ExpressionList ::new );
120- Collections .addAll (collection , groupByExpressions );
176+ ExpressionList < Expression > collection =
177+ Optional .ofNullable (getGroupByExpressionList ()).orElseGet (ExpressionList ::new );
178+ collection .addAll (groupByExpressions );
121179 return this .withGroupByExpressions (collection );
122180 }
123181
0 commit comments