|Homework #8||CMSC 131|
|Due May 9, 6:00 pm||Object-Oriented Programming I|
|Type of Homework: Open||Spring 2005|
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.
In case you have not already noticed, this is an Open homework. You must read the Open/Closed policy before working on this project. The policy can be found at policy.
Your company wants to build a suite of interactive calculators, e.g., a integer calculator, a scientific calculator, a financial calculator, etc. For this homework, 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. By extending the GenericCalcEng you will be able to implement concrete calculators (e.g., scientific, financial, etc.).
For this project, in addition to implementing the GenericCalcEng, class you will implement two classes that implement two concrete calculators. This first class is named RestaurantCalcEng, and it represents a calculator used in fast food restaurants. The second one is named ScientificCalcEng, and represents a typical scientific calculator.
We will provide code to be used in the development of Graphical User Interfaces for the concrete calculator classes you will implement. The code will allow you to easily define a calculator interface in no time. More details about the program are provided in the Specifications section.
Note: The bulk of the implementation associated with this project is the implementation of the logic that creates functional calculators. This logic is what the JUnit module (that you will create) will test. Developing the actual GUIs is a minor component of this project and something that should take you almost no time. For example, if you choose not to implement the GUIs for the calculators you will notice that you will pass all the tests in the submit server, as we don't check for that component via the submit server. The graders will check the GUIs manually. Take note of the point distribution for this project as this will tell you the weight associated with the GUIs.
This homework will be graded as follows:
Calculator Operation Model
Note: In the description below an operand represents a numeric value and an operator any user-defined computation (+, -, factorial, etc.)
Most numerical calculators process the following type of operations:
Before completing any calculations, all calculators need to identify whether a unary, binary, or no-operand operation is being requested, along with the operand(s) (if any). Once the identification has been made, the actual processing (determined by the operand) will take place. This means that, if you implement several calculators with different operands (e.g., a scientific, restaurant and binary calculators), parts of your processing will be common to all calculators. Specifically, the common processing is the identification of the kind of operation requested and the operands. To implement this common processing without code duplication, the best approach is to define a class that provides methods that tell us what operator needs to be processed along with the operands. By using this class, we need only worry about implementing the computation associated with each particular operator. For this project you will implement such a class. We name this class GenericCalcEng (Generic Calculator Engine), because, as its name implies, it provides the generic behavior associated with any calculator. The class identifies operators and operands, and provides support for common operations found in any calculator (e.g., processing of backspaces, reset, etc.). This class cannot generate any results. It just identifies what needs to be done and relies on a subclass to actually implement the processing associated with an operator.
Implementing a calculator using GenericCalcEng
Let's suppose you want to implement a simple floating-point calculator that supports the operators +, -, *, /, =, backspace, and reset. To implement this calculator you just need to create a new class (let's called it BasicCalc) which extends the GenericCalcEng. By extending GenericCalcEng, BasicCalc doesn't need to implement the backspace or the reset operation as those are already provided by the base class. Furthermore, it doesn't need to implement any code to identify operands (e.g., reading digits) or code that identifies operators. All this processing is taken care of by GenericCalcEng. What does BasicCalc implement? It provides the GenericCalcEng with a list of the operators (the name and type) the BasicCalc will process. In addition, it provides the implementation of abstract methods present in the GenericCalcEng class. One of these methods takes care of processing unary operations, the other takes care of binary operations and a third one is in charge of processing constants. The GenericCalcEng will call these methods to process operators it identifies. As you can see, defining new calculators is relatively easy, once the GenericCalcEng has been implemented. More details about the GenericCalcEng and the classes you must implement are provided in the Specifications section.
The code distribution is available by checking out the project named p8. The code distribution provides you with the following:
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.
You will implement GenericCalcEng, a Java abstract class that represents 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 implementing the operator.
We can classify the GenericCalcEng operators into the following categories or types:
The GenericCalcEng operands are defined by the digits 0 through 9 and/or a decimal point. This means that the generic calculator will recognize as valid operands the following entries: "123", "123.45", "0", "0.56". There is no need for a '-' sign, since negation (if needed) is handled at the operator level (e.g., unary +/-).
GenericCalcEng Public Operands
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. You may assume the operator input is valid. 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 user 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 or integer). 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 number. You may assume the operator input is valid.
public abstract String processBinaryOperator(String firstOperand, String operatorName, String secondOperand)
public abstract String processConstantOperator(String operatorName)
Feel free to add any additional private methods as you see fit. 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 apply any binary or unary operators, since this is done in the concrete derived classes (e.g., ScientificCalcEng). For example, if the operator is the binary operator "+", then processSelection will call a method in the derived class to apply the addition operator. (See processUnaryOperator, processBinaryOperator and processConstantOperator 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:
Keep in mind that you can add any other instance variables you see fit and you don't need to use exactly the variable names we provided.
The calculator's operation depends on the context. The possible contexts (states) are:
Semantics of GenericCalcEng Operators
The calculator starts in the default state, which is described in the CLEAR operator below. In the description below, 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.
We will describe the process for a basic floating-point calculator (which you do not need to implement for this project). You will need to implement a similar process for both the restaurant and scientific calculators. The setup for these is exactly analogous to the basic calculator (just replace "Basic" with "Restaurant" or "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 that implements the functionality of the basic calculator. Let's call it engine. In your BasicCalc constructor, create 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 interact at the user-interface level for this basic calculator example. The picture is similar for the restaurant and scientific calculator. Remember, you don't need to implement this basic calculator. We are just using it as an example of how to set up a GUI for any calculator.
Classes you must define
For this homework you must define the following classes:
|Fries||constant||constant with a value of 1.20|
|Burger||constant||constant with a value of 2.50|
|Salad||constant||constant with a value of 3.00|
|Drink||constant||constant with a value of 0.75|
|Onions||constant||constant with a value of 0.80|
|Super||unary||increases current operand by 1.00|
|C||clear||clears the calculator's state|
For the restaurant calculator only, result values should be displayed using two decimal points. This format requirement can be satisfied by using the java.text.DecimalFormat class, as the following example illustrates.
double value = 78.123;
DecimalFormat decimalFormat = new DecimalFormat("0.00");
String valueStr = decimalFormat.format(value);
A value of "78.12" will be stored in
|E||constant||base of the natural logarithms|
|PI||constant||mathematical pi value|
|log||unary||natural logarithm (base e)|
|+/-||unary||invert the operand's sign|
|abs||unary||operand's absolute value|
|x^2||unary||x raised to a power of 2|
|sin||unary||sine of the operand (radians)|
|cos||unary||cosine of the operand (radians)|
|tan||unary||tangent of the operand (radians)|
|x^y||binary||x raised to the power y|
|C||clear||cleans the calculator's state|
|CE||clear_last_operand||clears last operand|
Most of the aforementioned operators can be found in the Java Math class.
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.
Packages you must define
In order to gain experience in designing a 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".)
In this section we present two snapshots of the calculators you are expected to implement. This will guide you in determining how to set up the GUIs. If you take a look at the PublicTests.java file and the pub*.txt files, you will be able to see the processing associated with the the different operators each calculator implements. As you can see, you can tests your implementation without using GUIs.
Here is an example that shows two snapshots of the calculators you are expected to implement, in their initial states.
Writing a JUnit Test
We have provided a JUnit module representing the public tests. However, you
need to create your own JUnit module to test your project. Define the
tests as you see fit. We expect at least three tests in the JUnit file.
Name your JUnit test file "MyJUnitTests.java". Feel
free to use the TestsSupport.processSeveralSelections method to process
different key combinations, as we did in the PublicTests.java.
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. Make sure you provide, in the challenge package, a Challenge.java file with a main method that allows us to run the challenge problem calculator. Also make sure that your Challenge class has a default constructor.
Note: For the challenge problem you must use the scientific calculator. Make an entry in the timelog indicating which Challenge problem choice you have implemented.
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.