Adrineel Saha - C-191 - Formal Language and Automata Theory (PCCCS502) - Mini Project

You might also like

Download as pdf or txt
Download as pdf or txt
You are on page 1of 93

Visualisation of the working of finite state

automata (Automata & formal language


theory, Mini Project; PCCCS502)

Given a regular expression, write down a program which shows the step by
step labeled graph of corresponding finite automaton. Moreover, perform this
task preferably using Java language in order to construct a graphical tool for
visualisation of the above process.

Folder Tree:-

Codes Under D:\FA-Drawer-master\src\Core\BinaryTree :-

BinaryTree.java:-

package Core.BinaryTree;

/**

* @author Adrineel Saha

*/

public class BinaryTree {

private BinaryTreeNode root;


/**

* @author Adrineel Saha

*/

public BinaryTree(BinaryTreeNode root) {

setRoot(root);

/**

* @author Adrineel Saha

*/

public BinaryTree() {

/**

* @author Adrineel Saha

*/

public BinaryTreeNode getRoot() {

return root;

/**

* @author Adrineel Saha


*

*/

public void setRoot(BinaryTreeNode root) {

this.root = root;

this.root.setParent(null);

BinaryTreeNode.java:-

package Core.BinaryTree;

/**

* @author Adrineel Saha

*/

public class BinaryTreeNode {

private Object value;

private BinaryTreeNode parent, leftChild, rightChild;

/**

* @author Adrineel Saha

*/

public BinaryTreeNode(Object value) {

setValue(value);

}
/**

* @author Adrineel Saha

*/

public BinaryTreeNode() {

/**

* @author Adrineel Saha

*/

public BinaryTreeNode getParent() {

return parent;

/**

* @author Adrineel Saha

*/

public void setParent(BinaryTreeNode parent) {

this.parent = parent;

/**

* @author Adrineel Saha

*
*/

public Object getValue() {

return value;

/**

* @author Adrineel Saha

*/

public void setValue(Object value) {

this.value = value;

/**

* @author Adrineel Saha

*/

public BinaryTreeNode getLeftChild() {

return leftChild;

/**

* @author Adrineel Saha

*/

public void setLeftChild(BinaryTreeNode leftChild) {

this.leftChild = leftChild;

this.leftChild.setParent(this);
}

/**

* @author Adrineel Saha

*/

public BinaryTreeNode getRightChild() {

return rightChild;

/**

* @author Adrineel Saha

*/

public void setRightChild(BinaryTreeNode rightChild) {

this.rightChild = rightChild;

this.rightChild.setParent(this);

Codes Under D:\FA-Drawer-master\src\Core\DFA :-

DFA.java:-

package Core.DFA;

import Core.FA;

/**

*
* @author Adrineel Saha

*/

public class DFA extends FA {

/**

* @author Adrineel Saha

*/

public DFA(char[] alphabets) {

super(alphabets);

DFABuilder.java:-

package Core.DFA;

import java.util.ArrayList;

import java.util.Collections;

import java.util.HashMap;

import java.util.Set;

import Core.NFA.NFA;

import Core.Transition.LetterTransition;

import Core.Transition.Transition;

/**

* @author Adrineel Saha

*/
public class DFABuilder {

private NFA nfa;

private DFA dfa;

private char[] alphabets;

/**

* @author Adrineel Saha

*/

public DFABuilder(NFA nfa) {

this.nfa = nfa;

this.alphabets = nfa.getAlphabets();

/**

* @author Adrineel Saha

*/

public DFABuilder(DFA dfa) {

this.dfa = dfa;

this.alphabets = dfa.getAlphabets();

/**

* @author Adrineel Saha

*/
public NFA getNFA() {

return nfa;

/**

* @author Adrineel Saha

*/

public char[] getAlphabets() {

return alphabets;

/**

* @author Adrineel Saha

*/

public DFA getMinimizedDFAfromDFA() {

try{

int[][] decreaseTable = new int[dfa.getStates().size()][dfa.getStates().size()];

decreaseTable = calculateDecreaseTable();

ArrayList<String> oldStates = new ArrayList<String>();

oldStates.addAll(dfa.getStates());

for (int i = 1; i < decreaseTable.length; i++) {

for (int j = 0; j < i; j++) {

if (decreaseTable[i][j] == 0) {

unifyStates(oldStates.get(i), oldStates.get(j));

}
}

Collections.sort(dfa.getStates());

// dfa.optimizeStatesName();

}catch(NullPointerException E){}

return dfa;

/**

* @author Adrineel Saha

*/

private int[][] calculateDecreaseTable() {

int[][] decreaseTable = new int[dfa.getStates().size()][dfa.getStates().size()];

ArrayList<String> states = dfa.getStates();

for (int i = 0; i < dfa.getAcceptingStates().size(); i++) {

int rowOrColumn = states.indexOf(dfa.getAcceptingStates().get(i));

for (int j = 0; j < rowOrColumn; j++) {

if (!dfa.getAcceptingStates().contains(states.get(j))) {

decreaseTable[rowOrColumn][j] = 1;

for (int j = rowOrColumn + 1; j < decreaseTable[rowOrColumn].length; j++)


{

if (!dfa.getAcceptingStates().contains(states.get(j))) {

decreaseTable[j][rowOrColumn] = 1;

}
}

for (int i = 1; i < decreaseTable.length; i++) {

for (int j = 0; j < i; j++) {

decreaseTable[i][j] = statesRelation(i, j, decreaseTable,

new
boolean[decreaseTable.length][decreaseTable.length]);

return decreaseTable;

/**

* @author Adrineel Saha

*/

private int statesRelation(int i, int j, int[][] tableSoFar, boolean[][] inProgress) {

try{

if (i < j) {

int temp = i;

i = j;

j = temp;

int rel = tableSoFar[i][j];

if (rel != 0)

return rel;

if (inProgress[i][j])

return 0;

inProgress[i][j] = true;

String state1 = dfa.getStates().get(i);

String state2 = dfa.getStates().get(j);


HashMap<Character, String> state1To = new HashMap<Character, String>();

HashMap<Character, String> state2To = new HashMap<Character, String>();

for (int k = 0; k < dfa.getTransitions().size(); k++) {

if (dfa.getTransitions().get(k).getFrom().equals(state1)) {

state1To.put(dfa.getTransitions().get(k).getLetter(),
dfa.getTransitions().get(k)

.getTo());

if (dfa.getTransitions().get(k).getFrom().equals(state2)) {

state2To.put(dfa.getTransitions().get(k).getLetter(),
dfa.getTransitions().get(k)

.getTo());

for (int k = 0; k < getAlphabets().length; k++) {

if
(state1To.get(getAlphabets()[k]).equals(state2To.get(getAlphabets()[k]))) {

continue;

} else if (state1.equals(state1To.get(getAlphabets()[k]))

&& state2.equals(state2To.get(getAlphabets()[k]))) {

continue;

} else {

int relation = statesRelation(

dfa.getStates().indexOf(state1To.get(getAlphabets()[k])), dfa.getStates()

.indexOf(state2To.get(getAlphabets()[k])), tableSoFar, inProgress);

if (relation != 0) {

inProgress[i][j] = false;

return relation + 1;
}

inProgress[i][j] = false;

}catch(NullPointerException E){}

return 0;

/**

* @author Adrineel Saha

*/

private void unifyStates(String state1, String state2) {

if (!dfa.getStates().contains(state1)) {

outerFor: for (int i = 0; i < dfa.getStates().size(); i++) {

String[] embededStates = dfa.getStates().get(i).split(",");

for (int j = 0; j < embededStates.length; j++) {

if (embededStates[j].equals(state1)) {

state1 = dfa.getStates().get(i);

break outerFor;

if (!dfa.getStates().contains(state2)) {

outerFor: for (int i = 0; i < dfa.getStates().size(); i++) {

String[] embededStates = dfa.getStates().get(i).split(",");

for (int j = 0; j < embededStates.length; j++) {

if (embededStates[j].equals(state2)) {
state2 = dfa.getStates().get(i);

break outerFor;

dfa.getStates().remove(state1);

dfa.getStates().remove(state2);

dfa.getStates().add(state1 + "," + state2);

if (dfa.getStartingState().equals(state1) || dfa.getStartingState().equals(state2)) {

dfa.setStartingState(state1 + "," + state2);

if (dfa.getAcceptingStates().contains(state1) ||
dfa.getAcceptingStates().contains(state2)) {

dfa.addAcceptingState(state1 + "," + state2);

dfa.getAcceptingStates().remove(state1);

dfa.getAcceptingStates().remove(state2);

for (int i = 0; i < dfa.getTransitions().size(); i++) {

if (dfa.getTransitions().get(i).getFrom().equals(state1)

|| dfa.getTransitions().get(i).getFrom().equals(state2)) {

dfa.getTransitions().get(i).setFrom(state1 + "," + state2);

if (dfa.getTransitions().get(i).getTo().equals(state1)

|| dfa.getTransitions().get(i).getTo().equals(state2)) {

dfa.getTransitions().get(i).setTo(state1 + "," + state2);


}

ArrayList<Transition> shallowCopy = new ArrayList<Transition>();

shallowCopy.addAll(dfa.getTransitions());

for (int i = 0; i < shallowCopy.size(); i++) {

while (dfa.getTransitions().contains(shallowCopy.get(i))) {

dfa.getTransitions().remove(shallowCopy.get(i));

dfa.addTransition(shallowCopy.get(i));

/**

* @author Adrineel Saha

*/

public DFA getDFAfromNFA() {

DFA dfa = new DFA(getAlphabets());

dfa.addState(nfa.getStartingState());

dfa.setStartingState(nfa.getStartingState());

for (int i = 0; i < getAlphabets().length; i++) {

dfa.addTransition(new LetterTransition(getAlphabets()[i],
dfa.getStartingState(),

getUniqueNameFromList(deltaStar(dfa.getStartingState(),
getAlphabets()[i]))));

buildTransitionsRecursively(dfa);

for (int i = 0; i < dfa.getTransitions().size(); i++) {

if (!dfa.getStates().contains(dfa.getTransitions().get(i).getFrom()))
dfa.addState(dfa.getTransitions().get(i).getFrom());

if (!dfa.getStates().contains(dfa.getTransitions().get(i).getTo()))

dfa.addState(dfa.getTransitions().get(i).getTo());

for (int i = 0; i < dfa.getStates().size(); i++) {

if (!dfa.getStates().get(i).equals("null")) {

if (dfa.getStates().get(i).indexOf(',') > 0) {

for (int j = 0; j < dfa.getStates().get(i).split(",").length; j++) {

if
(nfa.getAcceptingStates().contains(dfa.getStates().get(i).split(",")[j])) {

dfa.addAcceptingState(dfa.getStates().get(i));

break;

} else {

if (nfa.getAcceptingStates().contains(dfa.getStates().get(i)))

dfa.addAcceptingState(dfa.getStates().get(i));

if (acceptsLambda(nfa.getStartingState())) {

if (!dfa.getAcceptingStates().contains(nfa.getStartingState()))

dfa.addAcceptingState(nfa.getStartingState());

dfa.optimizeStatesName();

return dfa;

/**

*
* @author Adrineel Saha

*/

private void buildTransitionsRecursively(DFA dfa) {

ArrayList<Transition> newTransitions = new ArrayList<Transition>();

newTransitions.addAll(dfa.getTransitions());

for (int i = 0; i < dfa.getTransitions().size(); i++) {

HashMap<Character, Boolean> alphasInTransitions = new


HashMap<Character, Boolean>();

for (int j = 0; j < dfa.getTransitions().size(); j++) {

if (dfa.getTransitions().get(i).getTo()

.equals(dfa.getTransitions().get(j).getFrom())) {

alphasInTransitions.put(dfa.getTransitions().get(j).getLetter(), true);

ArrayList<Character> notIncludedLetters =
getNotIncludedLetters(alphasInTransitions

.keySet());

for (int j = 0; j < notIncludedLetters.size(); j++) {

Transition trans = new LetterTransition(notIncludedLetters.get(j),


dfa

.getTransitions().get(i).getTo(),
getUniqueNameFromList(deltaStar(dfa

.getTransitions().get(i).getTo(),
notIncludedLetters.get(j))));

if (!newTransitions.contains(trans))

newTransitions.add(trans);

if (!dfa.getTransitions().equals(newTransitions)) {
dfa.setTransitions(newTransitions);

buildTransitionsRecursively(dfa);

/**

* @author Adrineel Saha

*/

private boolean acceptsLambda(String state) {

for (int i = 0; i < nfa.getTransitions().size(); i++) {

if (nfa.getTransitions().get(i).getFrom().equals(state)

&& nfa.getTransitions().get(i).isLambda()) {

if
(nfa.getAcceptingStates().contains(nfa.getTransitions().get(i).getTo())) {

return true;

} else {

if (acceptsLambda(nfa.getTransitions().get(i).getTo())) {

return true;

return false;

/**

* @author Adrineel Saha

*
*/

private ArrayList<Character> getNotIncludedLetters(Set<Character> list) {

ArrayList<Character> result = new ArrayList<Character>();

for (int i = 0; i < getAlphabets().length; i++) {

if (!list.contains(getAlphabets()[i]))

result.add(getAlphabets()[i]);

return result;

/**

* @author Adrineel Saha

*/

private String getUniqueNameFromList(ArrayList<String> list) {

String result = "";

for (int i = 0; i < list.size(); i++) {

result += list.get(i) + (i == list.size() - 1 ? "" : ",");

if (result.equals(""))

result = "null";

return result;

/**

* @author Adrineel Saha

*/

private ArrayList<String> deltaStar(String state, char letter) {


ArrayList<String> resultStates = new ArrayList<String>();

ArrayList<String> departureClosure = new ArrayList<String>();

if (state.indexOf(',') > 0)

for (int i = 0; i < state.split(",").length; i++) {

departureClosure.addAll(lambdaClosure(state.split(",")[i]));

else

departureClosure = lambdaClosure(state);

for (int i = 0; i < departureClosure.size(); i++) {

for (int j = 0; j < nfa.getTransitions().size(); j++) {

if
(nfa.getTransitions().get(j).getFrom().equals(departureClosure.get(i))

&& !nfa.getTransitions().get(j).isLambda()

&& nfa.getTransitions().get(j).getLetter() == letter)


{

ArrayList<String> states =
lambdaClosure(nfa.getTransitions().get(j).getTo());

if (!resultStates.containsAll(states))

resultStates.addAll(states);

return resultStates;

/**

* @author Adrineel Saha

*/

private ArrayList<String> lambdaClosure(String state) {

ArrayList<String> resultStates = new ArrayList<String>();


resultStates.add(state);

for (int i = 0; i < nfa.getTransitions().size(); i++) {

if (nfa.getTransitions().get(i).getFrom().equals(state)

&& nfa.getTransitions().get(i).isLambda()) {

resultStates.addAll(lambdaClosure(nfa.getTransitions().get(i).getTo()));

return resultStates;

Codes Under D:\FA-Drawer-master\src\Core\NFA :-

NFA.java:-

package Core.NFA;

import Core.FA;

/**

* @author Adrineel Saha

*/

public class NFA extends FA {

/**

* @author Adrineel Saha

*/

public NFA(char[] alphabets) {

super(alphabets);
}

NFABuilder.java:-

package Core.NFA;

import java.util.ArrayList;

import java.util.Date;

import java.util.regex.Pattern;

import java.util.regex.PatternSyntaxException;

import Core.BinaryTree.BinaryTree;

import Core.BinaryTree.BinaryTreeNode;

import Core.Transition.LambdaTransition;

import Core.Transition.LetterTransition;

import Core.Transition.Transition;

/**

* @author Adrineel Saha

*/

public class NFABuilder {

private static final String CONCAT_IDENTIFIER = "concat";

private static final String UNION_IDENTIFIER = "+";

private static final String KLEENE_START_IDENTIFIER = "*";

private String regularExpression;

private char[] alphabets;


private long uniqueNumber;

private int uniqueCounter = 0;

/**

* @author Adrineel Saha

*/

public NFABuilder(String regularText, char[] alphabets) {

setAlphabets(alphabets);

setRegularExpression(regularText);

/**

* @author Adrineel Saha

*/

public String getRegularExpression() {

return regularExpression;

/**

* @author Adrineel Saha

*/

public void setRegularExpression(String regularExpression) {

regularExpression = regularExpression.replace(" ", "");

this.regularExpression = regularExpression;

}
/**

* @author Adrineel Saha

*/

public char[] getAlphabets() {

return alphabets;

/**

* @author Adrineel Saha

*/

public void setAlphabets(char[] alphabets) {

this.alphabets = alphabets;

/**

* @author Adrineel Saha

*/

private boolean isRegex() {

String temp = getRegularExpression();

temp = temp.replace(" ", "");

char[] regex = temp.toCharArray();

for (int i = 0; i < regex.length - 1; i++) {

if (regex[i] == '(' && regex[i + 1] == '*')

return false;
}

temp = temp.replace("*", "");

temp = temp.replace("(", "");

temp = temp.replace(")", "");

temp = temp.replace("+", "");

for (int i = 0; i < getAlphabets().length; i++) {

temp = temp.replace(getAlphabets()[i] + "", "");

if (temp.equals(""))

return true;

return false;

/**

* @author Adrineel Saha

*/

private void checkRegexValidity() {

try {

Pattern.compile(getRegularExpression());

} catch (PatternSyntaxException e) {

throw new NotRegularStringException("String is not regular.");

if (!isRegex()) {

throw new NotRegularStringException("String is not regular.");

/**

*
* @author Adrineel Saha

*/

private String getEnhancedRegex(String regex) {

String temp = regex;

for (int i = 0; i < getAlphabets().length; i++) {

temp = temp.replace(getAlphabets()[i] + "", "(" + getAlphabets()[i] + ")");

String fixedKleeneStartPriorityRegex = temp;

ArrayList<Integer> kleenePositions = new ArrayList<Integer>();

for (int i = 0; i < temp.length() - 1; i++) {

if (temp.toCharArray()[i] == ')' && temp.toCharArray()[i + 1] == '*') {

kleenePositions.add(i);

for (int i = 0; i < kleenePositions.size(); i++) {

int counter = 0, position = kleenePositions.get(i);

firstLoop: while (true) {

if (temp.charAt(position) == ')')

counter++;

else if (temp.charAt(position) == '(')

counter--;

if (counter == 0)

break firstLoop;

position--;

position += i * 2;

int endPos = kleenePositions.get(i) + 2 + i * 2;

fixedKleeneStartPriorityRegex = fixedKleeneStartPriorityRegex.substring(0,
position)
+ "(" + fixedKleeneStartPriorityRegex.substring(position,
endPos) + ")"

+ fixedKleeneStartPriorityRegex.substring(endPos);

String fixedConcatPriorityRegex = fixedKleeneStartPriorityRegex;

ArrayList<Integer> concatPositions = new ArrayList<Integer>();

char[] basicRegexArray = fixedKleeneStartPriorityRegex.toCharArray();

for (int i = 0; i < basicRegexArray.length - 1; i++) {

if (basicRegexArray[i] == ')' && basicRegexArray[i + 1] == '(') {

concatPositions.add(i);

} else if (i < basicRegexArray.length - 2)

if (basicRegexArray[i] == ')' && basicRegexArray[i + 1] == '*'

&& basicRegexArray[i + 2] == '(') {

concatPositions.add(i);

for (int i = 0; i < concatPositions.size(); i++) {

int counter = 0, position = concatPositions.get(i);

firstLoop: while (true) {

if (fixedKleeneStartPriorityRegex.charAt(position) == ')')

counter++;

else if (fixedKleeneStartPriorityRegex.charAt(position) == '(')

counter--;

if (counter == 0)

break firstLoop;

position--;

position += i * 2;

fixedConcatPriorityRegex = fixedConcatPriorityRegex.substring(0, position)


+ "("

+ fixedConcatPriorityRegex.substring(position);
counter = 0;

position = concatPositions.get(i)

+
(fixedKleeneStartPriorityRegex.charAt(concatPositions.get(i) + 1) == '*' ? 2

: 1);

secondLoop: while (true) {

if (fixedKleeneStartPriorityRegex.charAt(position) == '(')

counter++;

else if (fixedKleeneStartPriorityRegex.charAt(position) == ')')

counter--;

if (counter == 0)

break secondLoop;

position++;

position += i * 2 + 1;

fixedConcatPriorityRegex = fixedConcatPriorityRegex.substring(0, position)


+ ")"

+ fixedConcatPriorityRegex.substring(position);

return fixedConcatPriorityRegex;

/**

* @author Adrineel Saha

*/

public NFA toNFA() {

checkRegexValidity();

BinaryTree tree = new BinaryTree(new BinaryTreeNode(

getEnhancedRegex(getRegularExpression())));
buildRegexTree(tree.getRoot());

NFA nfa = buildNFAFromRegexTree(tree);

nfa.optimizeStatesName();

return nfa;

/**

* @author Adrineel Saha

*/

private void buildRegexTree(BinaryTreeNode node) {

Object value = node.getValue();

if (value != null && value instanceof String) {

String expression = (String) value;

if (expression.length() > 0 && expression.charAt(0) == '(') {

int counter = 0, position = 0;

matchFinder: while (true) {

if (expression.charAt(position) == '(')

counter++;

else if (expression.charAt(position) == ')')

counter--;

position++;

if (counter == 0) {

break matchFinder;

if (position >= expression.length()) {

node.setValue(expression.substring(1, expression.length()
- 1));

buildRegexTree(node);
return;

} else {

BinaryTreeNode leftChildNode = new BinaryTreeNode(),


rightChildNode = new BinaryTreeNode();

if (expression.charAt(position) == '*' && position >=


expression.length() - 1) {

leftChildNode.setValue(expression.substring(1,
expression.length() - 2));

node.setValue(KLEENE_START_IDENTIFIER);

node.setLeftChild(leftChildNode);

buildRegexTree(leftChildNode);

return;

} else {

if (expression.charAt(position) == '*') {

position++;

BinaryTreeNode leftleftChildNode = new


BinaryTreeNode();

leftChildNode.setValue(KLEENE_START_IDENTIFIER);

leftleftChildNode.setValue(expression.substring(1, position - 2));

leftChildNode.setLeftChild(leftleftChildNode);

node.setLeftChild(leftChildNode);

buildRegexTree(leftleftChildNode);

String nodeValue = expression.charAt(position) ==


'+' ? UNION_IDENTIFIER

: CONCAT_IDENTIFIER;

if (nodeValue.equals(UNION_IDENTIFIER))

position++;

node.setValue(nodeValue);

if (node.getLeftChild() == null) {
leftChildNode.setValue(expression.substring(1,

position -
(nodeValue.equals(CONCAT_IDENTIFIER) ? 1 : 2)));

node.setLeftChild(leftChildNode);

buildRegexTree(leftChildNode);

rightChildNode.setValue(expression.substring(position));

node.setRightChild(rightChildNode);

buildRegexTree(rightChildNode);

return;

} else if (expression.length() == 1) {

return;

// if (value != null && value instanceof String) {

// System.out.println(value);

// String text = (String) value;

// for (int i = 0; i < getAlphabets().length; i++) {

// text = text.replace(getAlphabets()[i] + "", "(" + getAlphabets()[i] +

// ")");

// }

// while (true) {

// String newText = text.replace("()", "");

// if (newText.equals(text))

// break;

// text = newText;

// }

// System.out.println(text);
// node.setValue(text);

// buildRegexTree(node);

// }

throw new NotRegularStringException();

/**

* @author Adrineel Saha

*/

private NFA buildNFAFromRegexTree(BinaryTree tree) {

return buildNFAFromNode(tree.getRoot());

/**

* @author Adrineel Saha

*/

private NFA buildNFAFromNode(BinaryTreeNode node) {

if (node != null && node.getValue() != null) {

// if (node.getValue() instanceof NFA) {

// node.setRightChild(null);

// node.setLeftChild(null);

// return (NFA) node.getValue();

// }

if (node.getValue() instanceof String) {

String value = (String) node.getValue();

if (isInAlphabets(value))

return createSimpleNFA(value.charAt(0));
if (value.equals(CONCAT_IDENTIFIER) ||
value.equals(UNION_IDENTIFIER)) {

if (node.getLeftChild() == null ||
node.getLeftChild().getValue() == null

|| node.getRightChild() == null

|| node.getRightChild().getValue() == null)

throw new RuntimeException("Error in creating


NFA");

NFA leftNFA = buildNFAFromNode(node.getLeftChild());

NFA rightNFA = buildNFAFromNode(node.getRightChild());

if (value.equals(CONCAT_IDENTIFIER))

return concatNFA(leftNFA, rightNFA);

else if (value.equals(UNION_IDENTIFIER))

return unionNFA(leftNFA, rightNFA);

} else if (value.equals(KLEENE_START_IDENTIFIER)) {

if (node.getLeftChild() == null ||
node.getLeftChild().getValue() == null)

throw new RuntimeException("Error in creating


NFA");

NFA leftNFA = buildNFAFromNode(node.getLeftChild());

return kleeneStarNFA(leftNFA);

// return null;

throw new RuntimeException("Error in creating NFA");

/**

* @author Adrineel Saha

*/
private boolean isInAlphabets(String text) {

try {

for (int i = 0; i < getAlphabets().length; i++) {

if (text.equals(getAlphabets()[i] + ""))

return true;

} catch (Exception e) {

return false;

/**

* @author Adrineel Saha

*/

private NFA createSimpleNFA(char acceptingChar) {

String state1 = (getUniqueNumber() + "");

String state2 = (getUniqueNumber() + "");

NFA nfa = new NFA(getAlphabets());

ArrayList<String> states = new ArrayList<String>();

ArrayList<String> acceptingStates = new ArrayList<String>();

ArrayList<Transition> transitions = new ArrayList<Transition>();

String startingState = state1;

states.add(state1);

states.add(state2);

acceptingStates.add(state2);
transitions.add(new LetterTransition(acceptingChar, state1, state2));

nfa.setStates(states);

nfa.setAcceptingStates(acceptingStates);

nfa.setStartingState(startingState);

nfa.setTransitions(transitions);

return nfa;

/**

* @author Adrineel Saha

*/

private NFA unionNFA(NFA nfa1, NFA nfa2) {

if (nfa1 == null)

return nfa2;

if (nfa2 == null)

return nfa1;

String newState = (getUniqueNumber() + "");

NFA nfa = new NFA(getAlphabets());

nfa.addState(newState);

nfa.addStates(nfa1.getStates());

nfa.addStates(nfa2.getStates());

nfa.setStartingState(newState);

nfa.addAcceptingStates(nfa1.getAcceptingStates());

nfa.addAcceptingStates(nfa2.getAcceptingStates());
nfa.addTransition(new LambdaTransition(newState, nfa1.getStartingState()));

nfa.addTransition(new LambdaTransition(newState, nfa2.getStartingState()));

nfa.addTransitions(nfa1.getTransitions());

nfa.addTransitions(nfa2.getTransitions());

return nfa;

/**

* @author Adrineel Saha

*/

private NFA concatNFA(NFA nfa1, NFA nfa2) {

if (nfa1 == null)

return nfa2;

if (nfa2 == null)

return nfa1;

NFA nfa = new NFA(getAlphabets());

nfa.addStates(nfa1.getStates());

nfa.addStates(nfa2.getStates());

nfa.setStartingState(nfa1.getStartingState());

nfa.addAcceptingStates(nfa2.getAcceptingStates());

for (int i = 0; i < nfa1.getAcceptingStates().size(); i++) {

nfa.addTransition(new LambdaTransition(nfa1.getAcceptingStates().get(i),
nfa2

.getStartingState()));

}
nfa.addTransitions(nfa1.getTransitions());

nfa.addTransitions(nfa2.getTransitions());

return nfa;

/**

* @author Adrineel Saha

*/

private NFA kleeneStarNFA(NFA givenNfa) {

String newState = (getUniqueNumber() + "");

NFA nfa = new NFA(getAlphabets());

nfa.addState(newState);

nfa.addStates(givenNfa.getStates());

nfa.setStartingState(newState);

nfa.addAcceptingState(newState);

nfa.addTransition(new LambdaTransition(newState, givenNfa.getStartingState()));

for (int i = 0; i < givenNfa.getAcceptingStates().size(); i++) {

nfa.addTransition(new
LambdaTransition(givenNfa.getAcceptingStates().get(i), newState));

nfa.addTransitions(givenNfa.getTransitions());

return nfa;

}
/**

* @author Adrineel Saha

*/

private long getUniqueNumber() {

uniqueNumber = new Date().getTime();

uniqueNumber += uniqueCounter;

uniqueCounter++;

return uniqueNumber;

// private void printAllChildren(BinaryTreeNode node) {

// if (node != null && node.getValue() != null) {

// String text = node.getValue().toString();

// if (node.getParent() != null && node.getParent().getValue() != null)

// text += " my parent: " + node.getParent().getValue().toString();

// System.out.println(text);

// }

// if (node != null) {

// printAllChildren(node.getLeftChild());

// printAllChildren(node.getRightChild());

// }

// }

/**

* @author Adrineel Saha

*/

static class NotRegularStringException extends RuntimeException {


private static final long serialVersionUID = -5793081370423590610L;

/**

* @author Mehran Mahmoudi

*/

public NotRegularStringException(String string) {

super(string);

/**

* @author Adrineel Saha

*/

public NotRegularStringException() {

super();

Codes Under D:\FA-Drawer-master\src\Core\Transition :-

LambdaTransition.java:-

package Core.Transition;

/**

* @author Adrineel Saha


*

*/

public class LambdaTransition implements Transition {

private String from, to;

public LambdaTransition(String from, String to) {

super();

this.from = from;

this.to = to;

@Override

public boolean isLambda() {

return true;

@Override

public char getLetter() {

return 'Λ';

@Override

public String getFrom() {

return from;

@Override

public String getTo() {

return to;

}
@Override

public void setLetter(char letter) {

@Override

public void setFrom(String from) {

this.from = from;

@Override

public void setTo(String to) {

this.to = to;

@Override

public boolean equals(Object obj) {

if (!(obj instanceof Transition))

return false;

try {

if (((Transition) obj).getFrom().equals(getFrom())

&& ((Transition) obj).getTo().equals(getTo()) &&


((Transition) obj).isLambda())

return true;

} catch (Exception e) {

return false;

@Override

public String toString() {


return "From: " + getFrom() + " to " + getTo() + " with lambda";

LetterTransition.java:-

package Core.Transition;

/**

* @author Adrineel Saha

*/

public class LetterTransition implements Transition {

private char letter;

private String from, to;

public LetterTransition(char letter, String from, String to) {

super();

this.letter = letter;

this.from = from;

this.to = to;

@Override

public boolean isLambda() {

return false;

@Override
public char getLetter() {

return letter;

@Override

public String getFrom() {

return from;

@Override

public String getTo() {

return to;

@Override

public void setLetter(char letter) {

this.letter = letter;

@Override

public void setFrom(String from) {

this.from = from;

@Override

public void setTo(String to) {

this.to = to;

@Override

public boolean equals(Object obj) {


if (!(obj instanceof Transition))

return false;

try {

if (((Transition) obj).getFrom().equals(getFrom())

&& ((Transition) obj).getTo().equals(getTo())

&& ((Transition) obj).getLetter() == getLetter())

return true;

} catch (Exception e) {

return false;

@Override

public String toString() {

return "From: " + getFrom() + " to " + getTo() + " with " + getLetter();

Transition.java:-

package Core.Transition;

/**

* @author Adrineel Saha

*/

public interface Transition {

/**

* @author Adrineel Saha


*

*/

public boolean isLambda();

/**

* @author Adrineel Saha

*/

public char getLetter();

/**

* @author Adrineel Saha

*/

public String getFrom();

/**

* @author Adrineel Saha

*/

public String getTo();

/**

* @author Adrineel Saha

*/

public void setLetter(char letter);


/**

* @author Adrineel Saha

*/

public void setFrom(String from);

/**

* @author Adrineel Saha

*/

public void setTo(String to);

Codes Under D:\FA-Drawer-master\src\Core :-

FA.java:-

package Core;

import java.util.ArrayList;

import Core.Transition.Transition;

/**

* @author Adrineel Saha

*/

public class FA {
private char[] alphabets;

private ArrayList<String> states;

private ArrayList<String> acceptingStates;

private String startingState;

private ArrayList<Transition> transitions;

/**

* @author Adrineel Saha

*/

public FA(char[] alphabets) {

this.alphabets = alphabets;

states = new ArrayList<String>();

acceptingStates = new ArrayList<String>();

transitions = new ArrayList<Transition>();

/**

* @author Adrineel Saha

*/

public char[] getAlphabets() {

return alphabets;

/**

* @author Adrineel Saha

*
*/

public ArrayList<String> getStates() {

return states;

/**

* @author Adrineel Saha

*/

public void setStates(ArrayList<String> states) {

this.states = states;

/**

* @author Adrineel Saha

*/

public void addState(String state) {

this.states.add(state);

/**

* @author Adrineel Saha

*/

public void addStates(ArrayList<String> list) {

states.addAll(list);

}
/**

* @author Adrineel Saha

*/

public ArrayList<String> getAcceptingStates() {

return acceptingStates;

/**

* @author Adrineel Saha

*/

public void setAcceptingStates(ArrayList<String> acceptingStates) {

this.acceptingStates = acceptingStates;

/**

* @author Adrineel Saha

*/

public void addAcceptingStates(ArrayList<String> list) {

this.acceptingStates.addAll(list);

/**

* @author Adrineel Saha *


*/

public void addAcceptingState(String state) {

this.acceptingStates.add(state);

/**

* @author Adrineel Saha

*/

public String getStartingState() {

return startingState;

/**

* @author Adrineel Saha

*/

public void setStartingState(String startingState) {

this.startingState = startingState;

/**

* @author Adrineel Saha

*/

public ArrayList<Transition> getTransitions() {

return transitions;

}
/**

* @author Adrineel Saha

*/

public void setTransitions(ArrayList<Transition> transitions) {

this.transitions = transitions;

public void addTransitions(ArrayList<Transition> list) {

this.transitions.addAll(list);

/**

* @author Adrineel Saha

*/

public void addTransition(Transition item) {

this.transitions.add(item);

/**

* @author Adrineel Saha

*/

public void optimizeStatesName() {

ArrayList<String> newnewStates = new ArrayList<String>();

for (int i = 0; i < states.size(); i++) {


String oldStateName = states.get(i);

String newStateName = String.valueOf(i + 1);

newnewStates.add(newStateName);

ArrayList<String> newAcceptingStates = new ArrayList<String>();

for (int j = 0; j < acceptingStates.size(); j++) {

if (acceptingStates.get(j).equals(oldStateName)) {

newAcceptingStates.add(newStateName);

} else {

newAcceptingStates.add(acceptingStates.get(j));

setAcceptingStates(newAcceptingStates);

for (int j = 0; j < transitions.size(); j++) {

if (transitions.get(j).getFrom().equals(oldStateName))

transitions.get(j).setFrom(newStateName);

if (transitions.get(j).getTo().equals(oldStateName))

transitions.get(j).setTo(newStateName);

if (startingState.equals(oldStateName))

startingState = newStateName;

setStates(newnewStates);

/**

* @author Adrineel Saha

*/

@Override

public String toString() {


StringBuilder builder = new StringBuilder();

builder.append("States:\n");

for (int i = 0; i < getStates().size(); i++) {

builder.append(getStates().get(i) + ", ");

builder.append("\n---------------------\n");

builder.append("Starting State:\n");

builder.append(getStartingState());

builder.append("\n---------------------");

builder.append("\nAccepting States:\n");

for (int i = 0; i < getAcceptingStates().size(); i++) {

builder.append(getAcceptingStates().get(i) + ", ");

builder.append("\n---------------------");

builder.append("\nTransitions:");

for (int i = 0; i < getTransitions().size(); i++) {

builder.append("\n" + getTransitions().get(i).toString());

return builder.toString();

Codes Under D:\FA-Drawer-master\src\GUI :-

FAFrame.java:-

package GUI;

import java.awt.Dimension;

import java.awt.FlowLayout;
import java.awt.event.ActionEvent;

import java.awt.event.ActionListener;

import java.awt.event.ComponentEvent;

import java.awt.event.ComponentListener;

import javax.swing.BorderFactory;

import javax.swing.ButtonGroup;

import javax.swing.JButton;

import javax.swing.JFrame;

import javax.swing.JLabel;

import javax.swing.JPanel;

import javax.swing.JRadioButton;

import javax.swing.JTextField;

import javax.swing.SpringLayout;

import javax.swing.UIManager;

import javax.swing.UnsupportedLookAndFeelException;

import javax.swing.event.DocumentEvent;

import javax.swing.event.DocumentListener;

import Core.FA;

import Core.DFA.DFABuilder;

import Core.NFA.NFA;

import Core.NFA.NFABuilder;

/**

* This class is the main frame of the application. You just need to initialize

* it with it's constructor and boom... You're ready to go!

* @author Adrineel Saha

*
*

*/

public class FAFrame extends JFrame {

private static final long serialVersionUID = -6559694410472964579L;

public FAFrame() {

super("Adrineel Saha's FA Drawer");

try {

UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());

} catch (ClassNotFoundException | InstantiationException | IllegalAccessException

| UnsupportedLookAndFeelException e) {

e.printStackTrace();

setPreferredSize(new Dimension(900, 600));

pack();

setExtendedState(JFrame.MAXIMIZED_BOTH);

setDefaultCloseOperation(EXIT_ON_CLOSE);

init();

setVisible(true);

/**

* Initializes components of the JFrame.

* @author Mehran Mahmoudi

*/

private void init() {

final JPanel leftPane = new JPanel();

JPanel regPan = new JPanel();


regPan.setBorder(BorderFactory.createTitledBorder("Regular Expression"));

JLabel enterAlphas = new JLabel("Enter language alphabets, seprated with


commas:");

final JTextField alphaText = new JTextField("a,b");

alphaText.setPreferredSize(new Dimension(250, (int) alphaText.getPreferredSize()

.getHeight()));

JLabel enterRegex = new JLabel("Enter regular expression:");

final JTextField regexText = new JTextField("a*b*");

regexText.setPreferredSize(new Dimension(250, (int) regexText.getPreferredSize()

.getHeight()));

JPanel faTypePan = new JPanel();

faTypePan.setBorder(BorderFactory.createTitledBorder("FA Type"));

JLabel chooseFAType = new JLabel("Choose FA type:");

ButtonGroup gp = new ButtonGroup();

JRadioButton nfaButton = new JRadioButton("NFA");

nfaButton.setSelected(true);

final JRadioButton dfaButton = new JRadioButton("DFA");

final JRadioButton minidfaButton = new JRadioButton("Minimized DFA");

// minidfaButton.setEnabled(false);

gp.add(nfaButton);

gp.add(dfaButton);

gp.add(minidfaButton);

final JButton plotButton = new JButton("Plot!");

// plotButton.setEnabled(false);

final JPanel rightPane = new JPanel();

rightPane.setBorder(BorderFactory.createTitledBorder("Finite Automaton"));

final FAPanel faPanel = new FAPanel();

regPan.setLayout(new VerticalFlowLayout());

regPan.add(enterAlphas);
regPan.add(alphaText);

regPan.add(enterRegex);

regPan.add(regexText);

faTypePan.setLayout(new VerticalFlowLayout());

faTypePan.add(chooseFAType);

faTypePan.add(nfaButton);

faTypePan.add(dfaButton);

faTypePan.add(minidfaButton);

leftPane.setLayout(new VerticalFlowLayout());

leftPane.add(regPan);

leftPane.add(faTypePan);

leftPane.add(plotButton);

rightPane.setLayout(new FlowLayout());

rightPane.add(faPanel);

getContentPane().setLayout(new SpringLayout());

getContentPane().add(leftPane);

getContentPane().add(rightPane);

regexText.getDocument().addDocumentListener(new DocumentListener() {

@Override

public void removeUpdate(DocumentEvent arg0) {

if (regexText.getText().trim().equals("") ||
alphaText.getText().trim().equals("")) {

plotButton.setEnabled(false);

} else

plotButton.setEnabled(true);

}
@Override

public void insertUpdate(DocumentEvent arg0) {

if (regexText.getText().trim().equals("")) {

plotButton.setEnabled(false);

} else

plotButton.setEnabled(true);

@Override

public void changedUpdate(DocumentEvent arg0) {

});

alphaText.getDocument().addDocumentListener(new DocumentListener() {

@Override

public void removeUpdate(DocumentEvent arg0) {

if (regexText.getText().trim().equals("") ||
alphaText.getText().trim().equals("")) {

plotButton.setEnabled(false);

} else

plotButton.setEnabled(true);

@Override

public void insertUpdate(DocumentEvent arg0) {

if (regexText.getText().trim().equals("")) {

plotButton.setEnabled(false);

} else

plotButton.setEnabled(true);
}

@Override

public void changedUpdate(DocumentEvent arg0) {

});

getContentPane().addComponentListener(new ComponentListener() {

@Override

public void componentShown(ComponentEvent e) {

// TODO Auto-generated method stub

@Override

public void componentResized(ComponentEvent e) {

rightPane.setSize(getContentPane().getWidth() -
leftPane.getWidth() - 15,

getContentPane().getHeight() - 15);

rightPane.setPreferredSize(new
Dimension(getContentPane().getWidth()

- leftPane.getWidth() - 15,
getContentPane().getHeight() - 15));

rightPane.setLocation(leftPane.getWidth() + 5,
getContentPane().getY() + 5);

faPanel.setSize(rightPane.getWidth() - 40, rightPane.getHeight() -


50);

faPanel.setPreferredSize(new Dimension(rightPane.getWidth() -
40, rightPane

.getHeight() - 50));

faPanel.setLocation(20, 30);
}

@Override

public void componentMoved(ComponentEvent e) {

// TODO Auto-generated method stub

@Override

public void componentHidden(ComponentEvent e) {

// TODO Auto-generated method stub

});

plotButton.addActionListener(new ActionListener() {

@Override

public void actionPerformed(ActionEvent e) {

try {

String alphabets = alphaText.getText();

String regex = regexText.getText();

String[] alphas = alphabets.split(",");

char[] realAlhpas = new char[alphas.length];

for (int i = 0; i < realAlhpas.length; i++) {

realAlhpas[i] = alphas[i].trim().charAt(0);

FA fa = null;

NFABuilder nfaBuilder = new NFABuilder(regex,


realAlhpas);

fa = nfaBuilder.toNFA();
if (dfaButton.isSelected()) {

NFA nfa = (NFA) fa;

DFABuilder dfaBuilder = new DFABuilder(nfa);

fa = dfaBuilder.getDFAfromNFA();

if (minidfaButton.isSelected()) {

NFA nfa = (NFA) fa;

DFABuilder dfaBuilder = new DFABuilder(nfa);

DFABuilder minBuilder = new


DFABuilder(dfaBuilder.getDFAfromNFA());

fa = minBuilder.getMinimizedDFAfromDFA();

faPanel.setFA(fa);

} catch (NullPointerException E) {}

});

FAPanel.java:-

package GUI;

import java.awt.Color;

import java.awt.Graphics;

import java.awt.Graphics2D;

import java.awt.RenderingHints;

import java.awt.event.ComponentEvent;

import java.awt.event.ComponentListener;

import java.awt.geom.QuadCurve2D;

import java.util.ArrayList;
import javax.swing.JComponent;

import Core.FA;

import Core.Transition.Transition;

/**

* This is a JComponent. It basically gets an {@link FA} and draw it in its

* area.

* @author Adrineel Saha

*/

public class FAPanel extends JComponent {

private static final long serialVersionUID = 8009241661275578342L;

private StateView startingState;

private StateView[] states;

private ArrayList<Transition> transitions;

private final static int STATES_DIAMETER = 70;

private final static int STATES_GAP = 50;

private final static int BORDER_WIDTH = 30;

public FAPanel() {

super();

addComponentListener(new ComponentListener() {

@Override

public void componentShown(ComponentEvent event) {

}
@Override

public void componentResized(ComponentEvent event) {

reorderComponents();

repaint();

@Override

public void componentMoved(ComponentEvent event) {

@Override

public void componentHidden(ComponentEvent event) {

});

/**

* @author Adrineel Saha

*/

public void setFA(FA fa) {

states = null;

startingState = null;

transitions = null;

removeAll();

states = new StateView[fa.getStates().size()];

for (int i = 0; i < fa.getStates().size(); i++) {

states[i] = new StateView(fa.getStates().get(i),


fa.getAcceptingStates().contains(

fa.getStates().get(i)), STATES_DIAMETER);
if (fa.getStartingState().equals(fa.getStates().get(i))) {

startingState = states[i];

add(states[i]);

transitions = new ArrayList<Transition>();

transitions.addAll(fa.getTransitions());

reorderComponents();

repaint();

/**

* @author Adrineel Saha

*/

private void reorderComponents() {

try {

if (getWidth() > 0 && getHeight() > 0) {

int horizontalCount = (getWidth() - 2 * BORDER_WIDTH)

/ (STATES_DIAMETER + STATES_GAP);

for (int i = 0; i < states.length; i++) {

int noOfStateColumns = states.length < horizontalCount ?


states.length

: horizontalCount;

int noOfStateRows = (states.length - 1) / horizontalCount +


1;

int wholeStatesWidth = noOfStateColumns * (STATES_GAP


+ STATES_DIAMETER)

- STATES_GAP;
int wholeStatesHeight = noOfStateRows * (STATES_GAP +
STATES_DIAMETER)

- STATES_GAP;

int x = ((i % horizontalCount) * ((i % horizontalCount == 0 ?


0 : STATES_GAP) + STATES_DIAMETER))

+ ((getWidth() / 2) - (wholeStatesWidth /
2));

int y = ((i / horizontalCount) * (STATES_GAP +


STATES_DIAMETER))

+ ((getHeight() / 2) - (wholeStatesHeight /
2));

states[i].setLocation(x, y);

} catch (Exception e) {

validate();

repaint();

/**

* @author Adrineel Saha

*/

private double distance(StateView s1, StateView s2) {

return Math.sqrt(Math.pow(s1.getX() - s2.getX(), 2) + Math.pow(s1.getY() -


s2.getY(), 2));

/**
*

* @author Adrineel Saha

*/

private StateView getStateView(String stateName) {

for (int i = 0; i < states.length; i++) {

if (states[i].getName().equals(stateName))

return states[i];

return null;

/**

* @author Adrineel Saha

*/

@Override

public void paint(Graphics g) {

super.paint(g);

if (states == null)

return;

g.setColor(Color.black);

Graphics2D g2 = (Graphics2D) g;

g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,

RenderingHints.VALUE_TEXT_ANTIALIAS_ON);

g2.setRenderingHint(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);

RenderingHints qualityHints = new


RenderingHints(RenderingHints.KEY_ANTIALIASING,

RenderingHints.VALUE_ANTIALIAS_ON);
qualityHints.put(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);

g2.setRenderingHints(qualityHints);

if (getWidth() > 0 && getHeight() > 0) {

mainFor: for (int i = 0; i < transitions.size(); i++) {

StateView s1 = getStateView(transitions.get(i).getFrom());

StateView s2 = getStateView(transitions.get(i).getTo());

int fromX = 0, fromY = 0, toX = 0, toY = 0;

int diagnoalDifference = (int) (Math.sqrt(2 *


Math.pow(STATES_DIAMETER / 2, 2)) - (STATES_DIAMETER / 2)) - 4;

int xCurve = 0;

int yCurve = 0;

int curveDefaultValue = 40;

int curveValue = (int) (curveDefaultValue * (distance(s1, s2) /


(STATES_DIAMETER + STATES_GAP)));

int arrow1X = 0, arrow1Y = 0, arrow2X = 0, arrow2Y = 0;

int arrowLength = 10;

int arrowShadow = (int) ((Math.sqrt(2 * Math.pow(arrowLength,


2))) / 2);

if (s2.getX() < s1.getX()) {

if (s2.getY() < s1.getY()) {

fromX = s1.getX() + diagnoalDifference;

fromY = s1.getY() + diagnoalDifference;

toX = s2.getX() + STATES_DIAMETER -


diagnoalDifference;

toY = s2.getY() + STATES_DIAMETER -


diagnoalDifference;

arrow1X = toX;

arrow1Y = toY + arrowLength;

arrow2X = toX + arrowLength;

arrow2Y = toY;

} else if (s2.getY() == s1.getY()) {


fromX = s1.getX() + diagnoalDifference;

fromY = s1.getY() + STATES_DIAMETER -


diagnoalDifference;

toX = s2.getX() + STATES_DIAMETER -


diagnoalDifference;

toY = s2.getY() + STATES_DIAMETER -


diagnoalDifference;

arrow1X = toX;

arrow1Y = toY + arrowLength;

arrow2X = toX + arrowLength;

arrow2Y = toY;

yCurve = curveValue;

} else if (s2.getY() > s1.getY()) {

fromX = s1.getX() + diagnoalDifference;

fromY = s1.getY() + STATES_DIAMETER -


diagnoalDifference;

toX = s2.getX() + STATES_DIAMETER -


diagnoalDifference;

toY = s2.getY() + diagnoalDifference;

arrow1X = toX;

arrow1Y = toY - arrowLength;

arrow2X = toX + arrowLength;

arrow2Y = toY;

} else if (s2.getX() == s1.getX()) {

if (s2.getY() < s1.getY()) {

fromX = s1.getX() + diagnoalDifference;

fromY = s1.getY() + diagnoalDifference;

toX = s2.getX() + diagnoalDifference;

toY = s2.getY() + STATES_DIAMETER -


diagnoalDifference;

xCurve = -curveValue;

arrow1X = toX;
arrow1Y = toY + arrowLength;

arrow2X = toX - arrowLength;

arrow2Y = toY;

} else if (s2.getY() == s1.getY()) {

fromX = s1.getX() + diagnoalDifference;

fromY = s1.getY() + STATES_DIAMETER -


diagnoalDifference;

toX = s2.getX() + STATES_DIAMETER -


diagnoalDifference;

toY = s2.getY() + STATES_DIAMETER -


diagnoalDifference;

yCurve = 60;

arrow1X = toX - arrowShadow;

arrow1Y = toY + arrowShadow;

arrow2X = toX;

arrow2Y = toY + arrowLength;

} else if (s2.getY() > s1.getY()) {

fromX = s1.getX() + STATES_DIAMETER -


diagnoalDifference;

fromY = s1.getY() + STATES_DIAMETER -


diagnoalDifference;

toX = s2.getX() + STATES_DIAMETER -


diagnoalDifference;

toY = s2.getY() + diagnoalDifference;

xCurve = curveValue;

arrow1X = toX;

arrow1Y = toY - arrowLength;

arrow2X = toX + arrowLength;

arrow2Y = toY;

} else if (s2.getX() > s1.getX()) {

if (s2.getY() < s1.getY()) {


fromX = s1.getX() + STATES_DIAMETER -
diagnoalDifference;

fromY = s1.getY() + diagnoalDifference;

toX = s2.getX() + diagnoalDifference;

toY = s2.getY() + STATES_DIAMETER -


diagnoalDifference;

arrow1X = toX;

arrow1Y = toY + arrowLength;

arrow2X = toX - arrowLength;

arrow2Y = toY;

} else if (s2.getY() == s1.getY()) {

fromX = s1.getX() + STATES_DIAMETER -


diagnoalDifference;

fromY = s1.getY() + diagnoalDifference;

toX = s2.getX() + diagnoalDifference;

toY = s2.getY() + diagnoalDifference;

yCurve = -curveValue;

arrow1X = toX;

arrow1Y = toY - arrowLength;

arrow2X = toX - arrowLength;

arrow2Y = toY;

} else if (s2.getY() > s1.getY()) {

fromX = s1.getX() + STATES_DIAMETER -


diagnoalDifference;

fromY = s1.getY() + STATES_DIAMETER -


diagnoalDifference;

toX = s2.getX() + diagnoalDifference;

toY = s2.getY() + diagnoalDifference;

arrow1X = toX;

arrow1Y = toY - arrowLength;

arrow2X = toX - arrowLength;

arrow2Y = toY;

}
}

String text = transitions.get(i).getLetter() + "";

for (int j = 0; j < transitions.size(); j++) {

if
(transitions.get(i).getFrom().equals(transitions.get(j).getFrom())

&&
transitions.get(i).getTo().equals(transitions.get(j).getTo())

&& transitions.get(i).getLetter() !=
transitions.get(j).getLetter()) {

text += "," + transitions.get(j).getLetter();

if (j < i)

continue mainFor;

int middleX = 0, middleY = 0;

middleX = (fromX + toX) / 2;

middleY = (fromY + toY) / 2;

QuadCurve2D.Double curve = new QuadCurve2D.Double(fromX,


fromY, middleX + xCurve,

middleY + yCurve, toX, toY);

g2.draw(curve);

g2.drawLine(toX, toY, arrow1X, arrow1Y);

g2.drawLine(toX, toY, arrow2X, arrow2Y);

int labelCenterX = middleX + xCurve / 2 + (13 * (int)


Math.signum(xCurve));

int labelCenterY = middleY + yCurve / 2 + (13 * (int)


Math.signum(yCurve));
int textWidth = (int) g2.getFontMetrics().getStringBounds(text,
g2).getWidth();

int textHeight = (int) g2.getFontMetrics().getStringBounds(text,


g2).getHeight();

int textX = labelCenterX - textWidth / 2;

int textY = labelCenterY + textHeight / 2;

g.drawString(text, textX, textY);

g2.drawLine(startingState.getX() - 40, startingState.getY() +


(STATES_DIAMETER / 2),

startingState.getX(), startingState.getY() +
(STATES_DIAMETER / 2));

g2.drawLine(startingState.getX(), startingState.getY() +
(STATES_DIAMETER / 2),

startingState.getX() - 10, startingState.getY() +


(STATES_DIAMETER / 2) - 6);

g2.drawLine(startingState.getX(), startingState.getY() +
(STATES_DIAMETER / 2),

startingState.getX() - 10, startingState.getY() +


(STATES_DIAMETER / 2) + 6);

Main.java:-

package GUI;

/**

* @author Adrineel Saha

*/

public class Main {


/**

* @author Adrineel Saha

*/

public static void main(String[] args) throws InterruptedException {

new FAFrame();

StateView.java:-

package GUI;

import java.awt.Dimension;

import java.awt.Font;

import java.awt.Graphics;

import java.awt.Graphics2D;

import java.awt.RenderingHints;

import javax.swing.JComponent;

/**

* @author Adrineel Saha

*/

public class StateView extends JComponent {

private static final long serialVersionUID = -7766232547627440410L;

private int diameter;

private String name;

private boolean isAccepting;


/**

* @author Adrineel Saha

*/

public StateView(String name) {

super();

this.name = name;

/**

* @author Adrineel Saha

*/

public StateView(String name, boolean isAccepting) {

this(name);

this.isAccepting = isAccepting;

/**

* @author Adrineel Saha

*/

public StateView(String name, boolean isAccepting, int diameter) {

this(name, isAccepting);

setDiameter(diameter);

}
/**

* @author Adrineel Saha

*/

public int getDiameter() {

return diameter;

/**

* @author Adrineel Saha

*/

public void setDiameter(int diameter) {

this.diameter = diameter;

setPreferredSize(new Dimension(diameter + 5, diameter + 5));

setSize(getPreferredSize());

/**

* @author Adrineel Saha

*/

public String getName() {

return name;

/**

*
* @author Adrineel Saha

*/

public void setName(String name) {

this.name = name;

/**

* @author Adrineel Saha

*/

public boolean isAccepting() {

return isAccepting;

/**

* @author Adrineel Saha

*/

public void setAccepting(boolean isAccepting) {

this.isAccepting = isAccepting;

/**

* @author Adrineel Saha

*/

@Override
public void paint(Graphics g) {

super.paint(g);

Graphics2D g2 = (Graphics2D) g;

g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,

RenderingHints.VALUE_TEXT_ANTIALIAS_ON);

g2.setRenderingHint(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);

RenderingHints qualityHints = new


RenderingHints(RenderingHints.KEY_ANTIALIASING,

RenderingHints.VALUE_ANTIALIAS_ON);

qualityHints.put(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);

g2.setRenderingHints(qualityHints);

g2.drawOval(0, 0, diameter, diameter);

Font font = g2.getFont();

font = new Font(font.getName(), Font.PLAIN, 20);

g2.setFont(font);

g2.drawString(

name,

(int) (diameter / 2 - g.getFontMetrics().getStringBounds(name,


g).getWidth() / 2) + 1,

(int) (diameter / 2 + g.getFontMetrics().getStringBounds(name,


g).getHeight() / 2) - 3);

if (isAccepting)

g2.drawOval(5, 5, diameter - 10, diameter - 10);

VerticalFlowLayout.java:-

package GUI;

import java.awt.Component;
import java.awt.Container;

import java.awt.Dimension;

import java.awt.Insets;

import java.awt.LayoutManager;

/**

* A flow layout arranges components in a directional flow, much like lines of

* text in a paragraph. The flow direction is determined by the container's

* <code>componentOrientation</code> property and may be one of two values:

* <ul>

* <li><code>ComponentOrientation.TOP_TO_BOTTOM</code>

* <li><code>ComponentOrientation.BOTTOM_TO_TOP</code>

* </ul>

* Flow layouts are typically used to arrange buttons in a panel. It arranges

* buttons horizontally until no more buttons fit on the same line. The line

* alignment is determined by the <code>align</code> property. The possible

* values are:

* <ul>

* <li>{@link #TOP TOP}

* <li>{@link #BOTTOM BOTTOM}

* <li>{@link #CENTER CENTER}

* <li>{@link #LEADING LEADING}

* <li>{@link #TRAILING TRAILING}

* </ul>

* <p>

*/

public class VerticalFlowLayout implements LayoutManager, java.io.Serializable {

/**

*/

private static final long serialVersionUID = -5509812898746067578L;


/**

* This value indicates that each row of components should be

* left-justified.

*/

public static final int TOP = 0;

/**

* This value indicates that each row of components should be centered.

*/

public static final int CENTER = 1;

/**

* This value indicates that each row of components should be

* right-justified.

*/

public static final int BOTTOM = 2;

/**

* <code>align</code> is the property that determines how each column

* distributes empty space. It can be one of the following three values:

* <ul>

* <code>TOP</code> <code>BOTTOM</code> <code>CENTER</code>

* </ul>

* @see #getAlignment

* @see #setAlignment

*/

int align; // This is the one we actually use

/**
* The flow layout manager allows a seperation of components with gaps. The

* horizontal gap will specify the space between components and between the

* components and the borders of the <code>Container</code>.

* @see #getHgap()

* @see #setHgap(int)

*/

int hgap;

/**

* The flow layout manager allows a seperation of components with gaps. The

* vertical gap will specify the space between rows and between the the rows

* and the borders of the <code>Container</code>.

* @see #getHgap()

* @see #setHgap(int)

*/

int vgap;

/**

* Constructs a new <code>VerticalFlowLayout</code> with a centered

* alignment and a default 5-unit horizontal and vertical gap.

*/

public VerticalFlowLayout() {

this(CENTER, 5, 5);

/**

* Constructs a new <code>VerticalFlowLayout</code> with the specified

* alignment and a default 5-unit horizontal and vertical gap. The value of

* the alignment argument must be one of <code>VerticalFlowLayout.TOP</code>


* , <code>VerticalFlowLayout.BOTTOM</code>, or

* <code>VerticalFlowLayout.CENTER</code>

* @param align

* the alignment value

*/

public VerticalFlowLayout(int align) {

this(align, 5, 5);

/**

* Creates a new flow layout manager with the indicated alignment and the

* indicated horizontal and vertical gaps.

* <p>

* The value of the alignment argument must be one of

* <code>VerticalFlowLayout.TOP</code>,

* <code>VerticalFlowLayout.BOTTOM</code>, or

* <code>VerticalFlowLayout.CENTER</code>.

* @param align

* the alignment value

* @param hgap

* the horizontal gap between components and between the

* components and the borders of the <code>Container</code>

* @param vgap

* the vertical gap between components and between the components

* and the borders of the <code>Container</code>

*/

public VerticalFlowLayout(int align, int hgap, int vgap) {

this.hgap = hgap;

this.vgap = vgap;
setAlignment(align);

/**

* Gets the alignment for this layout. Possible values are

* <code>VerticalFlowLayout.TOP</code>,

* <code>VerticalFlowLayout.BOTTOM</code> or

* <code>VerticalFlowLayout.CENTER</code>,

* @return the alignment value for this layout

* @see java.awt.VerticalFlowLayout#setAlignment

* @since JDK1.1

*/

public int getAlignment() {

return align;

/**

* Sets the alignment for this layout. Possible values are

* <ul>

* <li><code>VerticalFlowLayout.TOP</code>

* <li><code>VerticalFlowLayout.BOTTOM</code>

* <li><code>VerticalFlowLayout.CENTER</code>

* </ul>

* @param align

* one of the alignment values shown above

* @see #getAlignment()

* @since JDK1.1

*/

public void setAlignment(int align) {


this.align = align;

/**

* Gets the horizontal gap between components and between the components and

* the borders of the <code>Container</code>

* @return the horizontal gap between components and between the components

* and the borders of the <code>Container</code>

* @see java.awt.VerticalFlowLayout#setHgap

* @since JDK1.1

*/

public int getHgap() {

return hgap;

/**

* Sets the horizontal gap between components and between the components and

* the borders of the <code>Container</code>.

* @param hgap

* the horizontal gap between components and between the

* components and the borders of the <code>Container</code>

* @see java.awt.VerticalFlowLayout#getHgap

* @since JDK1.1

*/

public void setHgap(int hgap) {

this.hgap = hgap;

/**
* Gets the vertical gap between components and between the components and

* the borders of the <code>Container</code>.

* @return the vertical gap between components and between the components

* and the borders of the <code>Container</code>

* @see java.awt.VerticalFlowLayout#setVgap

* @since JDK1.1

*/

public int getVgap() {

return vgap;

/**

* Sets the vertical gap between components and between the components and

* the borders of the <code>Container</code>.

* @param vgap

* the vertical gap between components and between the components

* and the borders of the <code>Container</code>

* @see java.awt.VerticalFlowLayout#getVgap

*/

public void setVgap(int vgap) {

this.vgap = vgap;

/**

* Adds the specified component to the layout. Not used by this class.

* @param name

* the name of the component

* @param comp
* the component to be added

*/

public void addLayoutComponent(String name, Component comp) {

/**

* Removes the specified component from the layout. Not used by this class.

* @param comp

* the component to remove

* @see java.awt.Container#removeAll

*/

public void removeLayoutComponent(Component comp) {

/**

* Returns the preferred dimensions for this layout given the <i>visible</i>

* components in the specified target container.

* @param target

* the container that needs to be laid out

* @return the preferred dimensions to lay out the subcomponents of the

* specified container

* @see Container

* @see #minimumLayoutSize

* @see java.awt.Container#getPreferredSize

*/

public Dimension preferredLayoutSize(Container target) {

synchronized (target.getTreeLock()) {

Dimension dim = new Dimension(0, 0);

int nmembers = target.getComponentCount();


boolean firstVisibleComponent = true;

for (int i = 0; i < nmembers; i++) {

Component m = target.getComponent(i);

if (m.isVisible()) {

Dimension d = m.getPreferredSize();

dim.width = Math.max(dim.width, d.width);

if (firstVisibleComponent) {

firstVisibleComponent = false;

} else {

dim.height += vgap;

dim.height += d.height;

Insets insets = target.getInsets();

dim.width += insets.left + insets.right + hgap * 2;

dim.height += insets.top + insets.bottom + vgap * 2;

return dim;

/**

* Returns the minimum dimensions needed to layout the <i>visible</i>

* components contained in the specified target container.

* @param target
* the container that needs to be laid out

* @return the minimum dimensions to lay out the subcomponents of the

* specified container

* @see #preferredLayoutSize

* @see java.awt.Container

* @see java.awt.Container#doLayout

*/

public Dimension minimumLayoutSize(Container target) {

synchronized (target.getTreeLock()) {

Dimension dim = new Dimension(0, 0);

int nmembers = target.getComponentCount();

boolean firstVisibleComponent = true;

for (int i = 0; i < nmembers; i++) {

Component m = target.getComponent(i);

if (m.isVisible()) {

Dimension d = m.getMinimumSize();

dim.width = Math.max(dim.width, d.width);

if (firstVisibleComponent) {

firstVisibleComponent = false;

} else {

dim.height += vgap;

dim.height += d.height;

Insets insets = target.getInsets();

dim.width += insets.left + insets.right + hgap * 2;


dim.height += insets.top + insets.bottom + vgap * 2;

return dim;

/**

* Lays out the container. This method lets each <i>visible</i> component

* take its preferred size by reshaping the components in the target

* container in order to satisfy the alignment of this

* <code>VerticalFlowLayout</code> object.

* @param target

* the specified component being laid out

* @see Container

* @see java.awt.Container#doLayout

*/

public void layoutContainer(Container target) {

synchronized (target.getTreeLock()) {

Insets insets = target.getInsets();

int maxHeight = target.getSize().height - (insets.top + insets.bottom + vgap


* 2);

int nmembers = target.getComponentCount();

int x = insets.left + hgap;

int y = 0;

int columnWidth = 0;

int start = 0;

boolean ttb = target.getComponentOrientation().isLeftToRight();

for (int i = 0; i < nmembers; i++) {

Component m = target.getComponent(i);
if (m.isVisible()) {

Dimension d = m.getPreferredSize();

m.setSize(d.width, d.height);

if ((y == 0) || ((y + d.height) <= maxHeight)) {

if (y > 0) {

y += vgap;

y += d.height;

columnWidth = Math.max(columnWidth, d.width);

} else {

moveComponents(target, x, insets.top + vgap,


columnWidth, maxHeight - y,

start, i, ttb);

y = d.height;

x += hgap + columnWidth;

columnWidth = d.width;

start = i;

moveComponents(target, x, insets.top + vgap, columnWidth, maxHeight -


y, start,

nmembers, ttb);

/**

* Centers the elements in the specified row, if there is any slack.


*

* @param target

* the component which needs to be moved

* @param x

* the x coordinate

* @param y

* the y coordinate

* @param width

* the width dimensions

* @param height

* the height dimensions

* @param columnStart

* the beginning of the column

* @param columnEnd

* the the ending of the column

*/

private void moveComponents(Container target, int x, int y, int width, int height,

int columnStart, int columnEnd, boolean ttb) {

switch (align) {

case TOP:

y += ttb ? 0 : height;

break;

case CENTER:

y += height / 2;

break;

case BOTTOM:

y += ttb ? height : 0;

break;

for (int i = columnStart; i < columnEnd; i++) {


Component m = target.getComponent(i);

if (m.isVisible()) {

int cx;

cx = x + (width - m.getSize().width) / 2;

if (ttb) {

m.setLocation(cx, y);

} else {

m.setLocation(cx, target.getSize().height - y -
m.getSize().height);

y += m.getSize().height + vgap;

/**

* Returns a string representation of this <code>VerticalFlowLayout</code>

* object and its values.

* @return a string representation of this layout

*/

public String toString() {

String str = "";

switch (align) {

case TOP:

str = ",align=top";

break;
case CENTER:

str = ",align=center";

break;

case BOTTOM:

str = ",align=bottom";

break;

return getClass().getName() + "[hgap=" + hgap + ",vgap=" + vgap + str + "]";

Output:-

You might also like