Standard Stack
Try to use the standard implementation of java.util.Stack. You have reinvented the wheel here.
State Pattern
Introduce the state pattern. I here provide you an example.
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
public class NumberParser {
public static void main(String[] args) {
List<BigInteger> parse = new NumberParser(" 10 22 32 ").parse();
for (BigInteger bigInteger : parse) {
System.out.println(bigInteger);
}
}
private List<BigInteger> numbers;
private final StringBuffer stringToParse;
private final StringBuffer buffer;
private State state;
public NumberParser(String string) {
this.stringToParse = new StringBuffer(string);
this.buffer = new StringBuffer();
this.setState(new Unknown());
}
private boolean hasCurrentChar() {
return this.stringToParse.length() > 0;
}
private char removeCurrentChar() {
if (hasCurrentChar()) {
char ch = this.stringToParse.charAt(0);
this.stringToParse.deleteCharAt(0);
return ch;
} else {
throw new NoSuchElementException();
}
}
private char currentChar() {
if (this.stringToParse.length() > 0) {
return this.stringToParse.charAt(0);
} else {
throw new NoSuchElementException();
}
}
private void clearBuffer() {
buffer.setLength(0);
}
private void recognizeNumber() {
numbers.add(new BigInteger(buffer.toString()));
clearBuffer();
}
public List<BigInteger> parse() {
if (numbers == null) {
this.numbers = new ArrayList<>();
while (!(getState() instanceof End)) {
getState().parse();
}
}
return this.numbers;
}
private State getState() {
return state;
}
private void setState(State state) {
System.out.println(state.getStateInfo());
this.state = state;
}
private interface State {
public String getStateInfo();
public void parse();
}
private interface End extends State {
}
private class Error implements End {
@Override
public String getStateInfo() {
return "Something went wrong ...";
}
@Override
public void parse() {
}
}
private class NoMoreChars implements End {
@Override
public String getStateInfo() {
return "No chars left.";
}
@Override
public void parse() {
}
}
private class RemoveWhiteSpaces implements State {
@Override
public String getStateInfo() {
return "Removing white spaces.";
}
@Override
public void parse() {
if (hasCurrentChar()) {
if (Character.isWhitespace(currentChar())) {
removeCurrentChar();
} else {
setState(new Unknown());
}
} else {
setState(new NoMoreChars());
}
}
}
private class Number implements State {
@Override
public String getStateInfo() {
return "Parse digits.";
}
@Override
public void parse() {
if (hasCurrentChar()) {
if (Character.isDigit(currentChar())) {
buffer.append(currentChar());
removeCurrentChar();
} else {
recognizeNumber();
setState(new Unknown());
}
} else {
recognizeNumber();
setState(new NoMoreChars());
}
}
}
private class Unknown implements State {
@Override
public String getStateInfo() {
return "Search ...";
}
@Override
public void parse() {
if (hasCurrentChar()) {
if (Character.isWhitespace(currentChar())) {
setState(new RemoveWhiteSpaces());
} else if (Character.isDigit(currentChar())){
setState(new Number());
} else {
setState(new Error());
}
} else {
setState(new NoMoreChars());
}
}
}
}
This parser searches for numbers and returns them. Whitespaces separate numbers as whitespaces are allowed to occur multiple times. If you input alphanumeric characters the machine goes into the Error-State.
The main point is to avoid nested if-statements and have clear responsibilities during parsing. Furthermore your solution lacks extendability. If new requirements occur the state pattern helps you to manage them in a well-defined way.
First I suggest to have a state chart. UML state machines are appropriate.