/* * Copyright (c) 2008, Manuel Mausz * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the copyright holders nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. */ import java.util.HashMap; import java.lang.reflect.Method; /** * Implements a RPN Calculator (stack based) * UPN is the german name for RPN * See http://en.wikipedia.org/wiki/Reverse_Polish_notation for more info * * @version 1.0 * @author Manuel Mausz (manuel at mausz.at) * @author http://manuel.mausz.at/ */ class UpnCalculator { private Stack stack = new Stack(20); private static HashMap operators = new HashMap(); private String errorMsg = null; /** * constructor * adds the supported operators and their method name * to the internal hashmap */ UpnCalculator() { operators.put("+", "opAdd"); operators.put("-", "opSub"); operators.put("*", "opMul"); operators.put("/", "opDiv"); } /** * check wheter the passed operator is supported * * @param String operator * @return true if supported, false otherwise */ public static boolean supportsOperator(String op) { return operators.containsKey(op); } /** * push operand to the internal stack * * @param int operator */ public void addOperand(int op) { stack.push(op); } /** * push operator to the internal stack * and invoke the method assigned to the operator * * @param String operator * @return true on success, false otherwise */ public boolean addOperator(String op) { if (!supportsOperator(op)) { setError("Unknown operator"); return false; } int op1, op2; if (!stack.hasNext()) { setError("Insuifficent operands on stack"); return false; } op1 = stack.pop(); if (!stack.hasNext()) { /* NOTE: * EPROG specification doesn't specify whether the unused operand should * be pushed back on stack. I think it should BUT eprog test cases don't! */ //stack.push(op1); setError("Insuifficent operands on stack"); return false; } op2 = stack.pop(); String methodname = operators.get(op); Object ret; try { Method method = this.getClass().getMethod(methodname, new Class[] { int.class, int.class }); ret = method.invoke(this, new Object[] { op1, op2 } ); } catch(Exception e) { setError(e.getMessage()); return false; } if (!(ret instanceof Boolean)) { setError("Unknown datatype returned from operation"); return false; } return (Boolean)ret; } /** * Addition * * @param op1 * @param op2 * @return true on success, false otherwise */ public boolean opAdd(int op1, int op2) { stack.push(op2 + op1); return true; } /** * Subtraction * * @param op1 * @param op2 * @return true on success, false otherwise */ public boolean opSub(int op1, int op2) { stack.push(op2 - op1); return true; } /** * Multiplication * * @param op1 * @param op2 * @return true on success, false otherwise */ public boolean opMul(int op1, int op2) { stack.push(op2 * op1); return true; } /** * Divison * * @param op1 * @param op2 * @return true on success, false otherwise */ public boolean opDiv(int op1, int op2) { if (op1 == 0) { setError("Division by zero"); return false; } stack.push(op2 / op1); return true; } /** * check if an error has occured * use getLastError() to get the error string * * @return boolean */ public boolean hasError() { return (errorMsg != null); } /** * returns error message * * @return string */ public String getLastError() { return errorMsg; } /** * reset internal error state */ public void resetError() { setError(null); } /** * set error string * * @param msg error string */ private void setError(String msg) { errorMsg = msg; } /** * returns internal stack converted to string * * @return string */ public String toString() { return stack.toString(); } }