CS U370 Assignment #2. Spring 2008, Prof. Hafner Assigned: Friday, January 25 REVISED TUESDAY, JAN. 29: hashCode() and routeInUS() REVISED FRIDAY, Feb. 1: New error condition defined Due: Wednesday, Feb 6 This purpose of this assignment is: * To continue the study of abstraction-based software design and algebraic specifications * To implement a class that is a client of another class * To organize collections of programs using packages * To begin practicing the agile approach to software development (along with Assignment 3) * To introduce the canoncial methods equals() and hashCode() * To introduce the use of Enum types * To introduce the handling of java.util.Calendar classes to represent date and time information You will complete an implementation in Java of the Route abstract data type (ADT) that is specified below. Collaboration between students is forbidden on this assignment. You are responsible for keeping your code hidden from all other students. Turn in your work on this assignment before 10 pm on the due date by submitting nothing but your Route.java file through the Web-based submission system. Your source file should begin with a block comment that lists 1. Your name, as you want the instructor to write it. 2. Your email address. 3. Any remarks that you wish to make to the instructor. Part of your grade will depend on the quality and correctness of your code; part will depend on the readability of your code (comments and indentation), and part will depend on your following the procedure above for submitting your work. Late assignments may be discounted, and very late assignments may be discarded. -------------------------------------------------- Your assignment is to write the code for a single file, Route.java, that implements the specification below. Note that Route.java must be part of the geography package. Note that this assignment will be done "in parallel" with Assignment 3, which is to write a RouteTester.java program along the lines of the CityTester.java file given to you for assignment 1. This assignment requires the City class, which was previously in the default package, to be moved into the geography package. A package statement must be added to your City.java source code, a geography sub-directory of your base directory must be created, and any code not in the geography package must contain an import statement that references the geography package. --------------------------------------------------------- Specification of the Route ADT. Route is an immutable abstract data type. Its operations shall have no side effects. The Route specification implicitly references the City specification from Assignment 1, and implementations of Route are allowed to rely on a correct implementation of City. The Route ADT shall be implemented in Java, and will be tested using Sun's Java 2 Runtime Environment, Standard Edition, version 5/6. The code for this implementation of the Route ADT shall be in the geography package, and shall define a public class named Route that defines the following public methods, which are specified below. Signature: //Nested enum class public static enum Direction {N, E, S, W, NE, SE, NW, SW}; //Creator methods public static Route interstate(City, City, int, Route.Direction); public static Route secondary(City, City, int, Route.Direction); //Canonical methods public String toString (); public boolean equals (Object); public int hashCode (); //Accessor methods public City startsAt(); public City endsAt(); public int travelMiles(); public int estTravelMinutes(); public Route.Direction travelDirection(); public java.util.GregorianCalendar estArrivalTime(java.util.GregorianCalendar); //Predicates public boolean routeInUS(); public boolean interstateRoute(); public boolean startsAtCapital(); public boolean goesToCapital(); Algebraic specification: Route.interstate(s, d, m, p).startsAt() = s Route.secondary(s, d, m, p).startsAt() = s Route.interstate(s, d, m, p).endsAt() = d Route.secondary(s, d, m, p).endsAt() = d Route.interstate(s, d, m, p).travelMiles() = m if routeInUS() = true Route.secondary(s, d, m, p).travelMiles() = m if routeInUS() = true Route.interstate(s, d, m, p).estTravelMinutes() = m //assume 60 mph Route.secondary(s, d, m, p).estTravelMinutes() = m * 2 //assume 30 mph Route.interstate(s, d, m, p).travelDirection() = p Route.secondary(s, d, m, p).travelDirection() = p Route.interstate(s, d, m, p).estArrivalTime(startTime) = startTime + estTravelMinutes() Route.secondary(s, d, m, p).estArrivalTime(startTime) = startTime + estTravelMinutes() Route.interstate(s, d, m, p).routeInUS() = 24.55 <= s.getLatitude() <= 48.93 AND -124.95 <= s.getLongitude() <= -67.78 AND 24.55 <= d.getLatitude() <= 48.93 AND -124.95 <= d.getLongitude() <= -67.78 Route.secondary(s, d, m, p).routeInUS() = 24.55 <= s.getLatitude() <= 48.93 AND -124.95 <= s.getLongitude() <= -67.78 AND 24.55 <= d.getLatitude() <= 48.93 AND -124.95 <= d.getLongitude() <= -67.78 Route.interstate(s, d, m, p).interstateRoute() = true Route.secondary(s, d, m, p).interstateRoute() = false Route.interstate(s, d, m, p).startsAtCapital() = s.isCapital() Route.secondary(s, d, m, p).startsAtCapital() = s.isCapital() Route.interstate(s, d, m, p).goesToCapital() = d.isCapital() Route.secondary(s, d, m, p).goesToCapital() = d.isCapital() Route.interstate(s, d, m, p).toString() = `"Route from s to d going p for m miles on the interstate highway" Route.secondary(s, d, m, p).toString() = `"Route from s to d going p for m miles on state or local roads" Route.interstate(s, d, m, p).equals(other) = other.interstateRoute() = true AND s.getName() = other.startsAt().getName() AND d.getName() = other.endsAt().getName() AND m = other.travelMiles() if other is a Route instance false otherwise Route.secondary(s, d, m, p).equals(other) = other.interstateRoute() = false AND s.getName() = other.startsAt().getName() AND d.getName() = other.endsAt().getName() AND m = other.travelMiles() if other is a Route instance false otherwise Route.interstate(s, d, m, p).hashCode() = (s.getName().hashCode() | d.getName().hashCode()) / 2 + m; Route.secondary(s, d, m, p) = (s.getName().hashCode() | d.getName().hashCode()) / 2 + m + 1013; ***ADDED NOTE: If the two City arguments to a factory method are the same City, or if the distance argument is less than or equal to 0, the class should generate an IllegalArgumentException. Note: If a client program invokes a method whose behavior is undefined, the class should generate an appropriate RuntimeException. Note: To create a correct equals() method, it is necessary for the implementor to understand the subtleties of equality testing for different data types. Note: The | operator in java is bitwise OR.