|
CS 596 Java Programming Fall Semester, 1998 Assignment 4 Solution |
|
|---|---|---|
|
© 1998, All Rights Reserved, SDSU & Roger Whitney San Diego State University -- This page last updated 11-Dec-98 |
Comments
Favor writing classes over writing programs
Send messages to object to get things done
Keep independent items independent
Do one thing at a time
Solution One
Overview
Building Blocks LowerCaseInputStream
NetworkInputStreamFuture
import java.io.FilterInputStream;
import java.io.InputStream;
import java.io.IOException;
class LowerCaseInputStream extends FilterInputStream{
public static final int EOF = -1;
public LowerCaseInputStream( InputStream input ) {
super( input );
}
public int read() throws IOException {
int nextChar = super.read();
if ( nextChar == EOF )
return EOF;
else
return Character.toLowerCase( (char) nextChar );
}
public int read(byte[] bytes, int offset, int length) throws IOException {
int charsRead = super.read(bytes, offset, length);
if ( charsRead == EOF )
return EOF;
for ( int k = offset; k < offset + charsRead; k++)
bytes[ k ] = (byte) Character.toLowerCase( (char)bytes[ k ] ) ;
return charsRead;
}
}
UpperCaseInputStream
import java.io.FilterInputStream;
import java.io.InputStream;
import java.io.IOException;
class UpperCaseInputStream extends FilterInputStream {
public static final int EOF = -1;
public UpperCaseInputStream( InputStream input ) {
super( input );
}
public int read() throws IOException {
int nextChar = super.read();
if ( nextChar == EOF )
return EOF;
else
return Character.toUpperCase( (char) nextChar );
}
public int read(byte[] bytes, int offset, int length) throws IOException {
int charsRead = super.read(bytes, offset, length);
if ( charsRead == EOF )
return EOF;
for ( int k = offset; k < offset + charsRead; k++)
bytes[ k ] = (byte) Character.toUpperCase( (char)bytes[ k ] ) ;
return charsRead;
}
}
NoCloseInputStream
& NoCloseOutputStream
import java.io.FilterInputStream;
import java.io.InputStream;
import java.io.IOException;
class NoCloseInputStream extends FilterInputStream{
public NoCloseInputStream( InputStream input ) {
super( input );
}
public void close() {}
}
import java.io.FilterOutputStream;
import java.io.OutputStream;
import java.io.IOException;
class NoCloseOutputStream extends FilterOutputStream {
public NoCloseOutputStream( OutputStream output ) {
super( output );
}
public void close() {}
}
NetworkInputStream
import java.io.FilterInputStream;
import java.io.InputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
class NetworkInputStream extends FilterInputStream{
public static final int EOF = -1;
public NetworkInputStream( int aPortNumber ) throws IOException{
super( getSocketStream( aPortNumber ) );
}
private static InputStream getSocketStream( int aPortNumber )
throws IOException{
ServerSocket acceptor = new ServerSocket( aPortNumber );
Socket client = acceptor.accept();
return client.getInputStream();
}
}
NetworkInputStreamFuture
import java.net.ServerSocket;
import java.net.Socket;
import java.io.InputStream;
import java.io.IOException;
public class NetworkInputStreamFuture extends InputStream{
InputStream in;
NetworkAcceptor dataServer;
IOException networkIOException;
public NetworkInputStreamFuture( int aPortNumber ) {
dataServer = new NetworkAcceptor( aPortNumber);
int runNowPriority =
Math.max( Thread.currentThread().getPriority() + 1,
Thread.MAX_PRIORITY);
dataServer.setPriority( runNowPriority );
dataServer.start();
}
public int read() throws IOException {
joinDataServer();
return in.read();
}
public int read(byte b[]) throws IOException {
joinDataServer();
return read(b, 0, b.length);
}
public int read(byte b[], int off, int len) throws IOException {
joinDataServer();
return in.read(b, off, len);
}
NetworkInputStreamFuture
Continued
public long skip(long n) throws IOException {
joinDataServer();
return in.skip(n);
}
public int available() throws IOException {
joinDataServer();
return in.available();
}
public void close() throws IOException {
joinDataServer();
in.close();
}
public synchronized void mark(int readlimit){}
public synchronized void reset() throws IOException {
joinDataServer();
in.reset();
}
public boolean markSupported() {
return false;
}
NetworkInputStreamFuture
Continued
private void joinDataServer() throws IOException{
try {
dataServer.join();
}
catch (InterruptedException threadError ){
throw new IOException( "Network Acceptor interrupted while awaiting");
}
if (networkIOException != null )
throw networkIOException;
}
class NetworkAcceptor extends Thread {
int portNumber;
public NetworkAcceptor( int aPortNumber ){
portNumber = aPortNumber;
}
public void run() {
try {
ServerSocket acceptor = new ServerSocket( portNumber );
Socket client = acceptor.accept();
in = client.getInputStream();
}
catch (IOException socketIOException ) {
networkIOException = socketIOException;
}
}
}
}
ReplacingInputStream
import java.io.FilterInputStream;
import java.io.InputStream;
import java.io.StringBufferInputStream;
import java.io.IOException;
import gnu.regexp.REException;
import gnu.regexp.RE;
import gnu.regexp.RESyntax;
import gnu.regexp.REMatch;
class ReplacingInputStream extends FilterInputStream
{
public ReplacingInputStream( InputStream in,
String pattern,
String replacement ) throws IOException, REException {
super( getREStream( in, pattern, replacement ) );
}
private static InputStream getREStream( InputStream in,
String pattern,
String replacement ) throws IOException, REException {
RE replacer = new RE( pattern );
String result = replacer.substituteAll( in, replacement );
return new StringBufferInputStream( result );
}
}
ReplacingInputStreamFuture
import java.io.InputStream;
import java.io.IOException;
import gnu.regexp.REException;
public class ReplacingInputStreamFuture extends InputStream {
InputStream in;
REReplacer inputServer;
IOException futureException;
public ReplacingInputStreamFuture( InputStream in,
String pattern,
String replacement ) {
inputServer = new REReplacer( in, pattern, replacement );
inputServer.start();
}
public int read() throws IOException {
joinREReplacer();
return in.read();
}
public int read(byte b[]) throws IOException {
joinREReplacer();
return read(b, 0, b.length);
}
public int read(byte b[], int off, int len) throws IOException {
joinREReplacer();
return in.read(b, off, len);
}
ReplacingInputStreamFuture
Continued
public long skip(long n) throws IOException {
joinREReplacer();
return in.skip(n);
}
public int available() throws IOException {
joinREReplacer();
return in.available();
}
public void close() throws IOException {
joinREReplacer();
in.close();
}
public synchronized void mark(int readlimit){ }
public synchronized void reset() throws IOException {
joinREReplacer();
in.reset();
}
public boolean markSupported() { return false; }
private void joinREReplacer() throws IOException {
try {
inputServer.join();
}
catch (InterruptedException threadError ) {
throw new IOException( "RE Replacer Thread interrupted");
}
if (futureException != null )
throw futureException;
}
ReplacingInputStreamFuture
Continued
class REReplacer extends Thread {
private InputStream originalIn;
String pattern;
String replacement;
public REReplacer(InputStream in,
String aPattern,
String aReplacement ) {
originalIn = in;
pattern = aPattern;
replacement = aReplacement;
}
public void run() {
try {
in = new ReplacingInputStream( originalIn, pattern, replacement );
}
catch (IOException aStartException ) {
futureException = aStartException;
}
catch (REException reProblem ) {
futureException = new IOException( "REException occured:" +
reProblem.toString() );
}
}
}
}
ThreadEvent
import java.util.EventObject;
public class ThreadEvent extends EventObject {
Exception thrown;
Object result;
String message;
public ThreadEvent( Object source ) { super( source ); }
public ThreadEvent( Object source, Object threadResult ) {
super( source );
result = threadResult;
}
public ThreadEvent( Object source, Exception threadException ) {
super( source );
thrown = threadException;
}
public Exception getException() { return thrown; }
public Object getResult() { return result; }
public void setMessage( String aMessage ) { message = aMessage; }
public String getMessage() { return message; }
}
public interface ThreadListener {
public void threadResult( ThreadEvent anEvent );
public void threadExceptionThrown( ThreadEvent anEvent );
}
ThreadListenerHandler
import java.util.*;
public class ThreadListenerHandler {
ArrayList listeners = new ArrayList();
Object theListened;
public ThreadListenerHandler( Object listened ) {
theListened = listened;
}
public synchronized void addThreadListener( ThreadListener aListener ) {
listeners.add( aListener );
}
public synchronized void removeThreadListener( ThreadListener aListener ) {
listeners.remove( aListener );
}
public void broadcastResult( ThreadEvent anEvent ) {
Iterator sendList;
synchronized ( this ) {
sendList = ( (ArrayList ) listeners.clone()).iterator();
}
while ( sendList.hasNext() ) {
ThreadListener aListener = (ThreadListener) sendList.next();
aListener.threadResult( anEvent );
}
}
ThreadListenerHandler
Continued
public void broadcastResult( Object result ) {
ThreadEvent broadcastData =
new ThreadEvent( theListened, result );
broadcastResult( broadcastData );
}
public void broadcastException( Exception anException ) {
ThreadEvent broadcastData =
new ThreadEvent( theListened, anException );
broadcastException( broadcastData );
}
public void broadcastException( ThreadEvent anEvent ) {
Iterator sendList;
synchronized ( this ) {
sendList = ( (ArrayList ) listeners.clone()).iterator();
}
while ( sendList.hasNext() ) {
ThreadListener aListener = (ThreadListener) sendList.next();
aListener.threadExceptionThrown( anEvent );
}
}
}
FilterThread
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
/**
* Note the order of methods called is important. Must first set
* input source and output destination. Second must make call to
* uncompressInput() if it is needed.
*/
class FilterThread extends Thread{
public static final int INTERACTIVE_PRIORITY = 5;
public static final int BACKGROUND_PRIORITY = 4;
ThreadListenerHandler listeners = new ThreadListenerHandler( this);
InputStream in;
OutputStream out;
//Used in error reporting, identifies thread's settings
String inputSource = "not set";
String outputDestination = "not set";
String filters = "";
public FilterThread( ){
setPriority( BACKGROUND_PRIORITY );
}
FilterThread
Continued
public void keyboardInput(){
in = new NoCloseInputStream( System.in );
setPriority( INTERACTIVE_PRIORITY );
inputSource = "keyboard";
}
public void fileInput( String fileName ) throws IOException {
inputSource = "file: " + fileName;
File inputFile = new File( fileName );
if ( !inputFile.exists() ) {
throw new IOException( "Input file: " + fileName +
" does not exist" );
}
in = new BufferedInputStream(
new FileInputStream( inputFile ));
}
public void networkInput( int socketNumber ) throws IOException {
inputSource = "network socket: " + socketNumber;
in = new BufferedInputStream(
new NetworkInputStreamFuture( socketNumber ));
}
public void screenOutput() {
outputDestination = "screen";
out = new NoCloseOutputStream( System.out );
setPriority( INTERACTIVE_PRIORITY );
}
FilterThread
Continued
public void fileOutput( String fileName ) throws IOException {
SimpleDateFormat formatter =
new SimpleDateFormat( ".yy-DDD-HH:mm:ss");
Date now = new Date();
String timeStamp = formatter.format( now );
fileName = fileName + timeStamp;
outputDestination = "file: " + fileName;
out = new BufferedOutputStream(
new FileOutputStream( fileName ));
}
public void uncompressInput() throws IOException {
filters = filters + " compressed input";
in = new GZIPInputStream( in );
}
public void upperCaseText() throws IOException {
filters = filters + " Upper case";
in = new UpperCaseInputStream( in );
}
public void lowerCaseText() throws IOException {
filters = filters + " Upper case";
in = new LowerCaseInputStream( in );
}
public void compressOutput() throws IOException {
filters = filters + " compressed output";
out = new GZIPOutputStream( out );
}
FilterThread
Continued
public void replaceText( String pattern, String replacement ) {
filters = filters + " replace: " + pattern + " with: " + replacement;
in = new ReplacingInputStreamFuture( in, pattern, replacement );
}
public synchronized void addThreadListener( ThreadListener aListener ) {
listeners.addThreadListener( aListener );
}
public synchronized void removeThreadListener( ThreadListener aListener ) {
listeners.removeThreadListener( aListener );
}
public void run() {
final int EOF = -1;
final int bufferSize = 1024;
int charsRead = 0;
byte[] buffer = new byte[ bufferSize];
try {
charsRead = in.read( buffer, 0, bufferSize);
while ( charsRead != EOF ) {
out.write( buffer, 0, charsRead );
charsRead = in.read( buffer, 0, bufferSize);
}
}
catch (Exception ioProblem ) {
broadcastException( ioProblem );
}
finally { closeStreams(); }
}
FilterThread
Continued
private void closeStreams() {
try {
in.close();
out.close();
}
catch (IOException closeProblem ) {
listeners.broadcastException( closeProblem );
}
}
public void broadcastException( Exception problem ) {
ThreadEvent exceptionInfo = new ThreadEvent( this, problem );
String lineSeparator = System.getProperty( "line.separator" );
String message = "Input: " + inputSource + lineSeparator +
"output: " + outputDestination + lineSeparator +
"filters: " + filters;
exceptionInfo.setMessage( message );
listeners.broadcastException( exceptionInfo );
}
}
FilterCreator
import sdsu.util.ProgramProperties;
import java.io.*;
import java.text.*;
import java.util.*;
public class FilterCreator{
ProgramProperties ioOptions;
public FilterCreator( ProgramProperties options ) {
ioOptions = options;
}
public static void main( String[] args ) throws IOException {
FilterCreator main =
new FilterCreator( new ProgramProperties( args, "config.properties" ) );
FilterMenus userInput = new FilterMenus( System.in, System.out, main );
userInput.run();
}
public void processFileInput( String fileName, ThreadListener errorReporter ){
FilterThread fileProcessor = new FilterThread();
try {
fileProcessor.fileInput( fileName );
addFilters( fileProcessor );
fileProcessor.addThreadListener(errorReporter);
fileProcessor.start();
joinOnScreenOutput( fileProcessor );
}
catch (Exception error ) {
errorReporter.threadExceptionThrown( new ThreadEvent( this, error ) );
}
}
FilterCreator
Continued
public void processNetworkInput( int portNumber,
ThreadListener errorReporter) {
FilterThread networkProcessor = new FilterThread();
try {
networkProcessor.networkInput( portNumber );
addFilters( networkProcessor );
networkProcessor.addThreadListener(errorReporter);
networkProcessor.start();
joinOnScreenOutput( networkProcessor );
}
catch (Exception error ) {
errorReporter.threadExceptionThrown( new ThreadEvent( this, error ) );
}
}
public void processKeyboardInput( ThreadListener errorReporter )
throws IOException {
FilterThread keyboardProcessor = new FilterThread();
try {
keyboardProcessor.keyboardInput();
addFilters( keyboardProcessor );
keyboardProcessor.addThreadListener(errorReporter);
keyboardProcessor.start();
keyboardProcessor.join();
}
catch (Exception error ) {
errorReporter.threadExceptionThrown( new ThreadEvent( this, error ) );
}
}
FilterCreator
Continued
void addFilters( FilterThread aFilterThread ) throws IOException {
//Order is important
addOutput( aFilterThread );
addCompression( aFilterThread );
addReplacement( aFilterThread );
addCaseFilter( aFilterThread );
}
void addReplacement( FilterThread aFilterThread ) throws IOException {
if ( ! ioOptions.containsKey( "pattern") )
return;
if ( ! ioOptions.containsKey( "replacement") )
return;
String pattern = ioOptions.getString( "pattern" );
String replacement = ioOptions.getString( "replacement" );
aFilterThread.replaceText( pattern, replacement );
}
void addOutput( FilterThread aFilterThread ) throws IOException {
String output = ioOptions.getString( "output", "screen" );
if ( output.equals( "file" ) ) {
String fileName = ioOptions.getString( "outputFile" );
aFilterThread.fileOutput( fileName );
}
else if ( output.equals( "screen" ))
aFilterThread.screenOutput();
else
throw new IOException( "Invalid output device: " + output );
}
FilterCreator
Continued
void addCompression( FilterThread aFilterThread ) throws IOException {
String inCompression = ioOptions.getString( "compressedInput", "false" );
if ( inCompression.equals( "true" ) )
aFilterThread.uncompressInput( );
String outCompression =
ioOptions.getString( "compressedOutput", "false" );
if ( outCompression.equals( "true" ) )
aFilterThread.compressOutput( );
}
void addCaseFilter( FilterThread aFilterThread ) throws IOException {
String textCase = ioOptions.getString( "textCase", "noChange" ).trim();
if ( textCase.equals( "upper" ) )
aFilterThread.upperCaseText( );
else if ( textCase.equals( "lower" ) )
aFilterThread.lowerCaseText( );
}
private void joinOnScreenOutput( FilterThread aFilter )
throws InterruptedException {
String output = ioOptions.getString( "output", "screen" );
if ( output.equals( "screen" ) )
aFilter.join();
}
}
FilterMenus
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.io.PrintStream;
import sdsu.io.ASCIIInputStream;
import java.util.*;
class FilterMenus implements ThreadListener {
static final String FILE = "1";
static final String NETWORK = "2";
static final String KEYBOARD = "3";
static final String QUIT = "q";
static final String SEPARATOR = "----";
ASCIIInputStream in;
PrintStream out;
FilterCreator processor;
ArrayList errorMessages = new ArrayList();
public FilterMenus( InputStream anInput,
OutputStream anOutput,
FilterCreator aProcessor ) {
in = new ASCIIInputStream( anInput);
out = new PrintStream( anOutput);
processor = aProcessor;
}
// No results are sent back, only exceptions
public void threadResult( ThreadEvent anEvent ) { }
public void threadExceptionThrown( ThreadEvent anEvent ){
synchronized ( errorMessages ) { errorMessages.add( anEvent ); }
}
FilterMenus
Continued
public void run() {
String selection;
try {
do {
displayErrorMessages();
selection = getMainMenusResult();
handleUserSelection( selection );
}
while ( !selection.equals( QUIT ) );
displayErrorMessages();
out.println( "Good Bye" );
}
catch ( IOException readWriteProblem ){
System.err.println( "There was a problem interacting with the user" );
}
}
void displayErrorMessages() {
Iterator toDisplay;
synchronized ( errorMessages ) {
toDisplay = ( (ArrayList ) errorMessages.clone()).iterator();
errorMessages.clear();
}
while ( toDisplay.hasNext() ) {
ThreadEvent anEvent = (ThreadEvent) toDisplay.next();
out.println( SEPARATOR );
out.println( "There was a problem with your request");
out.println( anEvent.getMessage() );
out.println( anEvent.getException() );
}
}
FilterMenus
Continued
String getMainMenusResult() throws IOException {
String selection = showMainMenus();
while ( !validateMainMenusInput( selection ) ) {
out.println( selection + " not valid input, try again.");
selection = showMainMenus();
}
return selection.trim().toLowerCase();
}
String showMainMenus() throws IOException {
out.println( SEPARATOR);
out.println( "Select one of the following:");
out.println( " 1. File input");
out.println( " 2. Network input");
out.println( " 3. Keyboard input");
out.println( " Q. Quit");
out.print( "Type your selection: ");
String input = in.readLine();
return input;
}
boolean validateMainMenusInput( String input ) {
if (input == null ) return false;
input = input.trim().toLowerCase();
if (input.length() == 0 ) return false;
if (input.startsWith( FILE ) || input.startsWith( NETWORK ) ||
input.startsWith( KEYBOARD ) || input.startsWith( QUIT ) )
return true;
return false;
}
FilterMenus
Continued
void handleUserSelection( String selection ) throws IOException {
if (selection.startsWith( FILE ) ) {
String fileName = getFileName();
processor.processFileInput( fileName, this );
}
else if (selection.startsWith( NETWORK ) ) {
int socketNumber = getSocket();
processor.processNetworkInput( socketNumber, this );
}
else if (selection.startsWith( KEYBOARD ) )
processor.processKeyboardInput( this );
}
int getSocket( ) throws IOException {
out.println( SEPARATOR);
out.print( "Type the socket number: ");
int input = in.readInt();
in.flushLine();
while ( input < 5000 ) {
out.println( "" + input +
" is not a valid socket number between 5000 and 65536. Try again. ");
out.println( SEPARATOR);
out.print( "Type the socket number: ");
input = in.readInt();
in.flushLine();
}
return input;
}
FilterMenus
Continued
String getFileName( ) throws IOException {
out.println( SEPARATOR);
out.print( "Type the name of the file: ");
String input = in.readLine();
while ( input == null || input.length() == 0 ) {
out.println( "Not a valid file name. Try again. " );
out.println( SEPARATOR);
out.print( "Type the name of the file: ");
input = in.readLine();
}
return input;
}
}
Improvements
Use
a counting semaphore to synchronize keyboard & screen use. This will
provide better concurrency than using the joins.
Use
reflection to remove the order dependencies of the method calls in FilterThread.
Create
a prototype FilterThread with the settings from the config file and command
line.
The
above three improvements should allow us to remove the class FilterCreator
Create
a generic ASCI Menu system to replace FilterMenu
Copyright © 1998 SDSU & Roger Whitney, 5500 Campanile Drive, San Diego, CA 92182-7700 USA.
All rights reserved.
visitors since 07-Dec-98