Download | Plain Text | Line Numbers


/*
 * Copyright (c) 2008, Manuel Mausz <manuel at mausz.at>
 * 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<String, String> operators = new HashMap<String, String>();
  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();
  }
}