@@ -39,32 +39,41 @@ public AdvancedQueryBuilder(List<FilterValue> filterValues) {
3939 }
4040
4141 public AdvancedQuery .RuleGroup build () {
42- AdvancedQuery .Rule firstRule = null ;
43- ReportConstants .QueryCombinators firstCombinator = null ;
42+ AdvancedQuery .RuleGroup ruleGroup = buildRuleGroup (ReportConstants .QueryCombinators .and , null );
4443
45- // Iterate through FilterValues until we find the first OPERATOR
46- while (hasNext ()) {
47- FilterValue filterValue = peek ();
48- if (filterValue .getValueType ().equals ("CLAUSE" )) {
49- firstRule = buildClause (filterValue );
50- } else if (filterValue .getValueType ().equals ("OPERATOR" )) {
51- if (filterValue .getOperator ().equals ("and" ) || filterValue .getOperator ().equals ("or" )) {
52- firstCombinator = ReportConstants .QueryCombinators .valueOf (filterValue .getOperator ());
53- break ;
54- }
55- }
56- advance ();
57- }
44+ return unwrapRuleGroup (ruleGroup );
45+ }
5846
59- // If we didn't find an OPERATOR, default to 'AND' combinator for a single rule
60- if (firstCombinator == null ) {
61- firstCombinator = ReportConstants .QueryCombinators .and ;
47+ private AdvancedQuery .RuleGroup unwrapRuleGroup (AdvancedQuery .RuleGroup ruleGroup ) {
48+ // If a RuleGroup has only one Rule, and it's also a RuleGroup
49+ if (ruleGroup .rules ().size () == 1
50+ && ruleGroup .rules ().getFirst () instanceof AdvancedQuery .RuleGroup ) {
51+
52+ // We can do away with the outer RuleGroup
53+ return unwrapRuleGroup ((AdvancedQuery .RuleGroup ) ruleGroup .rules ().getFirst ());
54+ } else if (ruleGroup .rules ().size () > 1 ) {
55+ return new AdvancedQuery .RuleGroup (
56+ ruleGroup .id (),
57+ ruleGroup .combinator (),
58+ ruleGroup .rules ().stream ()
59+ .map (
60+ rule -> {
61+ // If a RuleGroup's rule is a RuleGroup, and it has only one Rule
62+ if (rule instanceof AdvancedQuery .RuleGroup nestedRuleGroup ) {
63+ if ((nestedRuleGroup .rules ().size () == 1
64+ && nestedRuleGroup .rules ().getFirst () instanceof AdvancedQuery .Rule )) {
65+ // Bypass the nested RuleGroup and attach the inner Rule to the outer
66+ // RuleGroup
67+ return nestedRuleGroup .rules ().getFirst ();
68+ } else {
69+ return unwrapRuleGroup (nestedRuleGroup );
70+ }
71+ } else {
72+ return rule ;
73+ }
74+ })
75+ .toList ());
6276 }
63-
64- // Then build the root RuleGroup from said OPERATOR and corresponding rule
65- AdvancedQuery .RuleGroup ruleGroup = buildRuleGroup (firstCombinator , firstRule );
66-
67- System .out .println (ruleGroup );
6877 return ruleGroup ;
6978 }
7079
@@ -76,7 +85,6 @@ private AdvancedQuery.RuleGroup buildRuleGroup(
7685 }
7786
7887 AdvancedQuery .RuleGroup ruleGroup = null ;
79- int nestDepth = 0 ;
8088 boolean terminated = false ;
8189
8290 while (hasNext ()) {
@@ -92,29 +100,15 @@ private AdvancedQuery.RuleGroup buildRuleGroup(
92100 switch (filterValue .getOperator ()) {
93101 case "or" :
94102 if (combinator .equals (ReportConstants .QueryCombinators .and )) {
95- // If going directly from an 'AND' group to an 'OR' group, without parens
96- if (nestDepth == 0 ) {
97- // We terminate the current 'AND' group
98- terminated = true ;
99- AdvancedQuery .RuleGroup andGroup =
100- new AdvancedQuery .RuleGroup (
101- UUID .randomUUID ().toString (),
102- ReportConstants .QueryCombinators .and ,
103- rules );
104-
105- // And add it as a rule to the new 'OR' group, which we then build
106- ruleGroup = buildRuleGroup (ReportConstants .QueryCombinators .or , andGroup );
107-
108- // If going from an 'AND' group to an 'OR' group WITHIN inner parens
109- } else {
110- // We just build the 'OR' group, starting from the previous FilterValue
111- AdvancedQuery firstOrRule = rules .removeLast ();
112- AdvancedQuery .RuleGroup orGroup =
113- buildRuleGroup (ReportConstants .QueryCombinators .or , firstOrRule );
114-
115- // And add it to the existing list of 'AND' group rules
116- rules .add (orGroup );
117- }
103+ // If going directly from an 'AND' group to an 'OR' group, terminate the current
104+ // 'AND' group
105+ terminated = true ;
106+ AdvancedQuery .RuleGroup andGroup =
107+ new AdvancedQuery .RuleGroup (
108+ UUID .randomUUID ().toString (), ReportConstants .QueryCombinators .and , rules );
109+
110+ // And add it as a rule to the new 'OR' group, which we then build
111+ ruleGroup = buildRuleGroup (ReportConstants .QueryCombinators .or , andGroup );
118112 }
119113 break ;
120114 case "and" :
@@ -137,20 +131,12 @@ private AdvancedQuery.RuleGroup buildRuleGroup(
137131 "Cannot follow a closing parenthesis with an open parenthesis without an OPERATOR in between" ,
138132 filterValue ));
139133 }
140- nestDepth ++;
134+ advance ();
135+ rules .add (buildRuleGroup (ReportConstants .QueryCombinators .and , null ));
141136 break ;
142137 case ")" :
143138 // If we encounter a closed parenthesis without any nesting, terminate the rule group
144- if (nestDepth == 0 ) {
145- terminated = true ;
146- } else {
147- nestDepth --;
148- }
149-
150- if (nestDepth < 0 ) {
151- queryErrors .add (
152- new AdvancedQueryException ("Too many closing parentheses" , filterValue ));
153- }
139+ terminated = true ;
154140 break ;
155141 default :
156142 queryErrors .add (
@@ -170,10 +156,6 @@ private AdvancedQuery.RuleGroup buildRuleGroup(
170156 }
171157 }
172158
173- if (nestDepth != 0 ) {
174- queryErrors .add (new AdvancedQueryException ("Mismatched parentheses" ));
175- }
176-
177159 if (!queryErrors .isEmpty ()) {
178160 // TODO: What exactly do we wanna do here? Should we still return the partially built
179161 // ruleGroup
0 commit comments