@@ -45,21 +45,17 @@ static EndpointRuleSet transform(EndpointRuleSet ruleSet) {
4545 CoalesceTransform transform = new CoalesceTransform ();
4646
4747 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 + "]" ));
5050 }
5151
5252 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 ));
6359 }
6460
6561 return EndpointRuleSet .builder ()
@@ -69,50 +65,31 @@ static EndpointRuleSet transform(EndpointRuleSet ruleSet) {
6965 .build ();
7066 }
7167
72- private Rule transformRule (Rule rule ) {
68+ private Rule transformRule (Rule rule , String rulePath ) {
7369 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 );
7770
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 );
9676 }
97- } else if (!transformedConditions .equals (conditions )) {
98- // For other rule types, just update conditions
99- return rule .withConditions (transformedConditions );
10077 }
10178
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 );
10783
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 ));
11390 }
11491
115- return usage ;
92+ return rule . withConditions ( transformedConditions ) ;
11693 }
11794
11895 private List <Condition > transformConditions (
@@ -128,25 +105,19 @@ private List<Condition> transformConditions(
128105 continue ;
129106 }
130107
131- // Check if this is a bind that can be coalesced with the next condition
132108 if (i + 1 < conditions .size () && current .getResult ().isPresent ()) {
133109 String var = current .getResult ().get ().toString ();
134110 Condition next = conditions .get (i + 1 );
135111
136112 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 ));
141114 eliminatedConditions .add (current );
142115 eliminatedConditions .add (next );
143- // Skip the next condition
144- i ++;
116+ i ++; // Skip next
145117 continue ;
146118 }
147119 }
148120
149- // No coalescing possible, keep the condition as-is
150121 result .add (current );
151122 }
152123
@@ -155,28 +126,22 @@ private List<Condition> transformConditions(
155126
156127 private boolean canCoalesce (String var , Condition bind , Condition use , Map <String , Integer > localVarUsage ) {
157128 if (!use .getFunction ().getReferences ().contains (var )) {
158- // The use condition must reference the variable
159129 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 ()) {
162133 return false ;
163134 }
164135
165- // Check if variable is only used once in this local rule context (even if it appears multiple times globally)
136+ // Check local usage
166137 Integer localUses = localVarUsage .get (var );
167138 if (localUses == null || localUses > 1 ) {
168139 skippedMultipleUses ++;
169140 return false ;
170141 }
171142
172- // Get the actual return type (could be Optional<T> or T)
173143 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 ;
180145
181146 if (innerType instanceof RecordType ) {
182147 skippedNoZeroValue ++;
@@ -196,16 +161,11 @@ private Condition createCoalescedCondition(Condition bind, Condition use, String
196161 LibraryFunction bindExpr = bind .getFunction ();
197162 LibraryFunction useExpr = use .getFunction ();
198163
199- // Get the type and its zero value
200164 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 ;
205166
206167 Literal zero = innerType .getZeroValue ().get ();
207168
208- // Create cache key based on canonical representations
209169 String bindCanonical = bindExpr .canonicalize ().toString ();
210170 String zeroCanonical = zero .toString ();
211171 String useCanonical = useExpr .canonicalize ().toString ();
@@ -220,10 +180,9 @@ private Condition createCoalescedCondition(Condition bind, Condition use, String
220180
221181 Expression coalesced = Coalesce .ofExpressions (bindExpr , zero );
222182
223- // Replace the variable reference in the use expression
224183 Map <String , Expression > replacements = new HashMap <>();
225184 replacements .put (var , coalesced );
226- ReferenceRewriter rewriter = ReferenceRewriter .forReplacements (replacements );
185+ TreeRewriter rewriter = TreeRewriter .forReplacements (replacements );
227186 Expression replaced = rewriter .rewrite (useExpr );
228187 LibraryFunction canonicalized = ((LibraryFunction ) replaced ).canonicalize ();
229188
0 commit comments