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 FilterMenuCopyright © 1998 SDSU & Roger Whitney, 5500 Campanile Drive, San Diego, CA 92182-7700 USA.
All rights reserved.
visitors since 07-Dec-98