/* * [How.java] * * Summary: Generate sample source code to show you how to do various types of File IO. * * Copyright: (c) 1997-2017 Roedy Green, Canadian Mind Products, http://mindprod.com * * Licence: This software may be copied and used freely for any purpose but military. * http://mindprod.com/contact/nonmil.html * * Requires: JDK 1.8+ * * Created with: JetBrains IntelliJ IDEA IDE http://www.jetbrains.com/idea/ * * Version History: * 5.7 2008-04-02 add build number to title * 5.8 2009-12-21 use keyword final as much as possible in generated code. * 5.9 2010-02-21 use of FastCat instead of StringBuilder. Generate code to use Aux buffering for Readers and Writers. * 6.0 2010-12-06 support getResourceAsStream, FileReader, FileWriter. * 6.1 2011-12-03 configurable Look and Feel * 6.2 2014-05-10 optimal buffering ratios */ package com.mindprod.fileio; import com.mindprod.common18.Build; import com.mindprod.fastcat.FastCat; /* * Future work: * TODO: allow BufferedReaders from StringReaders CharArray so can do readLine. * TODO: allow buffered, Readers, DataInput etc from get/put/url * TODO: use unique animals. * TODO: rewrite the code to use subclasses instead of enumerations. * TODO: FTP upload/download properties read/write file copy * TODO: RMI pickle and revive java.util.zip.ZipOutputStream * TODO: support compressed readers */ /** * Generate sample source code to show you how to do various types of File IO. * * @author Roedy Green, Canadian Mind Products * @version 6.2 2014-05-10 optimal buffering ratioss * @since 1997 */ final class How { /** * comment to follow encoding usages. */ private static final String ABOUT_ENCODING = "// usually UTF-8 for Unicode 8-bit giving the full Unicode set, no BOMs.\n" + "// IBM437 is IBM OEM. IBM850 is what Take Command uses. windows-1252 is Windows default. ISO-8859-1 is Latin-1.\n" + "// See \"encoding\" in the Java glossary for alternatives\n" + "// such as UTF-16BE for 16-bit, big-endian Unicode characters.\n"; /** * space to allocate for aux BufferedReader/BufferedWriter */ private static final String AUX_CHAR_BUFFER = "16_384 /* 32K bytes/16K chars, 50% of 64K byte allocation is optimal */"; /** * space to allocate for intrenal Steam of a BufferedReader/Writer */ private static final String AUX_BYTE_BUFFER = "32_768 /* 32K bytes, 50% of 64K byte allocation is optimal */"; /** * space to allocate for byte byte */ private static final String STREAM_BYTE_BUFFER = "65_536 /* 64K bytes */"; /** * Sample GET CGI actually an echo server */ private static final String GET_CGI_URL = "\"http://mindprod.com/cgi-bin/query\""; /** * Sample GET URL */ private static final String GET_URL = "\"http://www.billabong.com:80/songs/lyrics.txt\""; /** * Sample POST CGI, actually an echo server */ private static final String POST_CGI_URL = "\"http://mindprod.com/cgi-bin/post-query\""; /** * Sample POST URL */ private static final String POST_URL = "\"http://www.billabong.com:80/songs/lyrics.txt\""; /** * Sample name for a temporary input file. */ private static final String TEMP_IN = "\"C:/temp/temp.in\""; /** * Sample name for a temporary output file. */ private static final String TEMP_OUT = "\"C:/temp/temp.out\""; /** * Sample name for a temporary random access file. */ private static final String TEMP_RAND = "\"C:/temp/rand.data\""; /** * which format is our data? */ private static DataType dataType; /** * true - we are generating reads. */ private static boolean doingInput; /** * true - we are generating writes. */ private static boolean doingOutput; /** * Are we generate code for a buffered stream? */ private static boolean isBuffered; /** * Is the I/O stream compressed with GZIP? */ private static boolean isCompressed; /** * true if a Reader has been created. false if have an InputStream or nothing. */ private static boolean readerCreated; /** * what sort of "file" for to generate code for */ private static FileType target; /** * name of current file in handle. */ private static String vIn; /** * name of current file in handle. */ private static String vOut; /** * true if a Writer has been created. false if have an OutputStream or nothing. */ private static boolean writerCreated; /** * HowToProcess is purely static. Hide the constructor. */ private How() { } /** * Ensure Java supports given combination of options. * * @return an error message or null if all is ok. */ private static String ensureSupported() { switch ( target ) { case BYTEARRAY: case HTTP: case SEQUENTIAL: case SOCKET: case URL: break; case CHARARRAY: if ( isCompressed ) { return ( "// Compressed data cannot be stored in char[].\n" ); } if ( isBuffered && doingOutput ) { return ( "// Buffering would lose the ability to do a to toCharArray to get the results.\n" ); } switch ( dataType ) { case UNICODE: break; case BIGEND: case ENCODED: case LITTLEEND: case LOCALEENCODED: case RAWBYTES: case SERIALISED: default: return ( "// Char arrays can only contain Unicode data.\n" ); } break; case CONSOLE: switch ( dataType ) { case LOCALEENCODED: case ENCODED: if ( isCompressed ) { return ( "// Console requires human-readable text.\n" ); } break; case BIGEND: case LITTLEEND: case RAWBYTES: case SERIALISED: case UNICODE: default: return ( "// Console requires human-readable text.\n" ); } break; case PIPE: if ( isBuffered && doingOutput && !( dataType == DataType .LOCALEENCODED || dataType == DataType.ENCODED ) ) { return ( "// There is no point in buffering because pipe queues are stored in RAM.\n" ); } // input buffering gets you the readLine method. break; case RANDOM: if ( isBuffered ) { return ( "// Buffering is incompatible with random access.\n" ); } if ( isCompressed ) { return ( "// Compression requires a sequential stream.\n" ); } switch ( dataType ) { case SERIALISED: return ( "// Serialized objects require a sequential stream.\n" ); case ENCODED: return ( "// locale-encoding requires a sequential stream.\n" ); case BIGEND: case LITTLEEND: case LOCALEENCODED: case RAWBYTES: case UNICODE: default: } break; case RESOURCE: if ( doingOutput ) { return ( "// You can't write to a resource. You must rebuild the jar or treat it as a standalone " + "file.\n" ); } break; case STRING: if ( isCompressed ) { return ( "// Compressed data cannot be stored in a String.\n" ); } switch ( dataType ) { case UNICODE: break; case BIGEND: case ENCODED: case LITTLEEND: case LOCALEENCODED: case RAWBYTES: case SERIALISED: default: return ( "// Strings can only contain Unicode data.\n" ); } break; default: assert false : "no case for target " + target; } // end outer switch // should not happen. if ( isBuffered && isCompressed ) { return ( "// Compression has built-in buffering.\n" ); } return null;// all was ok if we got through the gauntlet } // end ensureSupported /** * usually null, but otherwise generates commands to add input auxiliary buffering. * Buffer the InputStream. Later Reader may be buffered. * * @return generated Java source. */ private static String howAuxBufferInput() { final String vPrev = vIn; final FastCat sb = new FastCat( 10 ); if ( isBuffered ) { if ( FileIO.DEBUGGING ) { sb.append( "\n// A U X I N P U T B U F F E R\n" ); } switch ( target ) { case BYTEARRAY: case CHARARRAY: case CONSOLE: case RANDOM: case STRING: break; case HTTP: case PIPE: case RESOURCE: case SEQUENTIAL: case SOCKET: case URL: default: if ( dataType == DataType.ENCODED && !readerCreated ) { vIn = "bis"; sb.append( "final BufferedInputStream " ); sb.append( vIn ); sb.append( " = new BufferedInputStream( " ); sb.append( vPrev ); sb.append( ", " ); sb.append( isACharStream( dataType ) ? AUX_BYTE_BUFFER : STREAM_BYTE_BUFFER ); sb.append( " );\n" ); } break; // others have only one buffer, provided later in howBufferInput } // end inner switch } return sb.toString(); } /** * usually null, but otherwise generates commands to add output auxiliary buffering, * Buffer the OutputStream. Later Writer may be buffered. * * @return generated Java source. */ private static String howAuxBufferOutput() { String vPrev = vOut; final FastCat sb = new FastCat( 10 ); if ( isBuffered ) { // what to output to open the file if ( FileIO.DEBUGGING ) { sb.append( "\n// A U X O U T P U T B U F F E R\n" ); } switch ( target ) { case BYTEARRAY: case CHARARRAY: case CONSOLE: case RANDOM: case STRING: break; case HTTP: case PIPE: case RESOURCE: case SEQUENTIAL: case SOCKET: case URL:// write url default: if ( dataType == DataType.ENCODED && !writerCreated ) { vOut = "bos"; sb.append( "final BufferedOutputStream " ); sb.append( vOut ); sb.append( " = new BufferedOutputStream( " ); sb.append( vPrev ); sb.append( ", " ); sb.append( isACharStream( dataType ) ? AUX_BYTE_BUFFER : STREAM_BYTE_BUFFER ); sb.append( " );\n" ); } // others have only one buffer, provided later in howBufferOutput } // end inner switch } return sb.toString(); } /** * usually null, but otherwise generates commands to add input buffering. * * @return generated Java source. */ private static String howBufferInput() { final String vPrev = vIn; final FastCat sb = new FastCat( 10 ); if ( isBuffered ) { if ( FileIO.DEBUGGING ) { sb.append( "\n// B U F F E R\n" ); } if ( readerCreated ) { vIn = "br"; sb.append( "final BufferedReader " ); sb.append( vIn ); sb.append( " = new BufferedReader( " ); sb.append( vPrev ); sb.append( ", " ); sb.append( AUX_CHAR_BUFFER ); sb.append( " );\n" ); } else { vIn = "bis"; sb.append( "final BufferedInputStream " ); sb.append( vIn ); sb.append( " = new BufferedInputStream( " ); sb.append( vPrev ); sb.append( ", " ); sb.append( isACharStream( dataType ) ? AUX_BYTE_BUFFER : STREAM_BYTE_BUFFER ); sb.append( " );\n" ); } } // end if return sb.toString(); } // end howBufferInput /** * usually null, but otherwise generates commands to add output buffering. * * @return generated Java source. */ private static String howBufferOutput() { final String vPrev = vOut; final FastCat sb = new FastCat( 10 ); if ( isBuffered ) { if ( FileIO.DEBUGGING ) { sb.append( "\n// B U F F E R\n" ); } // what to output to buffer the file if ( writerCreated ) { // we have an OutputStreamWriter already, even for CONSOLE vOut = "bw"; sb.append( "final BufferedWriter " ); sb.append( vOut ); sb.append( " = new BufferedWriter( " ); sb.append( vPrev ); sb.append( ", " ); sb.append( AUX_CHAR_BUFFER ); sb.append( " );\n" ); } else { vOut = "bos"; sb.append( "final BufferedOutputStream " ); sb.append( vOut ); sb.append( " = new BufferedOutputStream( " ); sb.append( vPrev ); sb.append( ", " ); sb.append( isACharStream( dataType ) ? AUX_BYTE_BUFFER : STREAM_BYTE_BUFFER ); sb.append( " );\n" ); } } // end if return sb.toString(); } // end howBufferOutput /** * generate warnings for input. * * @return generated Java source containing any warnings. */ private static String howCaveatInput() { final FastCat sb = new FastCat( 10 ); sb.append( standardCaveats() ); switch ( target ) { case BYTEARRAY: case CHARARRAY: case CONSOLE: case HTTP: case STRING: break; case PIPE: sb.append( "// WARNING! consumer/input/receiver pipes can connect to at most one producer/output/sender " + "pipe.\n" ); sb.append( "// To allow multiple producers, you need message-level synchronisation to a single producer " + "pipe.\n" ); sb.append( "// WARNING! Pipes are for inter-thread communication only\n" ); sb.append( "// not communication within a thread or between processes.\n" ); break; case RESOURCE: sb.append( "// resource must live in jar or on classpath." ); break; case RANDOM: case SEQUENTIAL: sb.append( "// WARNING! unsigned Applets may not read the local hard disk.\n" ); break; case SOCKET: case URL: sb.append( "// WARNING! unsigned Applets may only read from the server they were loaded from.\n" ); sb.append( "// Sockets are a low level tool.\n" ); sb.append( "// If you implement HTTP via low level sockets, you will see all the headers and length " + "bytes.\n" ); sb.append( "// For HTTP normally you want HttpURLConnection rather than Socket.\n" ); sb.append( "// To copy/download files see http://mindprod.com/products.html#FILETRANSFER.\n" ); sb.append( "// To GET/PUT to a server see http://mindprod.com/products.html#HTTP for a library of useful" + " methods.\n" ); break; default: assert false : "no case for target " + target; } // end sourceTarget switch switch ( dataType ) { case LITTLEEND: sb.append( "// WARNING! Little endian is the native binary format on Windows/Intel machines.\n" ); sb.append( "// The Java standard is big endian binary, with the most significant byte first.\n" ); break; case LOCALEENCODED: sb.append( "// WARNING! locale-encoded expects different file formats on different computers. Better " + "to use a specific encoding. Also generates more efficient buffering.\n" ); break; } if ( sb.length() != 0 ) { sb.append( "\n" ); } return sb.toString(); } // end howCaveatInput /** * generate warnings for output. * * @return generated Java source. */ private static String howCaveatOutput() { final FastCat sb = new FastCat( 10 ); sb.append( standardCaveats() ); switch ( target ) { case BYTEARRAY: case CHARARRAY: case CONSOLE: case STRING: // nothing break; case HTTP: sb.append( "// Writing to a server over the Internet is usually done indirectly with sockets,\n" ); sb.append( "// RMI, servlets, CGI, HTTP PUT or JDBC.\n" ); sb.append( "// Further, unsigned Applets may only communicate with the server they were loaded from" + ".\n" ); break; case PIPE: sb.append( "// WARNING! producer/output/sender pipes can connect to at most one consumer/input/receiver " + "pipe.\n" ); sb.append( "// To allow multiple consumers, you need message-level synchronisation to a single consumer " + "pipe.\n" ); sb.append( "// WARNING! Pipes are for inter-thread communication only\n" ); sb.append( "// not communication within a thread or between processes.\n" ); break; case RESOURCE: sb.append( "// You cannot write to a resource.\n" ); break; case RANDOM: case SEQUENTIAL: sb.append( "// WARNING! unsigned Applets may not write to the local hard disk.\n" ); break; case SOCKET: sb.append( "// WARNING! unsigned Applets may only write to the server they were loaded from.\n" ); sb.append( "// Sockets are a low level tool. If you implement HTTP via low level sockets, " + "you will have to create all the headers and length bytes.\n" ); sb.append( "// For HTTP normally you want HttpURLConnection rather than Socket.\n" ); break; case URL: sb.append( "// WARNING! This code writes data to a URL, which may not be a successful way\n" ); sb.append( "// to write or upload a file.\n" ); sb.append( "// Writing to a server over the Internet is usually done indirectly with sockets,\n" ); sb.append( "// RMI, servlets, CGI, HTTP PUT or JDBC.\n" ); sb.append( "// Further, unsigned Applets may only communicate with the server they were loaded from" + ".\n" ); break; default: assert false : "no case for target " + target; } // end target switch switch ( dataType ) { case LITTLEEND: sb.append( "// WARNING! Little endian is the native binary format on Windows/Intel machines.\n" ); sb.append( "// The Java standard is big endian binary, with the most significant byte first.\n" ); break; case LOCALEENCODED: sb.append( "// WARNING! locale-encoded expects different file formats on different computers. Better " + "to use a specific encoding. Also generates more efficient buffering.\n" ); break; } if ( sb.length() != 0 ) { sb.append( "\n" ); } return sb.toString(); } // end howCaveatOutput /** * Generates code to close an input file. * * @return generated Java source. */ private static String howCloseInput() { final FastCat sb = new FastCat( 5 ); sb.append( "\n// C L O S E\n" ); switch ( target ) { case HTTP: sb.append( vIn ); sb.append( ".close();\n" ); sb.append( "urlc.disconnect();\n" ); break; default: sb.append( vIn ); sb.append( ".close();\n" ); } return sb.toString(); } /** * generates code to close an output file. * * @return generated Java source. */ private static String howCloseOutput() { final FastCat sb = new FastCat( 5 ); sb.append( "\n// C L O S E\n" ); sb.append( vOut ); sb.append( ".close();\n" ); if ( target == FileType.BYTEARRAY ) { // can get in trouble if do prior to close // can't do toByteArray on DataOutputStream. Need // to do it on the underlying ByteArrayOutputStream sb.append( "byte[] result = baos.toByteArray();\n" ); } return sb.toString(); } /** * Generate top of file comments for input. * * @return generated Java source. */ private static String howCommentInput() { final FastCat sb = new FastCat( 10 ); if ( FileIO.DEBUGGING ) { sb.append( "// O V E R V I E W\n" ); } sb.append( "// Read " ); sb.append( dataType.toString() ); sb.append( " from a " ); sb.append( isBuffered ? "buffered " : "" ); sb.append( isCompressed ? "compressed " : "" ); sb.append( target.toString() ); sb.append( ".\n" ); return sb.toString(); } // end howCommentInput /** * Generate top of file comments for output. * * @return generated Java source. */ private static String howCommentOutput() { final FastCat sb = new FastCat( 10 ); if ( FileIO.DEBUGGING ) { sb.append( "// C O M M E N T\n" ); } sb.append( "// Write " ); sb.append( dataType.toString() ); sb.append( " into a " ); sb.append( isBuffered ? "buffered " : "" ); sb.append( isCompressed ? "compressed " : "" ); sb.append( target.toString() ); sb.append( ".\n" ); return sb.toString(); } // end howCommentOutput /** * usually null, but otherwise generates commands to add input compression handling. * * @return generated Java source. */ private static String howCompressInput() { final String vPrev = vIn; final FastCat sb = new FastCat( 10 ); if ( FileIO.DEBUGGING ) { sb.append( "\n// C O M P R E S S\n" ); } if ( isCompressed ) { vIn = "gzis"; sb.append( "final GZIPInputStream " ); sb.append( vIn ); sb.append( " = new GZIPInputStream( " ); sb.append( vPrev ); sb.append( ", ", STREAM_BYTE_BUFFER, " );\n" ); } // end if return sb.toString(); } // end howCompressInput /** * usually null, but otherwise generates commands to add output compression handling. * * @return generated Java source. */ private static String howCompressOutput() { final String vPrev = vOut; final FastCat sb = new FastCat( 10 ); if ( FileIO.DEBUGGING ) { sb.append( "\n// C O M P R E S S\n" ); } if ( isCompressed ) { vOut = "gzos"; sb.append( "final GZIPOutputStream " ); sb.append( vOut ); sb.append( " = new GZIPOutputStream( " ); sb.append( vPrev ); sb.append( ", ", STREAM_BYTE_BUFFER, " );\n" ); } // end if return sb.toString(); } // end howCompressOutput /** * Generates Java code to decode a file for input. Here is where we create Readers, choose encodings. * DataInputStreams are added later in howFormatInput. * * @return generated Java source. */ private static String howDecodeInput() { String vPrev = vIn; // code to decode the file, e.g. select Readers. final FastCat sb = new FastCat( 10 ); // on entry vPrev/vIn usually has name of InputStream. if ( FileIO.DEBUGGING ) { sb.append( "\n// D E C O D E\n" ); } if ( !readerCreated ) { switch ( target ) { case CHARARRAY: // there is no existing stream, no such thing a CharArrayStream vIn = "car"; sb.append( "final char[] ica = new char[1000];\n" ); sb.append( "// ... code to fill ica with data to be read ...\n" ); sb.append( "final CharArrayReader " ); sb.append( vIn ); sb.append( " = new CharArrayReader( ica );\n" ); readerCreated = true; break; case RANDOM: // already a DataInputStream break; case STRING: vIn = "sr"; sb.append( "final String ids = \"data to be read as if from a file\";\n" ); sb.append( "final StringReader " ); sb.append( vIn ); sb.append( " = new StringReader( ids );\n" ); readerCreated = true; break; case BYTEARRAY: case CONSOLE: case HTTP: case PIPE: case RESOURCE: case SEQUENTIAL: case SOCKET: case URL: default: switch ( dataType ) { // for sequential, FileReader may be already be created. case LOCALEENCODED: vIn = "isr"; sb.append( "final InputStreamReader " ); sb.append( vIn ); sb.append( " = new InputStreamReader( " ); sb.append( vPrev ); sb.append( " );\n" ); readerCreated = true; break; case ENCODED: vIn = "eisr"; sb.append( "final InputStreamReader " ); sb.append( vIn ); sb.append( " = new InputStreamReader( " ); sb.append( vPrev ); sb.append( ",\"UTF-8\" );\n" ); sb.append( ABOUT_ENCODING ); readerCreated = true; break; default: } // end inner switch break; } // end outer switch } return sb.toString(); } /** * Generates Java code to encode a file for output. Here in where we create Readers, choose encodings, and add * DataInputStreams. * * @return generated Java source. */ private static String howEncodeOutput() { String vPrev = vOut; final FastCat sb = new FastCat( 10 ); // what to output to open the file if ( FileIO.DEBUGGING ) { sb.append( "\n// E N C O D E\n" ); } if ( !writerCreated ) { switch ( target ) { case CHARARRAY: // there is no such thing as a CharArrayStream vOut = "caw"; sb.append( "final CharArrayWriter " ); sb.append( vOut ); sb.append( " = new CharArrayWriter( 1000 );\n" ); writerCreated = true; break; case RANDOM: break; case STRING: // there is no such thing as a StringOutputStream vOut = "sw"; sb.append( "final StringWriter " ); sb.append( vOut ); sb.append( " = new StringWriter();\n" ); writerCreated = true; break; case RESOURCE: // can't write break; case BYTEARRAY: case CONSOLE: case HTTP: case PIPE: case SEQUENTIAL: case SOCKET: case URL:// write url default: switch ( dataType ) { // for sequential FileWriter may already be created. case LOCALEENCODED: vOut = "osw"; sb.append( "final OutputStreamWriter " ); sb.append( vOut ); sb.append( " = new OutputStreamWriter( " ); sb.append( vPrev ); sb.append( " );\n" ); writerCreated = true; break; case ENCODED: vOut = "eosw"; /* encoded OutputStreamWriter */ sb.append( "final OutputStreamWriter " ); sb.append( vOut ); sb.append( " = new OutputStreamWriter( " ); sb.append( vPrev ); sb.append( ",\"UTF-8\" );\n" ); sb.append( ABOUT_ENCODING ); writerCreated = true; break; default: /* nothing special */ } break; } // end switch } return sb.toString(); } // end howEncodeOutput /** * Generates Java code to flush a file, only applies to output. Also demonstrating data from a stream in progress. * * @return generated Java source. */ private static String howFlush() { final FastCat sb = new FastCat( 40 ); if ( FileIO.DEBUGGING ) { sb.append( "\n// F L U S H\n" ); } switch ( target ) { case BYTEARRAY: sb.append( vOut ); sb.append( ".flush();\n" ); /* must do finish on underlying GZIPOutputStream */ if ( isCompressed ) { sb.append( "gzos.finish();\n" ); } break; case CHARARRAY: sb.append( vOut ); sb.append( ".flush();\n" ); sb.append( "final char[] result = " ); sb.append( vOut ); sb.append( ".toCharArray();\n" ); break; case CONSOLE: case HTTP: case PIPE: case SEQUENTIAL: case SOCKET: case URL: sb.append( vOut ); sb.append( ".flush();\n" ); break; case RANDOM: // no such thing as flush. break; case RESOURCE: // can't write break; case STRING: sb.append( vOut ); sb.append( ".flush();\n" ); sb.append( "final String result = " ); sb.append( vOut ); sb.append( ".toString();\n" ); break; default: assert false : "no case for target " + target; } // end switch return sb.toString(); } // end howFlush /** * Generates additional open handling to provide formatting routines for input. Add DataInputStreams. * * @return generated Java source. */ private static String howFormatInput() { final String vPrev = vIn; final FastCat sb = new FastCat( 50 ); // code we generate if ( FileIO.DEBUGGING ) { sb.append( "\n// F O R M A T\n" ); } switch ( dataType ) { case BIGEND: // RandomAccessFile has the DataInputStream stuff built-in if ( target != FileType.RANDOM ) { vIn = "dis"; sb.append( "final DataInputStream " ); sb.append( vIn ); sb.append( " = new DataInputStream( " ); sb.append( vPrev ); sb.append( " );\n" ); } break; case ENCODED: case LOCALEENCODED: case RAWBYTES: // don't convert to DataInputStream break; case LITTLEEND: if ( target != FileType.RANDOM ) { vIn = "ledis"; sb.append( "// NOTE: LEDataInputStream is available free from Canadian Mind Products.\n" ); sb.append( "// See http://mindprod.com/products.html#LEDATASTREAM.\n" ); sb.append( "final LEDataInputStream " ); sb.append( vIn ); sb.append( " = new LEDataInputStream( " ); sb.append( vPrev ); sb.append( " );\n" ); } break; case SERIALISED: vIn = "ois"; if ( target == FileType.SOCKET ) { sb.append( "// Input data had better be available at open time to check the header\n" ); } sb.append( "final ObjectInputStream " ); sb.append( vIn ); sb.append( " = new ObjectInputStream( " ); sb.append( vPrev ); sb.append( " );\n" ); break; case UNICODE: switch ( target ) { case CHARARRAY: case HTTP: case RANDOM: case STRING: break; case BYTEARRAY: case PIPE: case RESOURCE: case SEQUENTIAL: case SOCKET: case URL: vIn = "dis"; sb.append( "final DataInputStream " ); sb.append( vIn ); sb.append( " = new DataInputStream( " ); sb.append( vPrev ); sb.append( " );\n" ); break; case CONSOLE: assert false : "attempt to write Unicode on console"; break; default: assert false : "no case for target " + target; } // end switch break; default: assert false : "no case for datatype " + dataType; } // end switch return sb.toString(); } // end howFormatInput // misc sample filenames to use in generated code. /** * Generates additional open handling to provide formatting routines for output. Add DataOutputStreams and * PrintWriters, ObjectOutputStreams * * @return generated Java source. */ private static String howFormatOutput() { final String vPrev = vOut; final FastCat sb = new FastCat( 40 ); // code we generate if ( FileIO.DEBUGGING ) { sb.append( "\n// F O R M A T\n" ); } switch ( dataType ) { case BIGEND: if ( target != FileType.RANDOM ) { vOut = "dos"; sb.append( "final DataOutputStream " ); sb.append( vOut ); sb.append( " = new DataOutputStream( " ); sb.append( vPrev ); sb.append( " );\n" ); } break; case ENCODED: case LOCALEENCODED: if ( target != FileType.RANDOM ) { // need PrintWriter to get the formatting routines. // We have a BufferedWriter, FileWriter, PrintStream ... vOut = "prw"; /* * consoles, sockets and pipes need to be flushed after * every io */ boolean autoflush = ( target == FileType.CONSOLE || target == FileType.SOCKET || target == FileType.PIPE ); sb.append( "final PrintWriter " ); sb.append( vOut ); sb.append( " = new PrintWriter( " ); assert vPrev != null : "missing vPrev"; sb.append( vPrev ); sb.append( ", " ); sb.append( autoflush ? "true" : "false" ); sb.append( " /* auto flush on println */ );\n" ); } break; case LITTLEEND: if ( target != FileType.RANDOM ) { vOut = "ledos"; sb.append( "// NOTE: LEDataOutputStream is available free from Canadian Mind Products.\n" ); sb.append( "// See http://mindprod.com/products.html#LEDATASTREAM.\n" ); sb.append( "final LEDataOutputStream " ); sb.append( vOut ); sb.append( " = new LEDataOutputStream( " ); sb.append( vPrev ); sb.append( " );\n" ); } break; case RAWBYTES: // don't convert to DataOutputStream break; case SERIALISED: vOut = "oos"; sb.append( "final ObjectOutputStream " ); sb.append( vOut ); sb.append( " = new ObjectOutputStream( " ); sb.append( vPrev ); sb.append( " );\n" ); break; case UNICODE: switch ( target ) { case CHARARRAY: case HTTP: case RANDOM: case STRING: break; case BYTEARRAY: case PIPE: case SEQUENTIAL: case SOCKET: case URL: vOut = "dos"; sb.append( "final DataOutputStream " ); sb.append( vOut ); sb.append( " = new DataOutputStream( " ); sb.append( vPrev ); sb.append( " );\n" ); break; case CONSOLE: assert false : "attempt to write 16-bit Unicode to console"; break; case RESOURCE: default: // can't write assert false : "attempt to write 16-bit Unicode to resource"; break; } // end switch break; default: assert false : "no case for datatype " + dataType; } // end switch return sb.toString(); } // end howFormatOutput /** * generate code for any import statements needed. * * @return generated Java source. */ private static String howImport() { // generate as comments since they go at the head, and // should not be repeated. final FastCat sb = new FastCat( 5 ); if ( FileIO.DEBUGGING ) { sb.append( "\n// I M P O R T\n" ); } sb.append( "// import java.io.*;\n" ); if ( target == FileType.URL || target == FileType.HTTP || target == FileType.SOCKET ) { sb.append( "// import java.net.*;\n" ); } if ( isCompressed ) { sb.append( "// import java.util.zip.*;\n" ); } if ( dataType == DataType.LITTLEEND ) { sb.append( "// import static java.lang.System.err; import static java.lang.System.*; import com.mindprod" + ".ledatastream.*;\n" ); } return sb.toString(); } // end howImport /** * Generates Java code to open a file for input. Should leave vIn describing some sort of InputStream. * * @return generated Java source. */ private static String howOpenInput() { // on entrance vIn and vPrev have nothing special // on exit they both point to the InputStream. // what to output to open the file final FastCat sb = new FastCat( 100 ); sb.append( "\n// O P E N\n" ); switch ( target ) { case BYTEARRAY: vIn = "bais"; sb.append( "final byte[] bai = new byte[1000];\n" ); sb.append( "// ... code to fill bai with data to be read ...\n" ); sb.append( "final ByteArrayInputStream " ); sb.append( vIn ); sb.append( " = new ByteArrayInputStream( bai );\n" ); break; case CHARARRAY: // no corresponding stream possible break; case CONSOLE: vIn = "System.in"; break; case HTTP: // CGI GET, very similar to http:// url // because this is so complicated, we handle // HTTP URL GET OPEN, WRITE, READ, CLOSE all in one // place. sb.append( "// Generate a HTTP CGI GET command\n" ); sb.append( "// prepare parm=value pairs data for GET.\n" ); sb.append( "final String parmString = \"flavour=\" + URLEncoder.encode( \"peaches & cream\", " + "\"UTF-8\" ) + \"&\" +\n" ); sb.append( " \"scoops=\" + URLEncoder.encode( \"2\", \"UTF-8\" );\n" ); sb.append( "// For GET, glue parms on tail end of URL\n" ); sb.append( "final URL url = new URL( " ); sb.append( GET_CGI_URL ); sb.append( " + \"?\" + parmString );\n" ); sb.append( "// Beware! Netscape 4 creates a netscape.net.URLConnection instead of a HttpURLConnection" + ".\n" ); sb.append( "// This code will not work under Netscape 4 without the Java plug-in.\n" ); sb.append( "final HttpURLConnection " ); sb.append( "urlc" ); sb.append( " = (HttpURLConnection)url.openConnection();\n" ); sb.append( "// Beware! HttpURLConnection is implemented with browser-specific code, often buggy.\n" ); sb.append( "// Consider using Apache HTTPClient instead." ); sb.append( "// GET or HEAD (like GET but just headers).\n" ); sb.append( "urlc.setRequestMethod(\"GET\");\n" ); sb.append( "// Identify as Firefox 29.0\n" ); sb.append( "final String userAgent = \"" ); sb.append( Build.USER_AGENT ); sb.append( "\";\n" ); sb.append( "urlc" ); sb.append( ".setRequestProperty(\"User-Agent\", userAgent );\n" ); sb.append( "// optionally turn off keep-alive\n" ); sb.append( "urlc" ); sb.append( ".setRequestProperty( \"Connection\", \"close\" );\n" ); sb.append( "urlc.setAllowUserInteraction( false );\n" ); sb.append( "urlc.setDoInput( true );\n" ); sb.append( "urlc.setDoOutput( false );\n" ); sb.append( "urlc.setUseCaches( false );\n" ); sb.append( "urlc" ); sb.append( ".setConnectTimeout( TimeUnit.SECONDS.toMillis( 50 ) ); // JDK 1.5+ only\n" ); sb.append( "urlc" ); sb.append( ".setReadTimeout( TimeUnit.SECONDS.toMillis( 40 ) ); // JDK 1.5+ only\n" ); sb.append( "// MIME types desired in response.\n" ); sb.append( "urlc.setRequestProperty( \"Accept\",\n" ); sb.append( " \"text/html, image/png, image/jpeg, \"\n" ); sb.append( " + \"image/gif,\"\n" ); sb.append( " + \" text/plain,\"\n" ); sb.append( " + \"*; q=.2, */*; q=.2\" );\n" ); sb.append( "// Make the physical connection.\n" ); sb.append( "urlc.connect();\n" ); sb.append( "\n// O P E N for read\n" ); sb.append( "// getResponseCode will block until the server responds.\n" ); sb.append( "final int statusCode = urlc.getResponseCode();\n" ); sb.append( "int length = urlc.getContentLength(); // -1 if not available\n" ); sb.append( "if ( length < 0 )\n" ); sb.append( " {\n" ); sb.append( " length = 32*1024;\n" ); sb.append( " }\n" ); vIn = "is"; sb.append( "final InputStream " ); sb.append( vIn ); sb.append( " = urlc.getInputStream();\n" ); // invoke now before possibly converted to Reader, needs basic stream. sb.append( invokeReadBytesBlocking() ); break; // end HTTP case PIPE: vIn = "pis"; sb.append( "final PipedInputStream " ); sb.append( vIn ); sb.append( " = new PipedInputStream();\n" ); sb.append( "// " ); sb.append( vIn ); sb.append( ".connect( /* PipedOutputStream */ sender );\n" ); sb.append( "// connect() must be done by either sender or receiver but not both.\n" ); break; case RANDOM: if ( dataType == DataType.LITTLEEND ) { vIn = "leraf"; sb.append( "// NOTE: LERandomAccessFile is available free from Canadian Mind Products.\n" ); sb.append( "// See http://mindprod.com/products.html#LEDASTREAM.\n" ); sb.append( "final LERandomAccessFile " ); sb.append( vIn ); sb.append( " = new LERandomAccessFile( " ); sb.append( TEMP_RAND ); sb.append( ", \"r\" /* read only */ );\n" ); } else { vIn = "raf"; sb.append( "final RandomAccessFile " ); sb.append( vIn ); sb.append( " = new RandomAccessFile( " ); sb.append( TEMP_RAND ); sb.append( ", \"r\" /* read only */ );\n" ); } break; case RESOURCE: vIn = "is"; sb.append( "final InputStream " ); sb.append( vIn ); sb.append( " = this.getClass().getResourceAsStream( \"someresource.txt\" );\n" ); break; case SEQUENTIAL: if ( ( dataType == DataType.LOCALEENCODED ) && !isCompressed ) { vIn = "fr"; sb.append( "final FileReader " ); sb.append( vIn ); sb.append( " = new FileReader( " ); sb.append( TEMP_IN ); sb.append( " );\n" ); readerCreated = true; } else { vIn = "fis"; sb.append( "final FileInputStream " ); sb.append( vIn ); sb.append( " = new FileInputStream( " ); sb.append( TEMP_IN ); sb.append( " );\n" ); } break; case SOCKET: sb.append( "final Socket socket = new Socket( \"mail.abc.com/\" /* server */ ," + "110 /* socket # */ );\n" ); vIn = "is"; sb.append( "final InputStream " ); sb.append( vIn ); sb.append( " = socket.getInputStream();\n" ); break; // end SOCKET case STRING: // no InputStream possible; break; case URL: // open http: style URL for input, similar to code for http CGI // get sb.append( "// Generate an URL read Command\n" ); sb.append( "final URL " ); sb.append( "url = new URL( " ); sb.append( GET_URL ); sb.append( " );\n" ); sb.append( "// if you want to analyse the URL\n" ); sb.append( " final String protocol = url.getProtocol(); // http\n" ); sb.append( " final String host = url.getHost(); // www.billabong.com\n" ); sb.append( " final String file = url.getFile(); // lyrics.txt\n" ); sb.append( " final int port = url.getPort();// 80\n" ); sb.append( "final URLConnection urlc = url.openConnection();\n" ); sb.append( "// optionally turn off keep-alive\n" ); sb.append( "urlc.setRequestProperty( \"Connection\", \"close\" );\n" ); sb.append( "urlc.setAllowUserInteraction( false );\n" ); sb.append( "urlc.setDoInput( true );\n" ); sb.append( "urlc.setDoOutput( false );\n" ); sb.append( "urlc.setUseCaches( false );\n" ); sb.append( "urlc.setConnectTimeout( TimeUnit.SECONDS.toMillis( 50 )); // JDK 1.5+ only\n" ); sb.append( "urlc.setReadTimeout( TimeUnit.SECONDS.toMillis( 40 ) ); // JDK 1.5+ only\n" ); sb.append( "// MIME types desired in response.\n" ); sb.append( "urlc.setRequestProperty( \"Accept\",\n" ); sb.append( " \"text/html, image/png, image/jpeg, \"\n" ); sb.append( " + \"image/gif, application/x-java-serialized-object, \"\n" ); sb.append( " + \"text/x-java-source, text/xml, application/xml, \"\n" ); sb.append( " + \"text/css, application/x-java-jnlp-file,\"\n" ); sb.append( " + \" text/plain, application/zip,\"\n" ); sb.append( " + \" application/octet-stream, \"\n" ); sb.append( " + \"*; q=.2, */*; q=.2\" );\n" ); sb.append( "// Make the physical connection.\n" ); sb.append( "urlc.connect();\n" ); sb.append( "final int length = urlc.getContentLength(); // -1 if not available\n" ); vIn = "is"; sb.append( "final InputStream " ); sb.append( vIn ); sb.append( " = urlc.getInputStream();\n" ); break; // end URL default: assert false : "no case for target " + target; } // end switch return sb.toString(); } // end howOpenInput /** * Generates Java code to open a file for output. Should leave vOut describing some sort of OutputStream. * * @return generated Java source. */ private static String howOpenOutput() { final FastCat sb = new FastCat( 100 ); // what to output to open the file sb.append( "\n// O P E N\n" ); switch ( target ) { case BYTEARRAY: vOut = "baos"; sb.append( "final ByteArrayOutputStream " ); sb.append( vOut ); sb.append( " = new ByteArrayOutputStream( 1000 );\n" ); break; case CHARARRAY: // no such thing an CharArrayStream break; case CONSOLE: vOut = "out"; // convert to PrintWriter later break; case RANDOM: if ( dataType == DataType.LITTLEEND ) { vOut = "leraf"; sb.append( "// NOTE: LERandomAccessFile is available free from Canadian Mind Products.\n" ); sb.append( "// See http://mindprod.com/products.html#LEDATASTREAM.\n" ); sb.append( "final LERandomAccessFile " ); sb.append( vOut ); sb.append( " = new LERandomAccessFile( " ); sb.append( TEMP_RAND ); sb.append( ", \"rw\" /* read/write */ );\n" ); } else { vOut = "raf"; sb.append( "final RandomAccessFile " ); sb.append( vOut ); sb.append( " = new RandomAccessFile( " ); sb.append( TEMP_RAND ); sb.append( ", \"rw\" /* read/write */ );\n" ); } break; case PIPE: vOut = "pos"; sb.append( "final PipedOutputStream " ); sb.append( vOut ); sb.append( " = new PipedOutputStream();\n" ); sb.append( "// " ); sb.append( vOut ); sb.append( ".connect( /* PipedInputStream */ receiver );\n" ); sb.append( "// connect() must be done by either sender or receiver but not both.\n" ); break; case HTTP: vOut = "url"; sb.append( "// Generate a HTTP CGI POST command\n" ); sb.append( "final URL " ); sb.append( vOut ); sb.append( " = new URL( " ); sb.append( POST_CGI_URL ); sb.append( " );\n" ); sb.append( "// Beware! Netscape 4 creates a netscape.net.URLConnection instead of a HttpURLConnection" + ".\n" ); sb.append( "// This code will not work under Netscape 4 without the Java plug-in.\n" ); sb.append( "final HttpURLConnection " ); sb.append( "urlc" ); sb.append( " = (HttpURLConnection)url.openConnection();\n" ); sb.append( "// Beware! HttpURLConnection is implemented with browser-specific code, often buggy.\n" ); sb.append( "// Consider using Apache HTTPClient instead." ); sb.append( "urlc.setRequestMethod( \"POST\" );\n" ); sb.append( "// optionally turn off keep-alive\n" ); sb.append( "urlc" ); sb.append( ".setRequestProperty( \"Connection\", \"close\" );\n" ); sb.append( "urlc.setAllowUserInteraction( false );\n" ); sb.append( "urlc.setDoInput( true );\n" ); sb.append( "urlc.setDoOutput( true );\n" ); sb.append( "urlc.setUseCaches( false );\n" ); sb.append( "urlc" ); sb.append( ".setRequestProperty( \"Content-type\", \"application/x-www-form-urlencoded\" );\n" ); sb.append( "// prepare parm=value pairs data for POST.\n" ); sb.append( "final String postString = \"flavour=\" + URLEncoder.encode( \"peaches & cream\", " + "\"UTF-8\" ) + \"&\" +\n" ); sb.append( " \"scoops=\" + URLEncoder.encode( \"2\", \"UTF-8\" );\n" ); sb.append( "urlc" ); sb.append( ".setRequestProperty( \"Content-length\", ( Integer.toString( postString.length() ) ) );" + "\n" ); sb.append( "urlc" ); sb.append( ".setConnectTimeout( TimeUnit.SECONDS.toMillis( 50 ) ); // JDK 1.5+ only\n" ); sb.append( "urlc" ); sb.append( ".setReadTimeout( TimeUnit.SECONDS.toMillis( 40 ) ); // JDK 1.5+ only\n" ); sb.append( "// MIME types desired in response.\n" ); sb.append( "urlc.setRequestProperty( \"Accept\",\n" ); sb.append( " \"text/html, image/png, image/jpeg, \"\n" ); sb.append( " + \"image/gif,\"\n" ); sb.append( " + \" text/plain,\"\n" ); sb.append( " + \"*; q=.2, */*; q=.2\" );\n" ); sb.append( "// Make the physical connection.\n" ); sb.append( "urlc.connect();\n" ); sb.append( "\n// O P E N for write\n" ); vOut = "os"; sb.append( "final OutputStream " ); sb.append( vOut ); sb.append( " = urlc.getOutputStream();\n" ); sb.append( "\n// W R I T E\n" ); sb.append( "// getResponseCode will block until the server responds.\n" ); sb.append( "urlc.getResponseCode();\n" ); sb.append( "int length = urlc.getContentLength(); // -1 if not available\n" ); sb.append( "if ( length < 0 )\n" ); sb.append( " {\n" ); sb.append( " length = 32*1024;\n" ); sb.append( " }\n" ); sb.append( "\n// O P E N for read\n" ); vIn = "is"; sb.append( "final InputStream " ); sb.append( vIn ); sb.append( " = urlc.getInputStream();\n" ); // invoke now before possibly converted to Reader, needs basic stream. sb.append( invokeReadBytesBlocking() ); break; // end HTTP case URL:// write url sb.append( "final URL " ); sb.append( "url" ); sb.append( " = new URL( " ); sb.append( POST_URL ); sb.append( " );\n" ); sb.append( "final URLConnection " ); sb.append( "urlc = url.openConnection();\n" ); sb.append( "urlc.setAllowUserInteraction( false );\n" ); sb.append( "urlc.setDoInput( false ); // if true, could read back results\n" ); sb.append( "urlc.setDoOutput( true );\n" ); sb.append( "urlc.setUseCaches( false );\n" ); sb.append( "urlc.setConnectTimeout( TimeUnit.SECONDS.toMillis( 50 ) ); // JDK 1.5+ only\n" ); sb.append( "// Make the physical connection.\n" ); sb.append( "urlc.connect();\n" ); vOut = "os"; sb.append( "final OutputStream " ); sb.append( vOut ); sb.append( " = urlc.getOutputStream();\n" ); break; // end URL case SOCKET: sb.append( "final Socket socket = new Socket( \"mail.abc.com/\" /* server */ ," + "110 /* socket # */ );\n" ); vOut = "os"; sb.append( "final OutputStream " ); sb.append( vOut ); sb.append( " = socket.getOutputStream();\n" ); break; // end SOCKET case SEQUENTIAL: if ( ( dataType == DataType.LOCALEENCODED ) && !isCompressed ) { vOut = "fw"; sb.append( "final FileWriter " ); sb.append( vOut ); sb.append( " = new FileWriter( " ); sb.append( TEMP_OUT ); sb.append( ", false /* append */ );\n" ); writerCreated = true; } else { // need OutputStream need compress or locale encode. vOut = "fos"; sb.append( "final FileOutputStream " ); sb.append( vOut ); sb.append( " = new FileOutputStream( " ); sb.append( TEMP_OUT ); sb.append( ", false /* append */ );\n" ); } break; case RESOURCE: // can't write break; case STRING: // no such thing as StringOutputStream break; default: assert false : "no case for datatype " + dataType; } // end switch return sb.toString(); } // end howOpenOutput /** * generates Java code to read a file. * * @return generated Java source. */ private static String howRead() { final FastCat sb = new FastCat( 100 ); sb.append( "\n// R E A D\n" ); if ( target == FileType.RANDOM ) { sb.append( vIn ); sb.append( ".seek( 0 /* byte offset in file */ );\n" ); } switch ( dataType ) { case UNICODE: switch ( target ) { case STRING: case CHARARRAY: sb.append( "final char c = ( char ) " ); sb.append( vIn ); sb.append( ".read();\n" ); sb.append( "/* there is no readChars method!*/\n" ); break; case BYTEARRAY: case RESOURCE: case SEQUENTIAL: case RANDOM: case PIPE: case URL: case SOCKET: sb.append( "final char c = " ); sb.append( vIn ); sb.append( ".readChar();\n" ); sb.append( "/* there is no readChars method */\n" ); break; case HTTP: break; case CONSOLE: assert false : "attempt to read 16-bit unicode from target " + target; break; default: assert false : "no case for target " + target; } // end inner switch break; case LOCALEENCODED: case ENCODED: if ( target == FileType.RANDOM ) { sb.append( "final byte b = " ); sb.append( vIn ); sb.append( ".readByte();\n" ); sb.append( "// File being read had better have a terminal \\n!\n" ); sb.append( "final String line = " ); sb.append( vIn ); sb.append( ".readLine();\n" ); } else { sb.append( "final char[] ca = new char[1024];\n" ); sb.append( "// -1 means eof.\n" ); sb.append( "// You don't necessarily get all you ask for in one read.\n" ); sb.append( "// You get what's immediately available.\n" ); sb.append( "final int charsRead = " ); sb.append( vIn ); sb.append( ".read( ca );\n" ); if ( isBuffered ) { // readLine not available in Readers, only BufferedReaders sb.append( "// File being read need not have have a terminal \\n.\n" ); sb.append( "// File being read may safely use any mixture of \\r\\n, " + "\\r or \\n line terminators.\n" ); sb.append( "final String line = " ); sb.append( vIn ); sb.append( ".readLine();\n// line == null means EOF\n" ); sb.append( "final int aChar = " ); sb.append( vIn ); sb.append( ".read();\n// aChar == -1 means EOF\n" ); } else { // readLine not available in Readers, only // BufferedReaders sb.append( "// " ); sb.append( vIn ); sb.append( ".readLine() not available without BufferedReader.\n" ); } } break; case RAWBYTES: sb.append( "final byte[] ba = new byte[1024];\n" ); sb.append( "// -1 means eof.\n" ); sb.append( "// You don't necessarily get all you ask for in one read.\n" ); sb.append( "// You get what's immediately available.\n" ); sb.append( "final int bytesRead = " ); sb.append( vIn ); sb.append( ".read( ba, 0 /* offset in ba */, ba.length /* bytes to read */ );\n" ); break; case BIGEND: case LITTLEEND: sb.append( "final boolean q = " ); sb.append( vIn ); sb.append( ".readBoolean();\n" ); sb.append( "final byte b = " ); sb.append( vIn ); sb.append( ".readByte();\n" ); sb.append( "final byte ba[] = new byte[1024];\n" ); sb.append( "// -1 means eof.\n" ); sb.append( "// You don't necessarily get all you ask for in one read.\n" ); sb.append( "// You get what's immediately available.\n" ); sb.append( "final int bytesRead = " ); sb.append( vIn ); sb.append( ".read( ba, 0 /* offset in ba */, ba.length /* bytes to read */ );\n" ); sb.append( "final char c = " ); sb.append( vIn ); sb.append( ".readChar();\n" ); sb.append( "// There is no readChars method\n" ); sb.append( "final double d = " ); sb.append( vIn ); sb.append( ".readDouble();\n" ); sb.append( "final float f = " ); sb.append( vIn ); sb.append( ".readFloat();\n" ); sb.append( "final int j = " ); sb.append( vIn ); sb.append( ".readInt();\n" ); sb.append( "final long l = " ); sb.append( vIn ); sb.append( ".readLong();\n" ); sb.append( "final short s = " ); sb.append( vIn ); sb.append( ".readShort();\n" ); sb.append( "// Do not use readUTF to read Unicode files.\n" ); sb.append( "// readUTF reads binary counted Strings created by writeUTF.\n" ); sb.append( "// The lead 16-bit byte count and UTF-8 encoding means Strings must be <= 10," + "922 chars!!\n" ); sb.append( "// See http://mindprod.com/jgloss/utf.html#WRITEUTF for details." ); sb.append( "// Use Reader.read with an explicit UTF-8 or UTF-16 encoding instead.\n" ); sb.append( "final String u = " ); sb.append( vIn ); sb.append( ".readUTF();\n" ); sb.append( "final byte ub = (byte)" ); sb.append( vIn ); sb.append( ".readUnsignedByte();\n" ); sb.append( "final short us = (short)" ); sb.append( vIn ); sb.append( ".readUnsignedShort();\n" ); if ( target == FileType.RANDOM ) { // not deprecated because there is no random access Reader sb.append( "// File being read had better have a terminal \\n!\n" ); sb.append( "final String line = " ); sb.append( vIn ); sb.append( ".readLine();\n// line == null means EOF\n" ); } // otherwise is deprecated, so we don't show it. break; case SERIALISED: sb.append( "final Object o = " ); sb.append( vIn ); sb.append( ".readObject();\n" ); break; default: assert false : "no case for datatype " + dataType; } // end switch return sb.toString(); } // end howRead /** * Mother of all "how" methods. Generate code for the current settings of sourceTarget, dataType, buffering, * doingInput, doingOutput. * * @return a long string of Java source code to do that combination of options. */ private static String howTo() { // avoid cross contamination. vIn = null; vOut = null; readerCreated = false; writerCreated = false; String problem = ensureSupported(); if ( problem != null ) { final FastCat sb = new FastCat( 2 ); if ( doingInput ) { sb.append( howCommentInput() ); } else { sb.append( howCommentOutput() ); } sb.append( problem ); return sb.toString(); } if ( doingInput ) { final FastCat sb = new FastCat( 11 ); sb.append( howCommentInput() ); sb.append( howCaveatInput() ); sb.append( howImport() ); sb.append( howOpenInput() ); sb.append( howCompressInput() ); sb.append( howAuxBufferInput() ); sb.append( howDecodeInput() ); sb.append( howBufferInput() ); sb.append( howFormatInput() ); sb.append( howRead() ); sb.append( howCloseInput() ); return sb.toString(); } else { if ( target == FileType.HTTP ) { // HTTP POST is treated sometimes an output and sometimes as input. final FastCat sb = new FastCat( 11 ); sb.append( howCommentOutput() ); sb.append( howCaveatOutput() ); sb.append( howImport() ); sb.append( howOpenOutput() ); sb.append( howCompressInput() ); sb.append( howAuxBufferInput() ); sb.append( howDecodeInput() ); sb.append( howBufferInput() ); sb.append( howFormatInput() ); sb.append( howRead() ); sb.append( howCloseInput() ); return sb.toString(); } else { final FastCat sb = new FastCat( 12 ); sb.append( howCommentOutput() ); sb.append( howCaveatOutput() ); sb.append( howImport() ); sb.append( howOpenOutput() ); sb.append( howCompressOutput() ); sb.append( howAuxBufferOutput() ); sb.append( howEncodeOutput() ); sb.append( howBufferOutput() ); sb.append( howFormatOutput() ); sb.append( howWrite() ); sb.append( howFlush() ); sb.append( howCloseOutput() ); return sb.toString(); } } } // end howTo /** * Generates Java code to write a file. * * @return generated Java source. */ private static String howWrite() { final FastCat sb = new FastCat( 40 ); sb.append( "\n// W R I T E\n" ); if ( target == FileType.RANDOM ) { sb.append( vOut ); sb.append( ".seek( 0 /* byte offset in file*/ );\n" ); } switch ( dataType ) { case UNICODE: switch ( target ) { case BYTEARRAY: sb.append( vOut ); sb.append( ".writeChar( \'x\' );\n" ); sb.append( vOut ); sb.append( ".writeChars( \"cassowary\" );\n" ); break; case STRING: sb.append( vOut ); sb.append( ".write( \'x\' );\n" ); sb.append( vOut ); sb.append( ".write( \"Tasmanian devil\" );\n" ); break; case CHARARRAY: sb.append( vOut ); sb.append( ".write( \'x\' );\n" ); sb.append( vOut ); sb.append( ".write( \"wombat\" );\n" ); break; case RESOURCE: // can't write break; case SEQUENTIAL: case RANDOM: case PIPE: case SOCKET: case CONSOLE: default: sb.append( vOut ); sb.append( ".writeChar( \'x\' );\n" ); sb.append( vOut ); sb.append( ".writeChars( \"dolphin\" );\n" ); break; } break; case LOCALEENCODED: case ENCODED: if ( target == FileType.RANDOM ) { sb.append( vOut ); sb.append( ".writeByte( (byte)44 );\n" ); sb.append( vOut ); sb.append( ".writeBytes( \"kangaroo\" /* string -> LSB 8-bit */ );\n" ); } else { sb.append( vOut ); sb.append( ".write( \"platypus\" );\n" ); sb.append( vOut ); sb.append( ".println( 149 );\n" ); } break; case RAWBYTES: sb.append( "final byte[] ba = new byte[10000];\n" ); sb.append( vOut ); sb.append( ".write( ba, 0 /* offset in ba */, ba.length /* bytes to write */ );\n" ); break; case BIGEND: case LITTLEEND: sb.append( vOut ); sb.append( ".writeBoolean( true );\n" ); sb.append( vOut ); sb.append( ".writeByte( (byte)44 );\n" ); sb.append( vOut ); sb.append( ".writeBytes( \"kangaroo\" /* string -> LSB 8-bit */ );\n" ); sb.append( vOut ); sb.append( ".writeChar( 'a' );\n" ); sb.append( vOut ); sb.append( ".writeChars( \"dingo\" /* string 16-bit Unicode */ );\n" ); sb.append( vOut ); sb.append( ".writeDouble( 3.14D );\n" ); sb.append( vOut ); sb.append( ".writeFloat( 3.14F );\n" ); sb.append( vOut ); sb.append( ".writeInt( 149 ); // beware write( int ) writes a byte\n" ); sb.append( vOut ); sb.append( ".writeLong( 149L );\n" ); sb.append( vOut ); sb.append( ".writeShort( (short)149 );\n" ); sb.append( "// Do not use writeUTF to create Unicode files.\n" ); sb.append( "// writeUTF creates binary files with counted Strings.\n" ); sb.append( "// The lead 16-bit byte count and UTF-8 encoding means Strings must be <= 10," + "922 chars!!\n" ); sb.append( "// See http://mindprod.com/jgloss/utf.html#WRITEUTF for details." ); sb.append( "// Use Writer.write with explicit UTF-16 or UTF-8 encoding instead.\n" ); sb.append( vOut ); sb.append( ".writeUTF( \"echidna\" );\n" ); if ( target == FileType.PIPE ) { sb.append( "// Note: You can't send raw objects down a pipe. They need to be serialised.\n" ); } else { sb.append( "// Note: You can't write raw objects, they need to be serialised.\n" ); } break; case SERIALISED: sb.append( "// object to write, and objects it points to must implement java.io.Serializable\n" ); sb.append( "// Ideally such objects should also have have a field:\n" ); sb.append( "// static final long serialVersionUID = 1L;\n" ); sb.append( "// to prevent spurious InvalidClassExceptions.\n" ); sb.append( "final Object o = this;\n" ); sb.append( vOut ); sb.append( ".writeObject( o );\n" ); break; default: assert false : "no case for datatype " + dataType; } // end switch return sb.toString(); } // end howWrite /** * use readBytesBlocking to read from a stream that may be intermittent. * * @return Java source to invoke readBytesBlocking. */ private static String invokeReadBytesBlocking() { final FastCat sb = new FastCat( 10 ); sb.append( "// Reads from a communications channel may fail to give you as much data as you asked for.\n" ); sb.append( "// You get what is available. readBytesBlocking waits until all the data you requested (or eof)" + ".\n" ); sb.append( "// See readBytesBlocking in the Java glossary http;//mindprod.com/jgloss/http.html for the " + "source\n" ); sb.append( "final byte all[] = new byte[1000];\n" ); sb.append( "final int byteCount = readBytesBlocking( " ); sb.append( vIn ); sb.append( ", all, 0 /* offset */, all.length );\n" ); return sb.toString(); } /** * is this a character stream data type ? * * @param dataType type of the project */ static boolean isACharStream( DataType dataType ) { switch ( dataType ) { case LOCALEENCODED: case ENCODED: case UNICODE: return true; case RAWBYTES: case BIGEND: case LITTLEEND: case SERIALISED: return false; default: throw new IllegalArgumentException( "Bug: invalid dataType" ); } } /** * Generate standard caveats that apply to every code snippet. * * @return Java Source text of warnings. */ private static String standardCaveats() { final FastCat sb = new FastCat( 5 ); sb.append( "\n// C A V E A T S\n" ); sb.append( "// WARNING! Code to handle IOExceptions is not shown.\n" ); sb.append( "// WARNING! This code is intended for teaching. In practice you would telescope some of the steps" + ".\n" ); return sb.toString(); } /** *

* Generate some sample Java source code for a requested I/O function. * * @param pSourceOrTarget what sort of file to do the I/O on, for example BYTEARRAY, SEQUENTIAL, URL, SOCKET. * @param pDataType what sort of data is in the file, for example: RAWBYTES, UNICODE, BIGEND, LITTLEEND. * @param pDoingInput true if reading, false if writing. * @param pIsBuffered true if doing buffered i/o. * @param pIsCompressed true if using a compressed stream. * * @return string containing Java source code. * @noinspection MethodNamesDifferingOnlyByCase */ public static String how( FileType pSourceOrTarget, DataType pDataType, boolean pDoingInput, boolean pIsBuffered, boolean pIsCompressed ) { target = pSourceOrTarget; dataType = pDataType; doingInput = pDoingInput; doingOutput = !pDoingInput; isBuffered = pIsBuffered; isCompressed = pIsCompressed; return howTo(); } // end how }