// M2.java // Author: Douglas W. Jones import java.io.FileInputStream; import java.io.FileNotFoundException; import java.util.List; import java.util.LinkedList; import java.util.Scanner; /** Error reporting package * Provide a standard prefix and behavior for error reporting * @author Douglas W. Jones * @version 2019-02-13 * Lifted directly from RoadNetwork.java */ class Errors { /** Prefix string for error messages */ private static String prefix = "??: "; /** Set prefix on error reports, should be done before any error reports * @arg p the prefix on any error messages */ public static void setPrefix( String p ) { prefix = p; } /** Report nonfatal errors, output a message and return * @arg m the message to output */ public static void warn( String m ) { System.err.println( prefix + ": " + m ); } /** Report fatal errors, output a message and die * @arg m the message to output */ public static void fatal( String m ) { warn( m ); System.exit( 1 ); } } /** Support package for application dependent extensions to class Scanner * @author Douglas Jones * @version 2019-02-15 * Lifted directly from RoadNetwork.java * @see Errors * If class Scanner wasn't final, this could be a subclass */ class ScanSupport { /** Scan and return one name, if available * @arg sc the scanner to read from * @arg msg the message to output if there is no name */ public static String scanName( Scanner sc, String msg ) { // Bug -- this is the place to enforce rules on what is a legal name if (sc.hasNext()) return sc.next(); Errors.warn( msg ); return null; } } /** Gates are joined by Wires * @author Douglas Jones * @version 2019-02-18 * Lightly edited from RoadNetwork.java class Intersection version 2019-02-15 * @see Wire * There will eventually be several subclasses of Gate */ class Gate { /** The name of this gate */ public String name; // the gate's name or null if broken // where this gate connects LinkedList outgoing = new LinkedList (); // Bug do we need to know what wires lead here? // Bug how about different kinds of gates /** construct a new Gate * @param sc the scanner used to get the attributes of this gate * When called, the keyword "gate" has already been scanned, * so we are ready to scan the additional attributes, if any. */ public Gate( Scanner sc ) { name = ScanSupport.scanName( sc, "Gate has missing name" ); if (MP2.findGate( name ) != null) { Errors.warn( "Name reused for gate" + name ); name = "reused-" + name; // Bug -- would we be better off throwing an exception here? } // Bug -- what what about other attributes? } public String toString() { if (name != null) { return "Gate " + name; } else { return "Gate ???"; } } } /** Wires connect Gates * @author Douglas Jones * @version 2019-02-19 * Lightly edited from RoadNetwork.java class Road version 2019-02-15, * except for the constructor, which is heavily edited * @see Gate */ class Wire { private float delay; // how long it takes a signal to get to the other end private Gate destination; // where does this wire go private Gate source; // where does this wire come from // Bug -- Need other attributes of a wire? /** construct a new Wire * @param sc the scanner used to get the attributes of this wire * @param srcName the name of the source gate, needed only for error msgs * @param src the source gate * When called, the keyword "wire" and the name of the source gate * have already been scanned, so we are ready to scan the delay and * destination. */ public Wire( Scanner sc, String srcName, Gate src ) { String dstName; // destination gate's name delay = 0.0F; // default if no delay provided source = src; // use what has already been scanned // source may be null, so may srcName, and need a name for error msgs String errname = "???"; if (srcName != null) errname = srcName; if (sc.hasNextFloat()) { // deal with delay delay = sc.nextFloat(); } else { // default value remains unchanged Errors.warn( "wire " + errname + " delay expected" ); } dstName = ScanSupport.scanName( sc, "Wire " + errname + " " + delay + " has no destination" ); // names are defined here but may be null if missing destination = MP2.findGate( dstName ); // it is legal to call toString now because all fields are initialized // deal with errors if ((source == null) && (srcName != null)) { Errors.warn( "" + this + " undefined source" ); } if (delay < 0.0F) { Errors.warn( "" + this + " illegal delay" ); } if ((destination == null) && (dstName != null)) { Errors.warn( "" + this + " undefined destination" ); } } public String toString() { // prepare for missing fields of a badly declared wire String src = "???"; String dst = "???"; // find real values if ((source != null) && (source.name != null)) { src = source.name; } if ((destination != null) && (destination.name != null)) { dst = destination.name; } return "Wire " + src + ' ' + delay + ' ' + dst; } } /** Main program * @author Douglas Jones * @version 2019-02-18 * Lightly edited from RoadNetwork.java class RoadNetwork version 2019-02-13, * except for the buildLogic code, which is heavily edited from buildNetwork. * @see Gate * @see Wire */ public class MP2 { // lists of all the parts of this model private static final List gates = new LinkedList (); private static final List wires = new LinkedList (); /** look up a gate by name * @param n the name of the gate, possibly null (matches nothing) * @returns the Gate with that name, or null if no match */ public static Gate findGate( String n ) { // stupid code, a linear search, but does it matter? for (Gate g: gates) { if (g.name.equals( n )) return g; } return null; } // build the logic circuit by scanning a source file private static void buildLogic( Scanner sc ) { // Bug -- what if there is no next while (sc.hasNext()) { // pick off the next part of the logic circuit description String command = sc.next(); if ("gate".equals( command )) { gates.add( new Gate( sc ) ); } else if ("wire".equals( command )) { // because of multiple destinations, must get the source here String srcName; srcName = ScanSupport.scanName( sc, "Wire has no source" ); Gate source = findGate( srcName ); if ((source == null) && (srcName != null)) { Errors.warn( "Wire " + srcName + " undefined source" ); } // now build at least one wire and possibly more wires.add( new Wire( sc, srcName, source ) ); while (sc.hasNextFloat()) { wires.add( new Wire( sc, srcName, source ) ); } // Bug - how can we move the above block of code to class Wire? } else { Errors.warn( "invalid command " + command ); } // Bug -- it would be nice to allow some kind of comments } } // print out the entire logic circuit private static void printLogic() { for (Gate g: gates) { System.out.println( g ); } for (Wire w: wires) { System.out.println( w ); } } public static void main( String[] args ) { Errors.setPrefix( "MP2" ); if (args.length < 1) { Errors.fatal( "missing argument" ); } if (args.length > 1) { Errors.warn( "extra arguments" ); } try { // args[0] is the text file holding the logic circuit description buildLogic( new Scanner( new FileInputStream( args[0] ) ) ); printLogic(); // something testable! } catch( FileNotFoundException e ) { Errors.fatal( "can't open file" ); } } }