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