|Homework #8||CMSC 131|
|Due December 7, 6:00 pm||Object-Oriented Programming I|
|Type of Homework: Open||Fall 2004|
History: (Look for "Update" tags.)
This homework will give you practice in parsing, inheritance, interfaces, event-driven programming, packages, ArrayList, and designs involving programs that use the MVC (Model-View-Controller) model of interaction.
Your company wants to build a suite of programs for various sorts of interactive calculators, e.g., a basic calculator, a scientific calculator, a financial calculator. Although this example is very simple, more complex software systems, such as Microsoft Office, involve a series of programs that share a common set of basic functions (e.g., text processing, editing, spelling and grammar checking) and are extended to form a number of different concrete applications, such as Word, PowerPoint, and FrontPage.)
To do this you will create a generic calculator class, called GenericCalcEng (Generic Calculator Engine), which provides a general framework for the implementation of several types of calculators. You will then extend this class to implement two concrete calculators, a basic mathematical calculator through BasicCalcEng (Basic Calculator Engine) and a scientific calculator through ScientificCalcEng (Scientific Calculator Engine).
We will provide a generic calculator GUI (GenericCalcGUI) to be used in the development of the user interface component of the calculators. Several classes, in addition to the GenericCalcEng, need to be implemented as part of this homework. More details about the program are provided in the Specifications section. You may want to take a look at the Sample Run section before you read the description.
This homework will be graded as follows:
Calculator operation model
In order to understand what you must implement, let's first review the structure of our calculator. The calculator is based on a GUI where user input comes from pressing buttons that contains digits ('0'-'9'), decimal point, various operators (+, /, -) which might be unary (acting on one argument) or binary (acting on two arguments), and various calculator-specific operations (backspace, clear). The calculator also has a display, where the results are shown. Your program's job will be to parse the user input into numbers, then perform the specified operations on these numbers, and finally display the appropriate results.
We can classify the operators into the following categories or types:
Our calculator uses floating-point calculations (using Java's type double) to store all operands. The user inputs operands using digits ('0'-'9') and/or a decimal point. For example, the following are valid operands: "123", "123.45", "0", "0.56". (There is no need for a '-' sign, since negation is handled by a unary operator "+/-".)
The model portion of your calculator will be organized around three classes: an abstract base class GenericCalcEng (Generic Calculator Engine), and two concrete classes derived from this: BasicCalcEng (Basic Calculator Engine) and ScientificCalcEng (Scientific Calculator Engine).
These classes will be discussed in greater detail below.
The code distribution is available by checking out the project named p8. The code distribution provides you with the following:
Here is a description of each of these components.
Calculator Library (We provide this.)
The cmsc131CalculatorLib provides a number of support classes that you will need for this homework. The classes you will find in this package are:
Java Files (We provide these.)
public CalcOperator(String operatorName, int operatorType)
This class also provides two accessor methods, getName( ) and getType( ), which return these values. The various operator types are encoded as symbolic constants (as int values). The operator type names for this assignment are:
For example, a binary operator such as "+" has the operator type CalcOperator.BINARY_OP.
The GenericCalcEng is an Java abstract class that implements the model component of the MVC paradigm for the generic (common) part of the calculator. The generic calculator can be tailored to recognize any unary and binary operators we want to implement. The generic calculator "parses" (that is, it syntactically classifies or recognizes) operators and operands and passes those values to methods in the derived classes that will take care of actually implementing the operator. The public methods of the GenericCalcEng class are:
public void addToOperatorList(String operatorName, int operatorType)
The parameter operatorName is the String name of the operator (e.g., "+" or "sqrt"). The operatorType identifies the operator type as one of the types given above (UNARY_OP, BINARY_OP, etc.), as given in the table above (see code distribution). The entire list of operators must be given before the calculator is actually used.
public void processSelection(String selection)
The method recognizes digits, the period and operators. It combines digit(s) and/or the period in order to generate operands. This method will also trigger the execution of any operator once the appropriate operands (if any) have been identified and will update, if need be, a string object representing the calculator display. Thus, processSelection is the core method of the GenericCalcEng class. This key method is described in greater detail below.
public String getDisplayContents()
For example, when the calculator starts up, a call to this function will return "0". After the use clicks on the button "4" (which causes processSelection("4") to be called) the new contents of the display will be "4". After clicking on "5" (causing the call processSelection("5")) the display contents will be "45".
public abstract String processUnaryOperator(String operand, String operatorName)
This method is called by processSelection once a unary operator and its operand have been identified. The processUnaryOperator method must implement the functionality of all the unary operators provided through the addToOperatorList method.
Note that the argument is passed in as a String (not double). Rather than converting digits to numbers immediately as each digit button is pressed, it is easier to concatenate the digits into a string, then pass the string to processUnaryOperator (or processBinaryOperator below), and then convert the result to a floating-point number (using Double.parseDouble).
public abstract String processBinaryOperator(String firstOperand, String operatorName, String secondOperand)
Feel free to add any additional private methods as you see fit to help you complete the implementation. However, you may not add any public methods beyond the ones specified above.
Further Discussion of the processSelection( ) Method
As mentioned above, the processSelection method does not compute the actual operation, since this is done in the concrete derived classes (either BasicCalcEng or ScientificCalcEng). For example, if the operator had been the binary operator "+", then processSelection will call a method in the derived class to evaluate the actual addition process. (See processUnaryOperator and processBinaryOperator methods above). On the other hand, operators that are deemed to be common to all calculators (e.g., ASSIGN_OP, CLEAR, CLEAR_LAST_OPERAND, BACKSPACE) are implemented by this method.
For purposes of testing, it is necessary that you implement your calculator functions in the same way that we do. If you consider the calculator's behavior when the user keys in the sequence "3 4 + 5 6 =" the number "34" is the first operand, the operator is "+", and the second operand is "56". When "=" is seen, the binary operator "+" is applied, and the resulting sum of "90" is displayed. How does the calculator know that "3" and "4" are parts of the first operand and "5" and "6" are parts of the second operand? The calculator is controlled by its state, which is made up of the following variables:
The calculator's operation depends on the context. The possible contexts are:
Semantics of GenericCalcEng operators: The calculator starts in the default state, which is described in the CLEAR operator below. Throughout, the term current operand refers to either firstOperand or secondOperand, depending on the current context, RFO or RSO, respectively.
Creating calculator engines using the GenericCalcEng class
In order to have an operational calculator we must extend the GenericCalcEng class. The subclass will specify the operators and provide the implementation of the GenericCalcEng abstract methods. The class CalcEngExample, part of the code distribution provides an example of creating a very simple calculator based on the GenericCalcEng.
Adding a GUI
Once you have implemented GenericCalcEng and one of its subclasses we have a fully operational calculator. However, it is not one that is easy to use. We have provided a class called GenericCalcGUI (Generic Calculator Graphical User Interface) that will enable you to create a complete calculator GUI for each of the two calculators (basic and scientific).
We will describe the process for the basic calculator. You will need to implement this for both the basic and scientific calculators, but the setup for the scientific calculator is exactly analogous (just replace "Basic" with "Scientific" below). As you read this, please refer to the file GUIExample.java (given in the code distribution). It illustrates how to create a simple GUI with a couple of buttons. The behavior of GUIExample.java is trivial, and simply echoes back the user's selection.
public void buttonSelected(String selection)
In order for your calculator to possess the desired functionality, you must create a second instance variable in your BasicCalc class. This variable stores a reference to a concrete instance of a GenericCalcEng class (either BasicCalcEng or ScientificCaleEng). Let's call it engine. In your BasicCalc constructor, create a such a new instance and assign it to engine. Now, when the buttonSelected() callback is called, it needs to perform the following operations:
The following figure illustrates how the various classes of this project interact at the user-interface level for the basic calculator. The picture is similar for the scientific calculator.
Classes you must define
For this homework you must define the following classes:
|+/-||unary||invert the operand's sign|
|C||clear||clear the calculator's state|
All the operations from the
plus the following:
|x^y||binary||x raised to the power y|
|log||unary||natural logarithm (base e)|
|sin||unary||sine of the operand (radians)|
|cos||unary||cosine of the operand (radians)|
|tan||unary||tangent of the operand (radians)|
|CE||clear_last_operand||clears last operand|
(Update Mon, 11/29, 8:30pm: The following requirement for no ".0" in the display has been corrected. It only applies to the scientific calculator.)
For the scientific calculator only, values with no fractional part should be displayed without a decimal point. This format requirement can be satisfied by using the java.text.DecimalFormat class, as the following example illustrates.
double result = 12.0;
DecimalFormat decimalFormat = new DecimalFormat();
decimalFormat.setGroupingUsed( false );
String str = decimalFormat.format(result);
A value of "12" will be stored in str (instead of "12.0"). The call to method setGroupingUsed( false ) is used to disable the automatic insertion of commas. With this call the value 12345678.9 would be converted to "12345678.9", and without it the value would be converted to "12,345,678.9" instead. You are not required to use this method, and will receive full credit either way.
(Update Fri, 12/03, 8:30pm: Added the above call to "setGroupingUsed( false )" and the explanation that follows.)
In processUnaryOperator and processBinaryOperator, while converting a string to double using Double.parseDouble, make sure that you catch the NumberFormatException that could be generated. The processing associated with the catch clause should be to return the string "Invalid argument format" and this should be shown in the calculator's window.
Packages you must define
In order to gain experience in designing programming project involving packages, you must create the following packages and arrange your classes as described below. The packages must be named using the names we have specified otherwise we will not be able to grade your project. (Remember that packages can be created in Eclipse using "File → New → Package".)
Here is an example that show two snapshots of the calculators you are expected to implement, shown in their initial states.
Testing Using CalculatorTests
To provide you with additional confidence that your program works correctly, we have provided a special class for testing purposes, called CalculatorTests. (The file contains a compilation error, which will go away when you implement the GenericCalcEng class.) Executing the main method of the class CalculatorTests will allow you to test the functionality of some of the class associated with your implementation. The results for each test can be found in the text files that came along with the code distribution.
Incidentally, these are the tests you will find in the submit server. Make sure that after submitting your program you verify your program pass the submit server tests.
Remember that you are not required to implement the following problem. Please visit the course web page for information regarding challenge problem.
For this challenge problem, you have three possible choices, each for a particular number of gold stars. If you want to implement them all that is fine, however we can only grade one. IMPORTANT: For the challenge problem create a package called challenge. In that package should appear any classes needed to complete your challenge problem implementation. The challenge problems below may require you to modify the GenericCalcEng and other classes of your homework. Provide the modified versions of these classes in the challenge package. In addition, make sure you provide, in the challenge package, a Challenge.java file with a main method that allow us to run the challenge problem calculator. Notice that for the challenge problem you must use the scientific calculator.
Choice 1 (1 Gold Star)
Add an option to the calculator that allows the display to show the results using commas. For example, instead of "12345" the user would see "12,345". You should add a button to the GUI labeled "com" that allows the user to turn this feature on and off.
Step 2 (1 additional Gold Star, for a total of 2)
In addition to Choice 1, add the following memory functions to the calculator infrastructure:
You should add three buttons to the GUI labeled "MS", "MR", and "MC", respectively.
Choice 3 (1 additional Gold Star, for a total of 3)
In addition to Choice 1 and Choice 2, add parenthesis semantics to the calculator infrastructure. That means your calculator can process expressions surrounded by parenthesis, regardless of the level of nesting. You should add two buttons to the GUI labeled "(" and ")".
Feel free to place the GUI buttons wherever you consider to be best.
Submit your project using the submit project option associated with Eclipse. Remember to complete your time log before submitting your homework.