CSE 142 Spring 2002Homework #6PL142 CompilerDue: Electronically, 11:00pm, Tuesday, May 28;
|
Assignment goals:
n
characters. For instance, if n==1
, the character
'a' becomes 'b', 'b' becomes 'c', ..., 'z' becomes 'a'. The word "animal"
would become "bojnbm" "zebra" would become "afcsb",
and the sentence "the zebra is an animal" would become
"uif afcsb jt bo bojnbm".
If n==6
, "animal" would become "gtosgr",
"zebra" would become "fkhxg", and the sentence would become
"znk fkhxg oy gt gtosgr".
"Rotating the alpahbet" can be done in Java with this code:
// assume the string you want to encode is named plainText,
// the result string is named codedText, and that
// you want to rotate the alphabet n characters to the left
String codedText = new String();
for ( int index=0; index < plainText.length(); index++ ) {
char nextChar = plainText.charAt( index );
if ( Character.isLowerCase( nextChar ) ) {
nextChar = (char)( ( nextChar - 'a' + n ) % 26 + (int)'a' );
} else if ( Character.isUpperCase( nextChar ) ) {
nextChar = (char)( ( nextChar - 'A' + n ) % 26 + (int)'A' );
}
codedText = codedText + nextChar;
}
To read input data (the words to encrypt),
you can (and probably should) use the Scanner
code from HW5.
If you want to read keyboard input, construct a Scanner
by
passing null
to its constructor. If you want to read
a file, construct one passing the filename to the constructor.
Program Structure
public static void main( String args[] )
.
Each class can contain a main()
method; not all the
classes in a single application must have one, but at least one must.
To write this program, you need to design a class that will do the
encryption, and write a main()
as the place your program
will start executing. Typically main() new
's an instance
of the class in which it is contained, and then sends it some messages.
Designing just what main()
does in this program is up to you.
The least work is to "hardcode" into your program just what it does -
it encrypts just the word "zebra" with an alphabet rotated by 10
characters, for instance. Alternatively, your main()
can read
input from the keyboard, so the user can decide what to do. (You can use the
Scanner
class from homework 5 for this.)
main()
if you have trouble with
these directions.
C:/a/b/c/
(so
your program is named something like C:/a/b/c/program1.java
.
C:/
. Type "cd" in the
Command Prompt window and it will tell you what directory you're in. Type "cd .."
to move up one directory - if you were in C:/g/h/i
, for instance, "cd .."
would put you in C:/g/h
. Keep doing that until you're in C:/
.
C:/a
. Repeat for b
and c
.
program1.java
).
main()
method of class program1
.
Step 6: If you see something like
it means your PATH variable does not contain the directory in which file
C:\>javac
'javac' is not recognized as an internal or external command,
operable program or batch file.
javac.exe
lives.
(For me, it's C:\jdk1.3.1_03\bin
, and it's likely to be the same for you.) Either
fix your
PATH environment variable or else revert to using BlueJ/Jeva.
Altenatively, if you've done something unexpected, like used a GWindow
in this program,
you might see something like:
Either
fix your
CLASSPATH environment variable or else revert to using BlueJ/Jeva.
C:\cse142\hw6\Program1>javac Encrypter.java
Encrypter.java:2: package uwcse.graphics does not exist
import uwcse.graphics.*;
^
Encrypter.java:7: cannot resolve symbol
symbol : class GWindow
location: class Encrypter
private GWindow gw;
Step 7: It shouldn't be possible to have problems with this step if step 6 worked. If you do, it's a CLASSPATH problem. See the problems for Step 6.
Take the sample solution for homework 5 and "simplify"
error handling - rather than checking return codes everywhere to detect
unexpected situations, use a try...catch
block in the
compile()
method to enclose the invocations of the lower level
methods, and use throw new Execption("my error message")
in those routines when you find that something has gone wrong.
In particular, in the sample solution the methods
handleINTEGERDeclaration()
and
handleOperandReference()
in Compiler.java
currently print error messages and return null
or
false
to indicate that an error has occured.
They really shouldn't be printing messages, because the caller might have
something else in mind - some way to recover from the error, for instance.
(That's not really the case here, but it is in many programs, and so as a
general rule low level routines shouldn't print anything.)
Change both those routines to throw
and Exception
when things go wrong. Also change the main compile()
routine
to catch
the Exception
and "deal with it".
In the case of the PL142 compiler, that means simply printing the error
message that has been associated with the Exception
and then
stopping compilation (by returning false back to the original caller).
There are two optional extensions you can try if you have the time and
the inclination.
The first is to change the Scanner
to throw an Exception
when the caller reads past EOF,
rather than printing an error message and dying (something that
is happening now in getNextToken()
.
This is a lot like what you've done already for the required part of Program 2;
the main difference is that you'll be wading into code that contains more
things that might be confusing if you try to understand every detail.
(But, that's the point of this - it should be possible, even relatively
simple, to make this change without having to understand every detail of
the Scanner
. Working in code where you don't understand every
detail is a valuable programming skill.)
The second optional extension is a bigger change.
The sample solution code is written using a number of
indicator methods - methods that answer questions
(like isValidINTEGERLiteral()
) and typically return
a boolean
.
These are used to decide whether or not something will cause an error.
The first optional component is to rethink the structure of all the
Compiler
code in light of Exception
s - could it
be simplified (made easier to understand/read) by using Exception
s
rather than these indicator routines?
Write a program (i.e., design a class class
and write a main()
method, as well as whatever else is needed) that draws lines from one mouse click to the next in
a GWindow
. After the first mouse click, which doesn't cause anthing to be drawn,
each successive mouse click should cause a uwcse.graphics.Line
to be drawn
from the location of the previous mouse click to the location of the latest mouse click.
Your class should contain a GWindow
. It also needs to create an
object of another class to receive window events. That other class can either extend
GWindowEventApapter
(i.e., be a derived class of it, the is a relationship)
or else implement GWindowEventHandler
(the acts like a relationship).
Either way, the only real message of importance in this application is
mousePressed
- the others can be ignored (meaning do nothing if
even receive them - just return).
Simple programs that just print out the keyboard
events they receive are below. In each case, DummyClass
is a class
that just creates a GWindow
and attaches an event handling object
to it. One version below uses inheritance and one uses interfaces.
Late-breaking update
A student has pointed out that there's a diffculty extending the examples
above to do what you need to do for Program 3. The difficulty is that the
event handler will be invoked when you click in the GWindow, but then what?
It doesn't have a reference back to the DummyClass object, so can't notify
it about the mouse click event. And, it would be inappropriate to put the
application logic into the event handler - the code that remembers old mouse
click (x,y) positions and draws lines - since it's an event handler and that's
what (and all) it should do.
There are two potential fixes to this. First, change the constructor for the handler object (in either of the cases above) so that it takes a DummyClass reference as a parameter. Add a public method to DummyClass that the event handler should call when an event occurs. Now put the program logic into DummyClass, in the method called by the event handler.
The second fix is to combine two classes in the examples above into one class. (This is shown in the code below, for both Inheritance and Interfaces.) This lets you make a single object that contains a GWindow and that is itself the handler for events in that GWindow.
This second approach is, well, a bit questionable - handling events is a distinct logical idea from doing whatever else an application is supposed to do (like draw lines), and you'll have combined those separate ideas into a single class. That's bad. On the other hand, it overcomes the problem noted above in a simple way - the object receiving the event notification is itself the object that eventually has to deal with the event, so there is no angonizing over how to convey the event from the handler to the application object.
Here are the examples using a single class: