Banner
Title: Java Tutorial
Tutor: Richard Hopkins
Author: Richard Hopkins

The Calculator Application

The 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.

"C+4" - increment the C accumulator by 4;

"C+B" - increment the C accumaltor by the current value of the B accumulator

The first operand identifes an accumulator, the second either identifies an accumulator or is a single-digit number.

The operator is either -

"+" for increment; "-" for decrement; "=" for re-set.

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.

"D?F" - to output (to standard output) the values of all accumulators in range D to F

The input character "q" at any time will terminate the program.

All input characters other than "A"-"Z", "0"-"9", "+", "-", "=" , "?" "q" are ignored.

STEP 1

Copy 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.

A line of java code with a comment "//???" is a line in which there is an error.

A line just consisting of "//???" indicates that there is some missing code.

Step 1.1

Read 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.2

Compile the code without changing anything, to see what syntax errors are reported by the compiler.

Step 1.3

Work 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 2

In 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 3

You 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 4

You 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 5

You 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.

Top