/* Road Network Simulator * Author: Douglas Jones * Status: Compiles and works, cleaner but poorly tested * Version: 9/17/2020 */ import java.util.LinkedList; import java.util.Iterator; import java.util.Scanner; import java.io.File; import java.io.FileNotFoundException; // Utility classes /** * Error handling */ class Error{ // BUG: Perhaps we should count warnings public static void warn( String message ) { System.err.println( message + "\n" ); // BUG: Perhaps count warnings and error out if too many } public static void fatal( String message ) { warn( message ); System.exit( 1 ); } } /** * Wrapper or Adapter for scanners that integrates error handling * @see java.util.Scanner * @see Error */ class MyScanner { Scanner self; // the scanner this object wraps // constructor we wish to inherit from Scanner but can't because it's Java public MyScanner( File f ) throws FileNotFoundException { self = new Scanner( f ); } // methods we wish could inherit from Scanner but can't beause it's final public boolean hasNext() { return self.hasNext(); } public boolean hasNextFloat() { return self.hasNextFloat(); } public String next() { return self.next(); } public float nextFloat() { return self.nextFloat(); } // new methods we add to this class /** Get the next string, if one is available * @param def the default value if no string is available * @param msg the error message to print if no string is available */ public String getNext( String def, String msg ) { if (self.hasNext()) return self.next(); Error.warn( msg ); return def; } /** Get the next float, if one is available * @param def the default value if no string is available * @param msg the error message to print if no string is available */ public float getNextFloat( float def, String msg ) { if (self.hasNextFloat()) return self.nextFloat(); Error.warn( msg ); return def; } } // Simulation Model Classes /** * Roads connect intersections * @see Intersection */ class Road { // instance variables private final float travelTime; private final Intersection destination; private final Intersection source; // the collection of all instances private static final LinkedList allRoads = new LinkedList (); /** The only constructor * @param sc MyScanner from which description comes * Input format scanned from sc: source-name destination-name travel-time *
where source-name is the name of the source intersection and *
destination-name is the name of the destination intersection and *
travel-time is a floating point time in seconds */ public Road( MyScanner sc ) { // keyword Road was already scanned final String src; // where does it come from final String dst; // where does it go src = sc.getNext( "???", "road source missing" ); dst = sc.getNext( "???", "road " + src + " to missing destination" ); travelTime = sc.getNextFloat( Float.NaN, "road " + src + " to missing destination" ); destination = Intersection.lookup( dst ); if (destination == null) { Error.warn( "road " + src + " " + dst + " undefined: " + dst ); } source = Intersection.lookup( src ); if (source == null) { Error.warn( "road " + src + " " + dst + " undefined: " + src ); } // BUG: Can we prevent creation of malformed roads allRoads.add( this ); // this is the only place items are added! } /** Primarily for debugging * @return textual name and travel time of the road */ public String toString() { return source.name + " " + destination.name + " " + travelTime; } /** Allow outsiders to iterate over all roads * @return textual name and travel time of the road */ public static Iterator iterator() { return allRoads.iterator(); } } /** * Intersections are connected by roads * @see Road */ class Intersection { // instance variables final String name; private final LinkedList outgoing = new LinkedList (); private final LinkedList incoming = new LinkedList (); // the collection of all instances private static final LinkedList allIntersections = new LinkedList (); /** The only constructor for Intersection * @arg sc the scanner from which the Intersection description is scanned * Input format scanned from sc: name *
where name is a string */ public Intersection( MyScanner sc ) { // pick off name of intersection; name = sc.getNext( "???", "Intersection name missing" ); // BUG: Can we prevent creation of malformed interesections. // BUG: Intersection type? Other attributes? Handle this later. allIntersections.add( this ); } /** Primarily for debugging * @return textual name and travel time of the road */ public String toString() { return name; // BUG: Other attributes? } /** Allow outsiders to iterate over all roads * @return textual name and travel time of the road */ public static Iterator iterator() { return allIntersections.iterator(); } /** Allow finding intersections by name * @return textual name and travel time of the road */ public static Intersection lookup( String n ) { for (Intersection i: allIntersections) { if (i.name.equals( n )) return i; } return null; } } /** * Main class builds model and will someday simulate it * @see Road * @see Intersection */ public class RoadNetwork { private static void readNetwork( MyScanner sc ) { while (sc.hasNext()) { // until the input file is finished String command = sc.next(); if ("intersection".equals( command )) { new Intersection( sc ); } else if ("road".equals( command )) { new Road( sc ); } else { Error.warn( "unknown command: " + command ); } } } private static void writeNetwork() { for (Iterator i = Intersection.iterator(); i.hasNext(); ){ System.out.println( "Intersection " + i.next() ); } for (Iterator i = Road.iterator(); i.hasNext(); ){ System.out.println( "Road " + i.next() ); } } public static void main( String[] args ) { if (args.length < 1) { Error.fatal( "Missing file name argument" ); } else try { readNetwork( new MyScanner( new File( args[0] ) ) ); writeNetwork(); // BUG -- this code is for debugging only // BUG -- simulation code goes here if readNetwork success } catch ( FileNotFoundException e) { Error.fatal( "Can't open file: " + args[0] ); } } }