// File VehicleTest.java

public class VehicleTest
{   public static void main(String[] args) throws PrecondViolation
    {   Vehicle v1 = new Automobile();
        Vehicle v2 = new Truck();
/* 1 */ System.out.println(v1);
/* 2 */ System.out.println(v2);
        Driven v3 = new Truck(new Position(10.0,10.0));
        v3.setDriver(new Driver("Joe"));
/* 3 */ System.out.println(v3);
        ((CargoCarrier) v3).setCargo("rice");
/* 4 */ System.out.println(v3);
        Trailer v4 = new Trailer();
        v4.setCargo("beans");
        TowVehicle v5 = new TowVehicle((PoweredVehicle)v3,v4);
/* 5 */ System.out.println(v5);
        Driver d2 = new Driver("Mary");
        v5.setDriver(d2);
/* 6 */ System.out.println(v3);
/* 7 */ System.out.println(((Trailer)(v5.getTow())).isLoaded());
/* 8 */ System.out.println(v1==v2);
/* 9 */ System.out.println(v1.getPosition().equals(v2.getPosition()));
/*10 */ System.out.println(v5.getFront().getClass().getName());
   } 
}
abstract class Vehicle 
{   public Vehicle() 
    {   _loc = new Position(0.0, 0.0);  _id = newId();   }
    public Vehicle(Position p) 
    {   _loc = p;  _id = newId();
        if (_loc == null) { _loc = new Position(0.0, 0.0); }
    }

    abstract public void moveTo(Position p) throws PrecondViolation;
    public void moveTo(Vehicle v) throws PrecondViolation
    {   moveTo(v.getPosition());   }

    public void setMovable()   { _movable = true; }
    public void clearMovable() { _movable = false; }

    public int getId() { return _id; }
    public Position getPosition() { return _loc; }
    public boolean isMovable() { return _movable; }

    protected void setPosition(Position p) throws PrecondViolation
    {   if ( p == null || !isMovable() )
        {   throw new PrecondViolation();   }
         _loc = p;
    }      
    public String toString() 
    {   return "(" + _id + "," + _loc + "," + _movable + ")";   }

    private static int newId() { return _nextId++; }
    private static int _nextId = 1;

    private int       _id = 0;
    private Position  _loc;
    private boolean   _movable = false;
}


class PoweredVehicle extends Vehicle implements Driven
{   public PoweredVehicle() { super(); }
    public PoweredVehicle(Position p) { super(p); }

    public void moveTo(Position p) throws PrecondViolation
    {   if (_driver == null) { throw new PrecondViolation(); }
        setPosition(p);
    }
    public void setDriver(Driver d) { _driver = d; }

    public Driver  getDriver() { return _driver; }
    public boolean hasDriver() { return (_driver != null); }
    public String toString() 
    {   return super.toString() + "Driver:" + _driver + ".";   }

    private Driver _driver = null;
}

class MotorVehicle extends PoweredVehicle
{   public MotorVehicle() { super(); }
    public MotorVehicle(Position p) { super(p); }

    public boolean hasDriver() { return (getDriver() != null); }
}

class PedalVehicle extends PoweredVehicle
{   public PedalVehicle() { super(); }
    public PedalVehicle(Position p) { super(p); }
}

class UnpoweredVehicle extends Vehicle
{   public UnpoweredVehicle() { super(); }
    public UnpoweredVehicle(Position p) { super(p); }

    public void moveTo(Position p) throws PrecondViolation 
    {   setPosition(p);   }
}   

class Trailer extends UnpoweredVehicle implements CargoCarrier
{   public Trailer() { super(); }
    public Trailer(Position p) { super(p); }

    public void setCargo(Object c) { _bed = c; }

    public Object getCargo() { return _bed; }
    public boolean isLoaded() { return (_bed != null); }
    public String toString() 
    {   return super.toString() + "Cargo:" + (_bed!=null) + ".";   }

    private Object _bed = null;
}

class Automobile extends MotorVehicle
{   public Automobile() { super(); }
    public Automobile(Position p) { super(p); }
}

class Truck extends MotorVehicle implements CargoCarrier
{   public Truck() { super(); }
    public Truck(Position p) { super(p); }

    public void setCargo(Object c) { _bed = c; }

    public Object getCargo() { return _bed; }
    public boolean isLoaded() { return (_bed != null); }
    public String toString() 
    {   return super.toString() + "Cargo:" + (_bed!=null) + ".";   }

    private Object _bed = null;
}

class TowVehicle extends Vehicle implements Driven
{   public TowVehicle(PoweredVehicle front, Vehicle back) 
           throws PrecondViolation
    {   if (front == null) { throw new PrecondViolation(); }
         _front = front;  _back = back; 
    }

    public void moveTo(Position p) throws PrecondViolation
    {   if (p == null || _front == null || !hasDriver())
        {   throw new PrecondViolation();   }
        if ( _back != null && ! _back.isMovable())
        {   throw new PrecondViolation();   }
        _front.moveTo(p);  
        if (_back != null) { _back.moveTo(p); }
    }   
    public void setDriver(Driver d) throws PrecondViolation
    {   if (_front == null) { throw new PrecondViolation(); }
        _front.setDriver(d); 
    }
    public void setTow(Vehicle t) { _back = t; }

    public Driver getDriver() throws PrecondViolation
    {   if (_front == null) { throw new PrecondViolation(); }
        return  _front.getDriver(); 
    }
    public boolean hasDriver() throws PrecondViolation
    {   if (_front == null) { throw new PrecondViolation(); }
        return  _front.hasDriver(); 
    }
    public PoweredVehicle getFront() { return _front; }
    public Vehicle getTow() { return _back; }
    public boolean hasTow() { return  (_back != null); }
    public String toString() 
    {   return "Front:" + _front + "|Back:" + _back + "|";   }

    private PoweredVehicle _front = null;
    private Vehicle        _back  = null;
}

interface CargoCarrier 
{   public void setCargo(Object c);
    public Object getCargo();
    public boolean isLoaded();
}

interface Driven
{   public void setDriver(Driver d) throws PrecondViolation;
    public Driver getDriver() throws PrecondViolation;
    public boolean hasDriver() throws PrecondViolation;
}

class Position
{   public Position(double x, double y) { _x = x; _y = y;   }
    public Position(Position p) { this(p.getx(), p.gety()); }

    public double getx() { return _x; }
    public double gety() { return _y; }
    public boolean equals(Object  p)
    {   if (p instanceof Position)
        {   Position pp = (Position) p;
            return (_x == pp._x && _y == pp._y);
        }
        else
        {   return false;   }
    }
    public String toString() { return "(" + _x + "," + _y + ")"; }

    private double _x;
    private double _y;
}

class Driver
{   public Driver() { _name = DEFAULTNAME; }
    public Driver(String name) { _name = name; }

    public static final String DEFAULTNAME = "*********";

    public String getName() { return _name; }
    public String toString() { return _name; }

    private String _name;
}
