AbstractAutomatonBuilder.java
package org.eu.autogex.core;
import java.util.*;
/**
* Abstract builder based on the Curiously Recurring Template Pattern (CRTP). Allows sharing state
* creation logic among all specific Builders (DFA, NFA, ENFA) while maintaining fluid method
* chaining.
*
* @param <B> The concrete Builder type (e.g., DFA.Builder)
* @param <A> The Automaton type to build (e.g., DFA)
*/
public abstract class AbstractAutomatonBuilder<
B extends AbstractAutomatonBuilder<B, A>, A extends Automaton> {
protected final Map<String, State> states = new HashMap<>();
protected final Set<State> finalStates = new HashSet<>();
protected State initialState;
/** Default constructor for the abstract builder. */
protected AbstractAutomatonBuilder() {
// Empty constructor since fields are initialized at declaration.
// Required explicitly to maintain Javadoc and satisfy SonarQube rules.
}
/**
* Abstract method that each concrete Builder must implement by returning 'this'. Ensures that
* chaining returns the correct type.
*
* @return The current builder instance.
*/
protected abstract B self();
/**
* Adds a new state to the automaton.
*
* @param name The name of the state.
* @param isFinal True if the state is an accepting state.
* @return The current builder instance.
*/
public B addState(String name, boolean isFinal) {
State state = new State(name, isFinal);
states.put(name, state);
if (isFinal) {
finalStates.add(state);
}
return self();
}
/**
* Sets the initial state of the automaton.
*
* @param name The name of an already added state.
* @return The current builder instance.
*/
public B setInitialState(String name) {
this.initialState = states.get(name);
return self();
}
/**
* Performs integrity checks common to all builders.
*
* @throws IllegalStateException if the builder configuration is invalid.
*/
protected void validate() {
if (initialState == null) {
throw new IllegalStateException(
"The initial state must be set before calling build().");
}
}
/**
* Validates and retrieves the states for a transition. Shared among all concrete builders to
* prevent code duplication.
*
* @param fromName The name of the source state.
* @param toName The name of the destination state.
* @return An array containing [sourceState, targetState].
* @throws IllegalArgumentException if either state does not exist.
*/
protected State[] getTransitionStatesOrThrow(String fromName, String toName) {
State from = states.get(fromName);
State to = states.get(toName);
if (from == null || to == null) {
throw new IllegalArgumentException("State not found. Add it first using addState.");
}
return new State[] {from, to};
}
/**
* Builds and returns the final automaton instance.
*
* @return The compiled automaton.
*/
public abstract A build();
/**
* Gets the map of all registered states.
*
* @return The states map.
*/
public Map<String, State> getStatesMap() {
return states;
}
/**
* Gets the set of all registered final states.
*
* @return The final states set.
*/
public Set<State> getFinalStates() {
return finalStates;
}
/**
* Gets the registered initial state.
*
* @return The initial state.
*/
public State getInitialState() {
return initialState;
}
}