6. Initializers

Part of CS:2820 Object Oriented Software Development Notes, Fall 2015
by Douglas W. Jones
THE UNIVERSITY OF IOWA Department of Computer Science

 

Where were we?

Putting everything together from the previous lecture, we have the following class definitions for building our road network:

import java.util.LinkedList;

class Road {
        /** Roads are one-way streets linking intersections
         *  @see Intersection
         */
        float travelTime;         //measured in seconds
        Intersection destination; //where the road goes
        Intersection source;      //where the comes from
        // name of road is source-destination
}

class Intersection {
        /** Intersections join roads
         *  @see Road
         */
        String name;
        LinkedList <Road> outgoing = new LinkedList <Road> ();
        LinkedList <Road> incoming = new LinkedList <Road> ();
        // Bug: multiple types of intersections (uncontrolled, stoplight)
}

It's time to start working on initializing a road network. To do this, we need to read the description of a road network from a text file, we need start work on the main program, and we need to work on the architecture of the interpretation of the text file.

Access to the text file

Java provides a very useful class for reading text files, the scanner. To quote the official definition: "A Scanner breaks its input into tokens using a delimiter pattern, which by default matches whitespace. The resulting tokens may then be converted into values of different types using the various next methods." The setup for calling a scanner is as follows:

import java.io.File;
import java.util.Scanner;

public class RoadNetwork {
        public static void main(String[] args) {
                // Bug:  Ought to check to see if there is a file name
                Scanner sc = new Scanner(new File(args[1]));
                // Bug:  What if the file doesn't exist?
        }
}

The above code creates a scanner called sc that reads from a file whose name is give by the first command line argument passed when launching the main program. If you have a file called IowaCity.txt containing the road network description for Iowa City, you would launch this program with the following command:

[HawkID@serv15 ~/project]$ java RoadNetwork IowaCity.txt

Of course, the skeletal definition given above has some bugs. The code ought to check that there is a command line argument before using it, and it ought to output a sensible error message if the file does not exist or cannot be read. We need to fix the latter to make this file compile:

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;

public class RoadNetwork {
        public static void main(String[] args) {
                // Bug:  Ought to check to see if there is a file name
                try {
                        Scanner sc = new Scanner(new File(args[0]));
                } catch (FileNotFoundException e) {
                        // Bug:  What if the file doesn't exist?
                }
        }
}

There are some puzzles here: The first command line argument after the program name is args[0]. This will seem strange to programmers accustomed to C or C++, where argv[0] is the program name and argv[1] is the first parameter after the program name. Another problem for C or C++ programmers is that there is no count of command line arguments in Java. To find out how many command line arguments there are in an Java program, use args.length where a C or C++ programmer would use argc.

What does a scanner do? We can ask the scanner whether there is more input with sc.hasNext(). We can ask if the next input is a number with sc.hasNextInteger() or sc.hasNextFloat(). We can ask for the next string from the input with sc.next() or the next integer from the input with sc.nextInt().

Processing the text file

The outermost loop of the scanner is pretty obvious: Read lines from the text file and process them. We could do all the processing in the outer loop, but that means that the outer loop needs to know about every detail of describing roads and intersections. We are better off if we can move the details of road description into the road class, and the details of intersection description into the intersection class. We do that here:

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;

public class RoadNetwork {

        /* the sets of roads and intersections */
        static LinkedList <Road> roads
                = new LinkedList <Road> ();
        static LinkedList <Intersection> inters
                = new LinkedList <Intersection> ();

        public static void main(String[] args) {
                // Bug:  Ought to check to see if there is a file name
                try {
                        Scanner sc = new Scanner(new File(args[0]));
                } catch (FileNotFoundException e) {
                        // Bug:  What if the file doesn't exist?
                }
                while (sc.hasNext()) {
                        // until the input file is finished
                        string command = sc.next()
                        if ((command == "intersection")
                        ||  (command == "i")) {
                                // Bug: Something about an intersection
                                inters.add( new Intersection( sc, inters ) );
                        } else if ((command == "road")
                        ||         (command == "r" )) {
                                // Bug: Something about a road
                                roads.add( new Road( sc, inters ) );
                        } else {
                                // Bug: Unknown command
                        }
                }
        }
}

If we try to build this code, we find that it doesn't work: The declaration of the scanner sc is local to the try block and invisible where it is used.

We could solve this by moving the declaration -- declaring sc without initialization (it gets a default void initialization) and then initializing it in the try block. This leaves the possibility that sc will be void in the case where opening the scanner throws an exception. If our handler throws an exception, this is not an issue, but an obvious alternative is to move the initialization into the try block. This, in turn is problematic because things get deeply indented. The following code may, in fact, be the most appropriate:

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;

public class RoadNetwork {

        /* the sets of roads and intersections */
        static LinkedList <Road> roads
                = new LinkedList <Road> ();
        static LinkedList <Intersection> inters
                = new LinkedList <Intersection> ();

        static void initializeNetwork( Scanneer sc ) {
                while (sc.hasNext()) {
                        // until the input file is finished
                        string command = sc.next()
                        if ((command == "intersection")
                        ||  (command == "i")) {
                                // Bug: Something about an intersection
                                inters.add( new Intersection( sc, inters ) );
                        } else if ((command == "road")
                        ||         (command == "r" )) {
                                // Bug: Something about a road
                                roads.add( new Road( sc, inters ) );
                        } else {
                                // Bug: Unknown command
                        }
                }
        }

        public static void main(String[] args) {
                // Bug:  Ought to check to see if there is a file name
                try {
                        initializenetwork( new Scanner(new File(args[0])) );
                } catch (FileNotFoundException e) {
                        // Bug:  What if the file doesn't exist?
                }
        }
}

The above code assumes something like the following for the structure of one of the basic structure of a road:

class Road {
        /** Roads are one-way streets linking intersections
         *  @see Intersection
         */
        float travelTime;         //measured in seconds
        Intersection destination; //where the road goes
        Intersection source;      //where the comes from
        // name of road is source-destination

        // initializer
        public Road( Scanner sc, LinkedList <Intersection> inters ) {
                // code here must scan & process the road definition

                string sourceName = sc.next();
                string dstName = sc.next();
                // Bug:  Must look up source and dest names to find objects
                
                // Bug:  What if the next isn't a float
                travelTime = sc.nextFloat();
                string skip = sc.nextLine();
        }       
}       

There are, of course, some bugs here! We need to do quite a bit of work, and, of course, we need to write code for intersections as well as roads.