|
4 | 4 | */ |
5 | 5 | package software.amazon.smithy.rulesengine.logic.bdd; |
6 | 6 |
|
| 7 | +import java.io.ByteArrayOutputStream; |
| 8 | +import java.io.IOException; |
| 9 | +import java.io.OutputStreamWriter; |
| 10 | +import java.io.Writer; |
| 11 | +import java.nio.charset.StandardCharsets; |
7 | 12 | import java.util.Arrays; |
8 | 13 | import java.util.List; |
9 | 14 | import java.util.Objects; |
@@ -247,107 +252,81 @@ public int hashCode() { |
247 | 252 |
|
248 | 253 | @Override |
249 | 254 | public String toString() { |
250 | | - return toString(new StringBuilder()).toString(); |
251 | | - } |
252 | | - |
253 | | - /** |
254 | | - * Appends a string representation to the given StringBuilder. |
255 | | - * |
256 | | - * @param sb the StringBuilder to append to |
257 | | - * @return the given string builder. |
258 | | - */ |
259 | | - public StringBuilder toString(StringBuilder sb) { |
260 | | - // Calculate max width needed for first column identifiers |
261 | | - int maxConditionIdx = conditions.size() - 1; |
262 | | - int maxResultIdx = results.size() - 1; |
263 | | - |
264 | | - // Width needed for "C" + maxConditionIdx or "R" + maxResultIdx |
265 | | - int conditionWidth = maxConditionIdx >= 0 ? String.valueOf(maxConditionIdx).length() + 1 : 2; |
266 | | - int resultWidth = maxResultIdx >= 0 ? String.valueOf(maxResultIdx).length() + 1 : 2; |
267 | | - int varWidth = Math.max(conditionWidth, resultWidth); |
268 | | - |
269 | | - sb.append("Bdd{\n"); |
270 | | - |
271 | | - // Conditions |
272 | | - sb.append(" conditions (").append(getConditionCount()).append("):\n"); |
273 | | - for (int i = 0; i < conditions.size(); i++) { |
274 | | - sb.append(String.format(" %" + varWidth + "s: %s%n", "C" + i, conditions.get(i))); |
275 | | - } |
276 | | - |
277 | | - // Results |
278 | | - sb.append(" results (").append(results.size()).append("):\n"); |
279 | | - for (int i = 0; i < results.size(); i++) { |
280 | | - sb.append(String.format(" %" + varWidth + "s: ", "R" + i)); |
281 | | - appendResult(sb, results.get(i)); |
282 | | - sb.append("\n"); |
283 | | - } |
| 255 | + try { |
| 256 | + ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| 257 | + Writer writer = new OutputStreamWriter(baos, StandardCharsets.UTF_8); |
| 258 | + |
| 259 | + // Calculate width for condition/result indices |
| 260 | + int maxConditionIdx = conditions.size() - 1; |
| 261 | + int maxResultIdx = results.size() - 1; |
| 262 | + int conditionWidth = maxConditionIdx >= 0 ? String.valueOf(maxConditionIdx).length() + 1 : 2; |
| 263 | + int resultWidth = maxResultIdx >= 0 ? String.valueOf(maxResultIdx).length() + 1 : 2; |
| 264 | + int varWidth = Math.max(conditionWidth, resultWidth); |
| 265 | + |
| 266 | + writer.write("Bdd{\n"); |
| 267 | + |
| 268 | + // Write conditions |
| 269 | + writer.write(" conditions ("); |
| 270 | + writer.write(String.valueOf(getConditionCount())); |
| 271 | + writer.write("):\n"); |
| 272 | + for (int i = 0; i < conditions.size(); i++) { |
| 273 | + writer.write(String.format(" %" + varWidth + "s: %s%n", "C" + i, conditions.get(i))); |
| 274 | + } |
284 | 275 |
|
285 | | - // Root |
286 | | - sb.append(" root: ").append(formatReference(rootRef)).append("\n"); |
287 | | - |
288 | | - // Nodes |
289 | | - sb.append(" nodes (").append(nodes.length).append("):\n"); |
290 | | - |
291 | | - // Calculate width needed for node indices |
292 | | - int indexWidth = String.valueOf(nodes.length - 1).length(); |
293 | | - |
294 | | - for (int i = 0; i < nodes.length; i++) { |
295 | | - sb.append(String.format(" %" + indexWidth + "d: ", i)); |
296 | | - if (i == 0) { |
297 | | - sb.append("terminal"); |
298 | | - } else { |
299 | | - int[] node = nodes[i]; |
300 | | - int varIdx = node[0]; |
301 | | - sb.append("["); |
302 | | - |
303 | | - // Use the calculated width for variable/result references |
304 | | - if (varIdx < conditions.size()) { |
305 | | - sb.append(String.format("%" + varWidth + "s", "C" + varIdx)); |
306 | | - } else { |
307 | | - sb.append(String.format("%" + varWidth + "s", "R" + (varIdx - conditions.size()))); |
308 | | - } |
309 | | - |
310 | | - // Format the references with consistent spacing |
311 | | - sb.append(", ") |
312 | | - .append(String.format("%6s", formatReference(node[1]))) |
313 | | - .append(", ") |
314 | | - .append(String.format("%6s", formatReference(node[2]))) |
315 | | - .append("]"); |
| 276 | + // Write results |
| 277 | + writer.write(" results ("); |
| 278 | + writer.write(String.valueOf(results.size())); |
| 279 | + writer.write("):\n"); |
| 280 | + for (int i = 0; i < results.size(); i++) { |
| 281 | + writer.write(String.format(" %" + varWidth + "s: ", "R" + i)); |
| 282 | + appendResult(writer, results.get(i)); |
| 283 | + writer.write("\n"); |
316 | 284 | } |
317 | | - sb.append("\n"); |
318 | | - } |
319 | 285 |
|
320 | | - sb.append("}"); |
321 | | - return sb; |
| 286 | + // Write root |
| 287 | + writer.write(" root: "); |
| 288 | + writer.write(BddFormatter.formatReference(rootRef)); |
| 289 | + writer.write("\n"); |
| 290 | + |
| 291 | + // Write nodes header |
| 292 | + writer.write(" nodes ("); |
| 293 | + writer.write(String.valueOf(nodes.length)); |
| 294 | + writer.write("):\n"); |
| 295 | + |
| 296 | + writer.flush(); |
| 297 | + |
| 298 | + // Use BddFormatter for nodes - no need to strip anything since we control the indent |
| 299 | + BddFormatter.builder() |
| 300 | + .writer(writer) |
| 301 | + .nodes(nodes) |
| 302 | + .rootRef(rootRef) |
| 303 | + .conditionCount(conditions.size()) |
| 304 | + .resultCount(results.size()) |
| 305 | + .indent(" ") |
| 306 | + .build() |
| 307 | + .format(); |
| 308 | + |
| 309 | + writer.write("}"); |
| 310 | + writer.flush(); |
| 311 | + |
| 312 | + return baos.toString(StandardCharsets.UTF_8.name()); |
| 313 | + } catch (IOException e) { |
| 314 | + // Should never happen with ByteArrayOutputStream |
| 315 | + throw new RuntimeException("Failed to format BDD", e); |
| 316 | + } |
322 | 317 | } |
323 | 318 |
|
324 | | - private void appendResult(StringBuilder sb, Rule result) { |
| 319 | + private void appendResult(Writer writer, Rule result) throws IOException { |
325 | 320 | if (result == null) { |
326 | | - sb.append("(no match)"); |
| 321 | + writer.write("(no match)"); |
327 | 322 | } else if (result instanceof EndpointRule) { |
328 | | - sb.append("Endpoint: ").append(((EndpointRule) result).getEndpoint().getUrl()); |
| 323 | + writer.write("Endpoint: "); |
| 324 | + writer.write(((EndpointRule) result).getEndpoint().getUrl().toString()); |
329 | 325 | } else if (result instanceof ErrorRule) { |
330 | | - sb.append("Error: ").append(((ErrorRule) result).getError()); |
331 | | - } else { |
332 | | - sb.append(result.getClass().getSimpleName()); |
333 | | - } |
334 | | - } |
335 | | - |
336 | | - private String formatReference(int ref) { |
337 | | - if (ref == 0) { |
338 | | - return "INVALID"; |
339 | | - } else if (ref == 1) { |
340 | | - return "TRUE"; |
341 | | - } else if (ref == -1) { |
342 | | - return "FALSE"; |
343 | | - } else if (ref >= Bdd.RESULT_OFFSET) { |
344 | | - // This is a result reference |
345 | | - int resultIdx = ref - Bdd.RESULT_OFFSET; |
346 | | - return "R" + resultIdx; |
347 | | - } else if (ref < 0) { |
348 | | - return "!" + (Math.abs(ref) - 1); |
| 326 | + writer.write("Error: "); |
| 327 | + writer.write(((ErrorRule) result).getError().toString()); |
349 | 328 | } else { |
350 | | - return String.valueOf(ref - 1); |
| 329 | + writer.write(result.getClass().getSimpleName()); |
351 | 330 | } |
352 | 331 | } |
353 | 332 |
|
|
0 commit comments