import java.io.File;//Used for opening text function files.
import java.io.FileNotFoundException;//Throws the FileNotFound Exception
import java.util.Scanner;//Used to scan information from text function files.
import java.util.StringTokenizer;//Used to break up a file into delimited sections
//This class implements all the methods of NumStackInterface
public class NumStack implements NumStackInterface {
public NumNode head;//This keeps track of the front of the Linked List
private int length = 0;//This variable keeps track of the Stack length
private int opslength = 4;//This variable keeps track of operations that can be used (Default is 4 for +,-,*,/)
private String[] operations = new String[20];//This Array keeps track of operations
private int[] operandnum = new int [20];//This Array keeps track of the amount of operands needed for an operation
private boolean hasOperations = false; //This variable checks if any operations are provided
private double a,b,c = 0;//These variables are used to implement basic +,-,*,/ operations
private String defaultFilename = null;//This is set to true if a custom filename is given
{//Begin Array setup
operations[0] = "+"; //The Operations and Operandum arrays are set up with basic
operations[1] = "-"; //operations and the amounts of operands required to execute them.
operations[2] = "*"; //Default operations are +,-,*,/ Each requires 2 operands.
operations[3] = "/";
operandnum[0] = 2;
operandnum[1] = 2;
operandnum[2] = 2;
operandnum[3] = 2;
}//End Array setup
public NumStack(){//This Constructor is implemented whenever there is no file name input.
setConstructor();//Calls the setConstructor method
}//End NumStack Default constructor
public NumStack(String filename) {//This Constructor is implemented whenever there is a default file input.
defaultFilename = filename;//Sets the filename to be parsed for operations
setConstructor();//Calls the setConstructor method
}//End NumStack Custom Filename constructor
public void setConstructor() {
Scanner s = null;
try {//This section will attempt to scan in the operations from a file.
if (defaultFilename!=null) {//Is there a custom operations file?
s = new Scanner(new File(defaultFilename));//Custom filename will be searched for additional operations
}//End custom file section
else {//Use default operations file
s = new Scanner(new File("functions.txt"));//By default functions.txt will be searched for additional operations
}//End default file section
}//End Try
catch(FileNotFoundException func) {//This part will throw an exception if a file is not found
func.printStackTrace();
}//End Catch Exception
while (s.hasNextLine()) {//This part will place the scanned parts into Arrays
String line = s.nextLine();//The line to be delimitered.
String[] lineArray = line.split(" ");//The delimiter is set to a blank space
operations[opslength] = lineArray[0];//The Operations are placed in this array
Integer parser = Integer.parseInt(lineArray[1]);//The Operand count is parsed as an integer
operandnum[opslength] = parser;//The Operand count required is placed in this array
opslength++;//Keeps track of the amount of Operations
}//End While loop
}
//This method checks if the Stack is empty
public boolean isEmpty() {
if (length == 0) {//If length is 0 then return true
return true;
}
return false;//If length is greater than 0, then the Stack has data
}//End isEmpty() method
//This method returns the value of the top object in the stack
public double peek() {
if (isEmpty()) {
throw new NullPointerException("The Stack is Empty and cannot be Peeked");
}
return head.num;//Returns the head numerical value.
}//End peek() method
//This method will return the value of the top object in the stack and remove it from the stack
public double pop() {
double returnVal = head.num;
head = head.next;//Sets the head to head.next making the original head value garbaged
length--;//Downcounts the length when an object is removed from the stack.
return returnVal;
}//End pop() method
//This method will add a new object to the stack.
public void push(double d) {
NumNode newNumNode = new NumNode(d);//Creates a new node instance
newNumNode.next = head;//This ensures that there is a link to the rest of the data
head = newNumNode;//The newNode is now set as the head
length++;//Increments the length as a new node is pushed.
}//End push() method
//This method will evaluate a function written in postfix notation
public double evaluatePostfix(String postfix) {
this.clear(); //Clears the list for a new postfix evaluation
StringTokenizer input = new StringTokenizer(postfix);//Separates the input into delimited parts
while (input.hasMoreTokens()) {//While more delimited sections exist
String token = input.nextToken();
try {//Tries to see if the input is a number
Double d = Double.parseDouble(token);//Parses the input to see if it is of type double
this.push(d);//Pushes the numerical value onto the stack
}//End Try
catch (Exception e){//Catches the exception
int index = 0;//Index that checks whether all known operations have been compared to the input
while (opslength > index) {//Goes through all known operations in the Operations Array to see if they apply to the input
if (operations[index].equals(token) ) {//Does the input token match a known operation?
if ( length >= operandnum[index]) {//Are there enough operands for the operation to succeed?
if (index < 4) {//Do not use MathMethod for the primitive +,-,*,/ operations
if (token.equals("+")) {//Is the primitve operation "+" ?
c = this.pop() + this.pop();
this.push(c);
hasOperations = true;//An operation has been detected
break;//Ends the loop - correct operation found
}//End "+" primitive statement
else if (token.equals("-")) {//Is the primitve operation "-" ?
a = this.pop();
b = this.pop();
c = b - a;
this.push(c);
hasOperations = true;//An operation has been detected
break;//Ends the loop - correct operation found
}//End "-" primitive statement
else if (token.equals("*")) {//Is the primitve operation "*" ?
c = (this.pop() * this.pop());
this.push(c);
hasOperations = true;//An operation has been detected
break;//Ends the loop - correct operation found
}//End "*" primitive statement
else if (token.equals("/")) {//Is the primitve operation "/" ?
a = this.pop();
b = this.pop();
c = b/a;
this.push(c);
hasOperations = true;//An operation has been detected
break;//Ends the loop - correct operation found
}//End "/" primitive statement
}//End Primitive operations section
else if (index >= 4) {//Use MathMethod for Complex operations
double[] mArgs = new double[operandnum[index]];//The array to send data to the MathMethod for evaluation
MathMethod mm = new MathMethod(operations[index],operandnum[index]);//Creates an instance of MathMethod with the function and amount of operands required
for (int j = 0; mArgs.length > j; j++) {//This loop will create an appropriate sized array with operand data (Either 1 or 2).
mArgs[j] = this.pop();
}//End For Loop
double toPush = mm.call(mArgs);//Calls the MathMethod with the correct operand Array
this.push(toPush);
hasOperations = true;//An operation has been detected
break;//Ends the loop - correct operation found
}//Ends Complex operations section
}//End Enough operands found section
else {//Not Enough operands found for the operation to succeed
throw new IllegalPostfixExpressionException("Not enough operands to complete operation");
}//End throw exception
}//End Valid Operation found
index++;//Increment known operation count
}//End Check for Valid Operations loop
if (index >= opslength) {//If the index went through all the possible operation the input is malformed
throw new IllegalArgumentException("Your input <" + token + "> is malformed");
}//End throw exception
}//End Catch Exception
}//End Tokenizer input
if (!hasOperations) {//If there are no operations detected throw a postfix exception
throw new IllegalPostfixExpressionException("No Operations provided");
}//End throw exception
return this.pop();//Returns the final result
}//End evaluatePostfix() method
//Simple method that returns the length of the Stack
public void length(){
System.out.println("Length is <" + length + ">");
}//End length() method
//Simple method that clears the entire list
public void clear() {
head = null;
length = 0;
}//End clear() method
//Simple debugging display method
public void display(){
if (isEmpty()) {//If the stack is empty throw an exception
throw new NullPointerException("The Stack is Empty and cannot be displayed");
}//End throw exception
else {//Display the Stack
NumNode temp = new NumNode();
temp = head;//Makes a copy of the stack
int index = 0;
System.out.println("Displaying the Stack - the Arrows point to the head");
System.out.print("[ ");
while (length > index) {//The copy is disassembled and displayed
System.out.print(temp.num + " <== ");
temp = temp.next;
index++;
}//End Display Loop
System.out.print(" ]");
System.out.println();
}//End whether to display statement
}//End display() method
} //End NumStack class