import java.io.*;
import java.net.*;


/**
 * Provides a generic way to create proper I/O classes based on resource name.
 *
 * @author Robert C. Duvall
 */
abstract public class IOFactory
{
    /**
     * Given name of resource, create a generic reader for it.
     */
    public BufferedReader makeReader (String name)
    {
        try
        {
            return new BufferedReader(getReader(name));
        }
        catch (IOException e)
        {
            e.printStackTrace();
            System.exit(1);
        }

        // required by compiler :(
        return null;
    }

    /**
     * Given name of resource, create a generic writer for it.
     */
    public PrintWriter makeWriter (String name)
    {
        try
        {
            return new PrintWriter(getWriter(name));
        }
        catch (IOException e)
        {
            e.printStackTrace();
            System.exit(1);
        }

        // required by compiler :(
        return null;
    }

    /**
     *  Returns true if a specific factory is capable of opening given resource.
     */
    abstract public boolean canOpen (String name);

    /**
     *  Returns specific reader for given resource.
     */
    abstract protected Reader getReader (String name) throws IOException;

    /**
     *  Returns specific writer for given resource.
     */
    abstract protected Writer getWriter (String name) throws IOException;
}


/**
 * Creates correct I/O classes for working with console input and output
 */
class Console extends IOFactory
{
    public boolean canOpen (String name)
    {
        return name.equals("") || name.startsWith("std");
    }

    protected Reader getReader (String name)
        throws IOException
    {
        return new InputStreamReader(System.in);
    }

    protected Writer getWriter (String name)
        throws IOException
    {
        return new OutputStreamWriter(System.out);
    }
}


/**
 * Creates correct I/O classes for working with network via HTTP protocol.
 */
class Web extends IOFactory
{
    public static final String PROTOCOL = "http:";


    public boolean canOpen (String name)
    {
        return name.startsWith(PROTOCOL);
    }

    protected Reader getReader (String name)
        throws IOException
    {
        URLConnection uc = new URL(name).openConnection();
        return new InputStreamReader(uc.getInputStream());
    }

    protected Writer getWriter (String name)
        throws IOException
    {
        // BUGBUG: this does not really make sense as is
        // perhaps could post data to a survey or something
        URLConnection uc = new URL(name).openConnection();
        return new OutputStreamWriter(uc.getOutputStream());
    }
}


/**
 * Creates correct I/O classes for working with network via sockets.
 */
class Net extends IOFactory
{
    public static final String DOMAIN = "duke.edu";
    public static final int PORT = 1234;  // arbitrary value > 1024


    public boolean canOpen (String name)
    {
        return (name.endsWith(DOMAIN)) || name.equals("localhost");
    }

    protected Reader getReader (String name)
        throws IOException
    {
        Socket s = new Socket(name, PORT);
        return new InputStreamReader(s.getInputStream());
    }

    protected Writer getWriter (String name)
        throws IOException
    {
        // do not write until a reader is listening
        ServerSocket ss = new ServerSocket(PORT);
        Socket s = ss.accept();
        return new OutputStreamWriter(s.getOutputStream());
    }
}


/**
 * Creates correct I/O classes for working with files.
 */
class File extends IOFactory
{
    public boolean canOpen (String name)
    {
        // not truly useful, but demonstrates exceptions
        try
        {
            FileReader tmp = new FileReader(name);
            return true;
        }
        catch (java.io.FileNotFoundException e)
        {
            try
            {
                FileWriter tmp = new FileWriter(name);
                return true;
            }
            catch (java.io.IOException fne)
            {
                return false;
            }
        }
    }

    protected Reader getReader (String name)
        throws IOException
    {
        return new FileReader(name);
    }

    protected Writer getWriter (String name)
        throws IOException
    {
        return new FileWriter(name);
    }
}
