|
||||||||||
|
The Calculator ApplicationThe application is a calculator. The calculator provides a number N of accumulators, named A, B, C ... The command line argument gives N. The input (standard input) consists of a number of commands, e.g.
The first operand identifes an accumulator, the second either identifies an accumulator or is a single-digit number. The operator is either -
The new value of the changed accumulator is output (to standard output) with a count of how many accumulator operations have happened.
Additionally there is a command of the form e.g.
The input character "q" at any time will terminate the program. All input characters other than "A"-"Z", "0"-"9", "+", "-", "=" , "?" "q" are ignored. STEP 1Copy and paste the following code into your favourite editor:package myCalculator1; /* *************************** PREAMBLE ********************* * The purpose of this example is try to illustrate all the constructs that you have been * told about in the lecture, and for this reason some things may be done in a somewhat * unnatural way - do not take this as always "best practice". * * * **** IN THIS VERSION ARE SYNTACTIC ERRORS ON LINES MARKED ??? * **** ??? ON A BLANK LINE MEANS ONE OR MORE MISSING LINES * ********************** END ******************************* * ********************** PREAMLE ****************************************8 */ /* The following IntegerVariable is an interface used by one version of accumulator. * Would normally be in a separate file and package. */ /** Provides Exception for IntegerVariable Interface*/ class uninitialized extends Exception {static final long serialVersionUID = 2; } /** This is for any class whose instances maintain an integer value * which can be manipulated through its set and get methods*/ interface IntegerVariable { int get() throws uninitialized; int set(int value);} /* The following is a 2-level class heirarchy of accumulator classes. * A basic one Accumulator and an extended one AccMKII. * Would normally be Public in separate files and package(s) */ /** Maintains a running integer value, initially 0, which can be incremented and reSet * and accessed using methods incr, reSet and get respectively * @author Richard Hopkins * */ class Accumulator { int total; static int uses=0; //to keep count of number of accumulator operations public Accumulator(){total=0;} //constructor public int incr(int i){++uses; total= total+i ; return total;}//increment public int reSet(int r){uses++; return total=r;} public int get(){return total;} } /* The AccMKII extension - * - provides an additional constructor with an initial value, * and re-implemets the old one * - supports the "standard" IntegerVariable Interface * - inherits the reSet and get methods * - includes an additional decrement method (decr) */ /** Maintains a running integer value which can be incremented, decremented, set * and accessed using methods incr, decr, set and get respectively. * @author Richard Hopkins * */ class AccMKII extends Accumulator implements IntegerVariable { public AccMKII(int initialTotal) //new constructor {total=initialTotal;} public AccMKII() //re-implemented constructor {this(0)} //??? public int decr(int i) // new operation {uses++;total=total-i;return total;} public int incr(int i) //re-implemented operation {return this.decr(-i) ;} //??? } class userQuit extends Exception //exception for MyCalculator {static final long serialVersionUID = 2; }; /** This application provides a number of accumulator objects (class AccMKII), * and implements an input language for operating on them. * This version has syntactic errors. * @author Richard Hopkins * */ public class MyCalculator1 { static final char QUIT='q'; // the character for quitting public static int main(String[] args) { //??? // ****** Initialisation char [][] syn ={{'A','Z'},{'0','9'},{'+','+'},{'-','-'},{'=','='},{'?','?'}}; // syntax array for use in getInput(syn) - each pair of characters // is an inclusive range of recoginsed input characters int size= Integer.parseInt(args[0]); //size of accumulator array if size>26 size=26; //??? Accumulator [] accs = new Accumulator [size]; for ( int i=0 ; i<size ; i++ ) accs[i]= new AccMKII(i); System.out.println("My Calculator1 started, with "+ args[0]+" accumulators"); // ****** Main Loop // *** Reading Input Command char c1, c2, c3; //The three input characters for a command int p1, p2=0; //The two parameters of a command boolean isLiteral=false; //whether parameter 2 is a literal try{ do { c1= getInput(syn); c2= getInput(syn); c3= getInput(syn); p1= c1-'A'; //the index of the accumulator identifed by first param if (('A' <= c3) && (c3 <= 'Z')) {p2= c3-'A'; isLiteral=false;} else if (('0' <= c3) && (c3 <= '9')) {p2= c3-'0' ; isLiteral=true;} // *** Doing the requested action int v2, v3=0; //values of second operand and result boolean show=false; // whether there is a result to show AccMKII target = accs[p1]; //??? v2=(isLiteral ? p2 : accs[p2].get()); switch (c2){ case '+' : v3=target.incr(v2); show=true; break; case '-' : v3=target.decr(v2); show=true; break; case '=' : v3=target.set(v2); show=true; break; case '?' : String s = ""; do { s=s+"["+c1+"]="+accs[p1].get()+" "; p1++; c1++; }while (p1 <= p2); System.out.println(s); show = false; break; } if (show) System.out.println(Accumulator.uses+")----------> ["+c1+"]= "+v3); }while(true); } // ****** Exception Handling catch (userQuit e1){System.out.println("You have quit - Goodbye");} catch (java.io.IOException e1) {System.out.println("IOException");} } /* Below are helper methods for reading user input. * Both take a parameter syn. This is an array of pairs of characters. * Each pair defines an exclusive range of characters. * The getInput method reads the input unitl finding a character which is within one * of those ranges and returns that. Except that it iterprets a "q" input as * quitting the program. * The isNoticed is method tests a character to determine whether it * is within one of the ranges */ static boolean isNoticed (char c, char [] syn){ //??? int i=0 ; for ( ; i<syn.length ; i+=1) {if ((c>=syn[i][0])&&(c<=syn[i][1])) return true;} return false;} static char getInput(char [] syn){ //??? char c; while (!isNoticed(c= (char) System.in.read(),syn)) if (c==MyCalculator1.QUIT) throw new userQuit(); return c; } } The above is our first attempt at programming the Calculator application. Unfortunately it has some syntax errors.
Step 1.1Read the code and see if you can understand all the syntax that is used. Without compiling it, try to see what changes would need to be made to correct the syntax errors. Step 1.2Compile the code without changing anything, to see what syntax errors are reported by the compiler. Step 1.3Work through the code correcting all the errors, and then do some runs. The solution to Step 1 - the correct code - is Calculator1.java. If you do nothing else, make sure you understand this code as it contains examples of just about everything that has been covered in the lecture. Each of the next steps involves progressive modifications of the code for the re-design of the application. Except that Step 6 could be done at any time. STEP 2In MyCalculator1 (from Step 1) there is a helper method getInput. Now you should modify that to give MyCalculator2 in which there is a class SelectiveInput that plays the same role as the getInput method did. The SelectiveInput class provides a getInput method - which has no parameters and returns a char. The calculator should create an instance of SelectiveInput and use its getInput method. In the SelectiveInput class, be discerning about the accessibility of the entities that it declares. My solution is here: Calculator2.java. Compare your solution with mine. STEP 3You should now modify MyCalculator2 to give MyCalculator3 in which the error checking has been improved as follows: (a) For a command line argument which is not a positive integer, it outputs message: MyCalculator3: Argument error (b) It outputs error messages for incorrect input - Try Again - "<input >" input error: Input <N> out of range For <input> being the 3 character user input and <N> being 1, 2 or 3. For the command line error, (a), you should catch the exception thrown by the method Integer.parseInt(args[0]) used in the code. How can you find out what exception to catch? My solution is here: Calculator3.java The changed parts of the program have comments Compare your solution with mine. STEP 4You should now modify MyCalculator3 to give MyCalculator4 in which user input has been re-designed to have line-oriented, rather than character-oriented, input. For an accumulator command the first three characters of the line are taken as the command and the rest ignored. For a "quit" the first character of the line is 'q' and the rest are ignored. To do this you will need a "BufferedReader", obtained by the code InputStreamReader isr = new InputStreamReader(System.in); BufferedReader br = new BufferedReader (isr); The BufferedReader object provides a method for reading a complete line of input. The required classes are in package java.io. My solution is here: Calculator4.java. The changed parts of the program have comments STEP 5You should now modify MyCalculator3 to give MyCalculator4 in which the input commands are read from, and their results written to, named files. There is an optional second command line argument which is the name of a working directory (relative to the user's home directory) within which the input and output files reside. If this argument is omitted the working directory is the home directory. The names of the files relative to the working directory are input on the standard input. You should make appropriate checks, e.g. a named file is within (a sub-directory) of the working directory and it is a file rather than a directory; and give the user the opportunity to re-input invalid file names. To do this you will have to use the java.io package. This provides file objects which represent (potential) actual files and provide operations such as creating the corresponding actual file. It also provides other classes to give convenient file interfaces. The ones you should use here are FileReader, BufferedReader, FileWriter and PrintWriter. Getting file I/O to work can be a bit tricky and time-consuming. You could just study the solution below together with the java.io package documentation. My solution is Calculator5.java. There are two additional methods: one for obtaining the input file name and the other for obtaining the output file name. The changed parts of the main method have comments. STEP 6 (could be done at any point)The main method is class MyCalculatorN is arguably not very good design n the grounds of being too big and not O-O. It would be better to have a class Calculator which provides the array of Accumulators, with main creating an instance of that. Design such a class Calculator. If you want more practice in Java programming - implement it and then re-implement main to use it. |
|||||||||
|