CopybookBracketValidator.java
package com.datastructures.linear.stack.applied;
import com.datastructures.linear.stack.classic.Stack;
/**
* Validates that parentheses/brackets in a legacy COBOL source line (PICTURE clauses,
* {@code COMPUTE} expressions) are balanced before an automated parser attempts to translate
* it — part of the tooling built for a legacy bank's mainframe-to-microservices modernization effort.
* A malformed copybook line should fail fast here, not produce a silently wrong translation.
*
* <p>This is the textbook stack application (balanced brackets) applied to a real parsing
* problem: every opening bracket is pushed; every closing bracket must match whatever is on
* top of the stack, and the stack must be empty again once the line ends.
*/
public final class CopybookBracketValidator {
public BracketValidationResult validate(String sourceLine) {
Stack<OpenBracket> openBrackets = new Stack<>();
for (int position = 0; position < sourceLine.length(); position++) {
char current = sourceLine.charAt(position);
if (isOpening(current)) {
openBrackets.push(new OpenBracket(current, position));
} else if (isClosing(current)) {
if (openBrackets.isEmpty()) {
return BracketValidationResult.unexpectedClosing(current, position);
}
OpenBracket lastOpened = openBrackets.pop();
char expectedClosing = closingFor(lastOpened.symbol());
if (expectedClosing != current) {
return BracketValidationResult.mismatch(expectedClosing, current, position);
}
}
}
if (!openBrackets.isEmpty()) {
OpenBracket unclosed = openBrackets.pop();
return BracketValidationResult.unclosed(unclosed.symbol(), unclosed.position());
}
return BracketValidationResult.ok();
}
private boolean isOpening(char c) {
return c == '(' || c == '[' || c == '{';
}
private boolean isClosing(char c) {
return c == ')' || c == ']' || c == '}';
}
private char closingFor(char opening) {
return switch (opening) {
case '(' -> ')';
case '[' -> ']';
default -> '}';
};
}
private record OpenBracket(char symbol, int position) {
}
}