@@ -45,21 +45,17 @@ static EndpointRuleSet transform(EndpointRuleSet ruleSet) {
45
45
CoalesceTransform transform = new CoalesceTransform ();
46
46
47
47
List <Rule > transformedRules = new ArrayList <>();
48
- for (Rule rule : ruleSet .getRules ()) {
49
- transformedRules .add (transform .transformRule (rule ));
48
+ for (int i = 0 ; i < ruleSet .getRules (). size (); i ++ ) {
49
+ transformedRules .add (transform .transformRule (ruleSet . getRules (). get ( i ), "root/ rule[" + i + "]" ));
50
50
}
51
51
52
52
if (LOGGER .isLoggable (Level .INFO )) {
53
- StringBuilder msg = new StringBuilder ();
54
- msg .append ("\n === Coalescing Transform Complete ===\n " );
55
- msg .append ("Total: " ).append (transform .coalesceCount ).append (" coalesced, " );
56
- msg .append (transform .cacheHits ).append (" cache hits, " );
57
- msg .append (transform .skippedNoZeroValue ).append (" skipped (no zero value), " );
58
- msg .append (transform .skippedMultipleUses ).append (" skipped (multiple uses)" );
59
- if (!transform .skippedRecordTypes .isEmpty ()) {
60
- msg .append ("\n Skipped record-returning functions: " ).append (transform .skippedRecordTypes );
61
- }
62
- LOGGER .info (msg .toString ());
53
+ LOGGER .info (String .format (
54
+ "Coalescing: %d coalesced, %d cache hits, %d skipped (no zero), %d skipped (multiple uses)" ,
55
+ transform .coalesceCount ,
56
+ transform .cacheHits ,
57
+ transform .skippedNoZeroValue ,
58
+ transform .skippedMultipleUses ));
63
59
}
64
60
65
61
return EndpointRuleSet .builder ()
@@ -69,50 +65,31 @@ static EndpointRuleSet transform(EndpointRuleSet ruleSet) {
69
65
.build ();
70
66
}
71
67
72
- private Rule transformRule (Rule rule ) {
68
+ private Rule transformRule (Rule rule , String rulePath ) {
73
69
Set <Condition > eliminatedConditions = new HashSet <>();
74
- List <Condition > conditions = rule .getConditions ();
75
- Map <String , Integer > localVarUsage = countLocalVariableUsage (conditions );
76
- List <Condition > transformedConditions = transformConditions (conditions , eliminatedConditions , localVarUsage );
77
70
78
- if (rule instanceof TreeRule ) {
79
- TreeRule treeRule = (TreeRule ) rule ;
80
- List <Rule > transformedNestedRules = new ArrayList <>();
81
- boolean nestedChanged = false ;
82
-
83
- for (Rule nestedRule : treeRule .getRules ()) {
84
- Rule transformedNested = transformRule (nestedRule );
85
- transformedNestedRules .add (transformedNested );
86
- if (transformedNested != nestedRule ) {
87
- nestedChanged = true ;
88
- }
89
- }
90
-
91
- if (!transformedConditions .equals (conditions ) || nestedChanged ) {
92
- return TreeRule .builder ()
93
- .description (rule .getDocumentation ().orElse (null ))
94
- .conditions (transformedConditions )
95
- .treeRule (transformedNestedRules );
71
+ // Count local usage for THIS rule's conditions
72
+ Map <String , Integer > localVarUsage = new HashMap <>();
73
+ for (Condition condition : rule .getConditions ()) {
74
+ for (String ref : condition .getFunction ().getReferences ()) {
75
+ localVarUsage .merge (ref , 1 , Integer ::sum );
96
76
}
97
- } else if (!transformedConditions .equals (conditions )) {
98
- // For other rule types, just update conditions
99
- return rule .withConditions (transformedConditions );
100
77
}
101
78
102
- return rule ;
103
- }
104
-
105
- private Map <String , Integer > countLocalVariableUsage (List <Condition > conditions ) {
106
- Map <String , Integer > usage = new HashMap <>();
79
+ List <Condition > transformedConditions = transformConditions (
80
+ rule .getConditions (),
81
+ eliminatedConditions ,
82
+ localVarUsage );
107
83
108
- // Count how many times each variable is used within this specific rule
109
- for (Condition condition : conditions ) {
110
- for (String ref : condition .getFunction ().getReferences ()) {
111
- usage .merge (ref , 1 , Integer ::sum );
112
- }
84
+ if (rule instanceof TreeRule ) {
85
+ TreeRule treeRule = (TreeRule ) rule ;
86
+ return TreeRule .builder ()
87
+ .description (rule .getDocumentation ().orElse (null ))
88
+ .conditions (transformedConditions )
89
+ .treeRule (TreeRewriter .transformNestedRules (treeRule , rulePath , this ::transformRule ));
113
90
}
114
91
115
- return usage ;
92
+ return rule . withConditions ( transformedConditions ) ;
116
93
}
117
94
118
95
private List <Condition > transformConditions (
@@ -128,25 +105,19 @@ private List<Condition> transformConditions(
128
105
continue ;
129
106
}
130
107
131
- // Check if this is a bind that can be coalesced with the next condition
132
108
if (i + 1 < conditions .size () && current .getResult ().isPresent ()) {
133
109
String var = current .getResult ().get ().toString ();
134
110
Condition next = conditions .get (i + 1 );
135
111
136
112
if (canCoalesce (var , current , next , localVarUsage )) {
137
- // Create coalesced condition
138
- Condition coalesced = createCoalescedCondition (current , next , var );
139
- result .add (coalesced );
140
- // Mark both conditions as eliminated
113
+ result .add (createCoalescedCondition (current , next , var ));
141
114
eliminatedConditions .add (current );
142
115
eliminatedConditions .add (next );
143
- // Skip the next condition
144
- i ++;
116
+ i ++; // Skip next
145
117
continue ;
146
118
}
147
119
}
148
120
149
- // No coalescing possible, keep the condition as-is
150
121
result .add (current );
151
122
}
152
123
@@ -155,28 +126,22 @@ private List<Condition> transformConditions(
155
126
156
127
private boolean canCoalesce (String var , Condition bind , Condition use , Map <String , Integer > localVarUsage ) {
157
128
if (!use .getFunction ().getReferences ().contains (var )) {
158
- // The use condition must reference the variable
159
129
return false ;
160
- } else if (use .getFunction ().getFunctionDefinition () == IsSet .getDefinition ()) {
161
- // Never coalesce into presence checks (isSet)
130
+ }
131
+
132
+ if (use .getFunction ().getFunctionDefinition () == IsSet .getDefinition ()) {
162
133
return false ;
163
134
}
164
135
165
- // Check if variable is only used once in this local rule context (even if it appears multiple times globally)
136
+ // Check local usage
166
137
Integer localUses = localVarUsage .get (var );
167
138
if (localUses == null || localUses > 1 ) {
168
139
skippedMultipleUses ++;
169
140
return false ;
170
141
}
171
142
172
- // Get the actual return type (could be Optional<T> or T)
173
143
Type type = bind .getFunction ().getFunctionDefinition ().getReturnType ();
174
-
175
- // Check if we can get a zero value for this type. For OptionalType, we use the inner type's zero value
176
- Type innerType = type ;
177
- if (type instanceof OptionalType ) {
178
- innerType = ((OptionalType ) type ).inner ();
179
- }
144
+ Type innerType = type instanceof OptionalType ? ((OptionalType ) type ).inner () : type ;
180
145
181
146
if (innerType instanceof RecordType ) {
182
147
skippedNoZeroValue ++;
@@ -196,16 +161,11 @@ private Condition createCoalescedCondition(Condition bind, Condition use, String
196
161
LibraryFunction bindExpr = bind .getFunction ();
197
162
LibraryFunction useExpr = use .getFunction ();
198
163
199
- // Get the type and its zero value
200
164
Type type = bindExpr .getFunctionDefinition ().getReturnType ();
201
- Type innerType = type ;
202
- if (type instanceof OptionalType ) {
203
- innerType = ((OptionalType ) type ).inner ();
204
- }
165
+ Type innerType = type instanceof OptionalType ? ((OptionalType ) type ).inner () : type ;
205
166
206
167
Literal zero = innerType .getZeroValue ().get ();
207
168
208
- // Create cache key based on canonical representations
209
169
String bindCanonical = bindExpr .canonicalize ().toString ();
210
170
String zeroCanonical = zero .toString ();
211
171
String useCanonical = useExpr .canonicalize ().toString ();
@@ -220,10 +180,9 @@ private Condition createCoalescedCondition(Condition bind, Condition use, String
220
180
221
181
Expression coalesced = Coalesce .ofExpressions (bindExpr , zero );
222
182
223
- // Replace the variable reference in the use expression
224
183
Map <String , Expression > replacements = new HashMap <>();
225
184
replacements .put (var , coalesced );
226
- ReferenceRewriter rewriter = ReferenceRewriter .forReplacements (replacements );
185
+ TreeRewriter rewriter = TreeRewriter .forReplacements (replacements );
227
186
Expression replaced = rewriter .rewrite (useExpr );
228
187
LibraryFunction canonicalized = ((LibraryFunction ) replaced ).canonicalize ();
229
188
0 commit comments