/* * This file is part of MoleculeViewer. * * MoleculeViewer is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * MoleculeViewer is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with MoleculeViewer. If not, see <http://www.gnu.org/licenses/>. */ package astex; /* Copyright Astex Technology Ltd. 1999 */ /* Copyright David Hall, Boston University, 2011 */ /* * 08-08-03 mjh * add in the sprint functionality for formatting and returning * numbers internally as a string. * 14-03-03 mjh * add rudimentary code to handle scientific notation in * floating point input numbers. * 14-02-03 pw * merge Format class in for high performance output formatting. * 02-08-02 mjh * look for .gz versions of files first, to work around * the shortcomings of microsoft web servers that cannot * handle two file extensions correctly such as .pdb.gz * 13-08-02 mjh * add internal field splitting to avoid overhead of String * creation with the old method. To get the fields, you had * to get the current line as a string and then tokenise that * to give you an array of strings. The current method should * be much faster. * 24-01-99 mjh * fix serious bug in fill() that caused infinite loop with Mac * style line endings (\r). * 23-12-99 mjh * add method to load a properties file * 22-12-99 mjh * FILE.open() will now try and load from a jar file if one is * present. Netscape is ridiculously fussy about what you can * load from a jar file. lib, dat and many other extensions are * expressly forbidden when running as an applet. properties is just * about the only one that works. * 30-11-99 mjh * make FILE.open() determine if the resource is a file or URL * and open it appropriately. make open(URL) use URLConnection's * so that it can take advantage of cached files. * 11-11-99 mjh * work around fact that java bytes are signed, but ascii isn't * i.e. read() returns b & 255. Didn't cause problems with regular * text files but hoses binary files e.g. maps. No particular impact * on performance * 29-10-99 mjh * removed code for original implementation of unget buffer * 28-10-99 mjh * created */ import java.io.*; import java.util.*; import java.util.zip.*; import java.net.*; /** * A class which implements a high performance byte based input * stream. * * Buffering is performed internally. This class is NOT thread safe. */ public class FILE extends InputStream { /** * Input and output member variables. */ /** The size of the input buffer we will use. */ private static final int bufferSize = 2048; /** The buffer into which we will read the input. */ private byte buffer[] = new byte[bufferSize + 1]; /** The number of characters currently in the buffer. */ private int charactersInBuffer = 1; /** * The last exception that was thrown by * a FILE.open() call or FILE.write(). */ private static Exception exception = null; /** * Input specific variable. */ /** The InputStream that this object will read from. */ private InputStream inputStream = null; /** The next character from the buffer that we will return. */ private int nextCharacter = 1; /** The constant that represents the end of file. */ public static final int EOF = -1; /** Have fields been determined for the current line. */ private boolean fieldsDetermined = false; /** The number of fields on the current line. */ private int fieldCount = 0; /** The maximum number of fields we can have. */ private static int MaxFields = 1024; /** The start position of each field on the current line. */ private int fieldStartPosition[] = new int[MaxFields]; /** The stop position of each field on the current line. */ private int fieldLength[] = new int[MaxFields]; /** The document base if there is one. */ private static URL documentBase = null; /** The code base if there is one. */ private static URL codeBase = null; /** Should we output debug information. */ private static boolean debug = false; /** Should we try and open resources from files. */ private static boolean tryFiles = true; /** The length of the buffer used for storing a line of input. */ private static int lineBufferSize = 1024; /** A buffer for storing the current line in the input. */ private byte lineBuffer[] = new byte[lineBufferSize]; /** The length of the current line. */ private int lineLength = 0; /** * Output specific variables. */ /** Wrapper allowing write to stdout and stderr. */ public static final FILE out = new FILE(System.out); public static FILE spr = new FILE(); /** Output stuff. */ private OutputStream outputStream = null; /** eol identifier. */ private byte eol0 = (byte)'\n'; private byte eol1 = 0; private int eolLength = 1; public static final int UNIX = 0; public static final int PC = 1; public static final int MAC = 2; /* Variables for formatting. */ private int width = 0; private int precision = -1; private byte pre[] = new byte[bufferSize]; private byte post[] = new byte[bufferSize]; private int preCount = 0; private int postCount = 0; private boolean leading_zeroes = false; private boolean show_plus = false; private boolean alternate = false; private boolean show_space = false; private boolean left_align = false; private char fmt = ' '; /** Temporary byte buffer. */ private byte tb[] = new byte[2048]; /** Number of bytes in temporary byte buffer. */ private int ntb = 0; private int ntbStart = 0; private int ntbE = 0; /** * autoflush variable. If output is stderr * or stdout is set true else false. */ private boolean autoFlush = false; /** Arrays of bytes for different ouput types. */ private static final byte baseo[] = {(byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', (byte)'6', (byte)'7'}; private static final byte basex[] = {(byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f'}; private static final byte baseX[] = {(byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F'}; private static final byte based[] = {(byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', (byte)'6', (byte)'7', (byte)'8', (byte)'9'}; /** Load of constants for character bytes. */ private static final byte bSpace = (byte)' '; private static final byte bPlus = (byte)'+'; private static final byte bMinus = (byte)'-'; private static final byte b0 = (byte)'0'; private static final byte bx = (byte)'x'; private static final byte bX = (byte)'X'; private static final byte bDot = (byte)'.'; private static final byte be = (byte)'e'; private static final byte bE = (byte)'E'; /** Stores the previous format. */ private String prevFormat = null; /** sets the autoFlush flag. */ public void setautoFlush(boolean f){ autoFlush = f; } /** Print a char. */ public void print(char c){ flushIfFull(1); buffer[charactersInBuffer++] = (byte)c; } /** Quick print function for floats/doubles. */ public void print(int w, int p, double d){ width = w; precision = p; leading_zeroes = false; show_plus = false; alternate = false; show_space = false; left_align = false; fmt = 'f'; preCount = 0; postCount = 0; format(d); copyAll(); } /** Quick print function for ints/long */ public void print(int w, long d){ width = w; precision = -1; leading_zeroes = false; show_plus = false; alternate = false; show_space = false; left_align = false; fmt = 'd'; preCount = 0; postCount = 0; format(d); copyAll(); } /** Print function for 3 floats/doubles. */ public void print(String f, double d1, double d2, double d3){ /* Parse the format string. */ if( prevFormat == null ? f != null : !prevFormat.equals(f)){ parseFormat(f); f.intern(); prevFormat = f; } format(d1); copyAll(); format(d2); copyAll(); format(d3); copyAll(); } /** Print function for 3 ints/longs. */ public void print(String f, long d1, long d2, long d3){ /* Parse the format string. */ if( prevFormat == null ? f != null : !prevFormat.equals(f)){ parseFormat(f); f.intern(); prevFormat = f; } format(d1); copyAll(); format(d2); copyAll(); format(d3); copyAll(); } /** print function. No formatting. */ public void print(String s){ int len = s.length(); byte b = 0; flushIfFull(len); for(int i = 0; i < len; i++){ b = (byte)s.charAt(i); buffer[charactersInBuffer++] = b; } if(autoFlush) flush(); } /** println function. No formatting adds eol. */ public void println(String s){ int len = s.length(); byte b = 0; flushIfFull(len + eolLength); for(int i = 0; i < len; i++){ b = (byte)s.charAt(i); buffer[charactersInBuffer++] = b; } eol(); if(autoFlush) flush(); } /** Print a formatted String. */ public void print(String f, String s){ if( prevFormat == null ? f != null : !prevFormat.equals(f)){ parseFormat(f); f.intern(); prevFormat = f; } format(s); if(autoFlush) flush(); } /** Print a formatted char. */ public void print(String f, char c){ if( prevFormat == null ? f != null : !prevFormat.equals(f)){ parseFormat(f); f.intern(); prevFormat = f; } format(c); if(autoFlush) flush(); } /** Print a formatted integer. */ public void print(String f, long d){ if( prevFormat == null ? f != null : !prevFormat.equals(f)){ parseFormat(f); f.intern(); prevFormat = f; } format(d); copyAll(); } /** Print a formatted double. */ public void print(String f, double d){ if( prevFormat == null ? f != null : !prevFormat.equals(f)){ parseFormat(f); f.intern(); prevFormat = f; } format(d); copyAll(); } /** Format the String argument. */ public void format(String s){ if (fmt != 's') throw new java.lang.IllegalArgumentException(); ntb = 0; ntbStart = 0; /* Here we can copy directly in the main buffer. */ ntbE = s.length(); //System.out.println("format preCount " + preCount); //System.out.println("format postCount " + postCount); //System.out.println("format width " + width); //System.out.println("format ntbE " + ntbE); //int n = preCount + postCount + width - ntbE; int n = preCount + postCount + width + ntbE; flushIfFull(n); preFormat(); if(!left_align){ pad(); copyString(s, ntbE); } else { copyString(s, ntbE); pad(); } postFormat(); } /** Format integer. */ public void format(long d){ int s = 0; /* sign of the number. */ /* Reinitialise counters. */ ntb = 0; ntbStart = 0; ntbE = 0; /* If format is d or i set sign value. */ if(fmt == 'd' || fmt == 'i'){ if(d < 0) s = -1; else s = 1; } if(fmt == 'd' || fmt == 'i') convert(d, based, 10); else if (fmt == 'o') convert(d, baseo, 8); else if (fmt == 'x') convert(d, basex, 16); else if (fmt == 'X') convert(d, baseX, 16); else throw new java.lang.IllegalArgumentException(); /* Add the sign character bytes. */ sign(s); } /** Format double. */ public void format(double x) { byte localtb[] = tb; /* Reinitialise counters. */ ntb = 0; ntbStart = 0; ntbE = 0; int s = 1; /* Set sign to positive. */ /* Handle + and - Infinity. */ if(Double.isInfinite(x)){ if(x == Double.NEGATIVE_INFINITY) s = -1; else s = 1; localtb[ntb++] = (byte)'f'; localtb[ntb++] = (byte)'n'; localtb[ntb++] = (byte)'I'; ntbStart = ntbE; sign(s); return; } /* Handle Nan. */ if(Double.isNaN(x)){ localtb[ntb++] = (byte)'N'; localtb[ntb++] = (byte)'a'; localtb[ntb++] = (byte)'N'; ntbStart = ntbE; return; } /* Set default precision and sign. */ if(precision < 0) precision = 6; if(x < 0){ x = -x; s = -1; } if(fmt == 'f'){ fixedFormat(x); } else if(fmt == 'e' || fmt == 'E' || fmt == 'g' || fmt == 'G'){ expFormat(x); } else throw new java.lang.IllegalArgumentException(); /* Need this here if we round to infinity. */ if(Double.isInfinite(x)){ localtb[ntb++] = (byte)'f'; localtb[ntb++] = (byte)'n'; localtb[ntb++] = (byte)'I'; ntbStart = ntbE; sign(s); return; } else { sign(s); /* Sort out the sign. */ } } /** Format the char argument. */ public void format(char c){ byte localBuffer[] = buffer; if (fmt != 'c') throw new java.lang.IllegalArgumentException(); /* Initialise counters. */ ntb = 0; ntbStart = 0; /* Here we can copy directly in the main buffer. */ ntbE = 1; int n = preCount + postCount + width - ntbE; flushIfFull(n); preFormat(); if(!left_align){ pad(); buffer[charactersInBuffer++] = (byte)c; } else { buffer[charactersInBuffer++] = (byte)c; pad(); } postFormat(); } /** Copies all data to the main buffer. */ private void copyAll(){ int n = preCount + postCount + width + (ntb - ntbStart - ntbE); /* Check limits of buffer if full, then flush. */ flushIfFull(n); preFormat(); if(!left_align){ pad(); copyNumber(); } else { copyNumber(); pad(); } postFormat(); /* If autoFlush is true. Flush the buffer. */ if(autoFlush) flush(); } /** Copies the number from the tb temporary array into the main buffer. Need to do this backwards. */ private void copyNumber(){ byte localBuffer[] = buffer; byte localtb[] = tb; for(int i = ntb-1; i >= ntbStart; i--){ localBuffer[charactersInBuffer++] = localtb[i]; } for(int i = ntbE-1; i >= 0; i--){ localBuffer[charactersInBuffer++] = localtb[i]; } } /** Copy preformat segment into main buffer. */ private void preFormat(){ byte localBuffer[] = buffer; for(int i = 0; i < preCount; i++){ localBuffer[charactersInBuffer++] = pre[i]; } } /** Copy postformat segment into main buffer. */ private void postFormat(){ byte localBuffer[] = buffer; for(int i = 0; i < postCount; i++){ localBuffer[charactersInBuffer++] = post[i]; } } /** Create padded segment in main buffer. */ private void pad(){ charactersInBuffer = repeat(bSpace, width - (ntb - ntbStart) - ntbE, buffer, charactersInBuffer); } /** Copies repeated characters to supplied buffer. */ private int repeat(byte b, int n, byte bArray[], int nb){ if(n <= 0) return nb; for (int i = 0; i < n; i++){ bArray[nb++] = b; } return nb; } /** Copies the content of a String directly to the main buffer. */ private void copyString(String s, int len){ for(int i = 0; i < len; i++){ buffer[charactersInBuffer++] = (byte)s.charAt(i); } } /** Outputs fixed format numbers. */ private void fixedFormat(double d){ byte localtb[] = tb; /* Sort out whether to remove trailing balnks. */ boolean removeTrailing = (fmt == 'G' || fmt == 'g') && !alternate; /* Remove trailing zeroes and decimal point. */ if(d > 0x7FFFFFFFFFFFFFFFL){ expFormat(d); return; } /* Handle precision zero stuff as sepecial case. */ if(precision == 0){ if(!removeTrailing){ localtb[ntb++] = bDot; } convert((long)(d + 0.5), based, 10); ntbStart = 0; ntbE = 0; return; } /* Get parts of the number. */ long whole = (long)d; double fr = d - whole; if(fr >= 1 || fr < 0){ expFormat(d); return; } double factor = 1; for (int i = 1; i <= precision && factor <= 0x7FFFFFFFFFFFFFFFL; i++) { factor *= 10; } long l = (long) (factor * fr + 0.5); if (l >= factor) { l = 0; whole++; } /* Convert and add the fractional part to tb. */ convert(l, based, 10); /* Add any leading zeros */ int realLeadingZeros = precision - ntb + ntbE; for(int i = 0; i < realLeadingZeros; i++){ localtb[ntb++] = b0; } /* Add the decimal point. */ localtb[ntb++] = bDot; /* Reset nbStart and remove trailing zeros if set. */ ntbStart = ntbE; if(removeTrailing){ while(ntbStart < ntb && localtb[ntbStart] == b0) ntbStart++; if(ntbStart < ntb && localtb[ntbStart] == bDot) ntbStart++; } /* Convert and add the whole part to tb. */ convert(whole, based, 10); } /** Handle epxonential format numbers. */ private void expFormat(double d){ int e = 0; double dd = d; double factor = 1; byte localtb[] = tb; if(d != 0){ while(dd > 10){ e++; factor /= 10; dd /= 10; } while(dd < 1){ e--; factor *= 10; dd *= 10; } } if((fmt == 'g' || fmt == 'G') && e >= -4 && e < precision){ fixedFormat(d); return; } /* Need to add the power bit first. */ if(e >= 0){ convert(e, based, 10); /* Add on required number of zeros. */ if(ntb < 3){ for(int i = ntb; i < 3; i++){ localtb[ntb++] = b0; } } localtb[ntb++] = bPlus; } else{ convert(-e, based, 10); /* Add on required number of zeros. */ if(ntb < 3){ for(int i = ntb; i < 3; i++){ localtb[ntb++] = b0; } } localtb[ntb++] = bMinus; } /* Add the e, or E. */ if(fmt == 'e' || fmt == 'g') localtb[ntb++] = be; else localtb[ntb++] = bE; /* Record the position we got. */ ntbE = ntb; /* Now add the floating point bit. */ d *= factor; fixedFormat(d); } /** Converts a long to a stream of bytes for the provided base. Note that it fills the temporary buffer in reverse. */ private void convert(long x, byte b[], long base){ long posx; byte localtb[] = tb; if(x == Long.MIN_VALUE){ localtb[ntb++] = b[8]; localtb[ntb++] = b[0]; localtb[ntb++] = b[8]; localtb[ntb++] = b[5]; localtb[ntb++] = b[7]; localtb[ntb++] = b[7]; localtb[ntb++] = b[4]; localtb[ntb++] = b[5]; localtb[ntb++] = b[8]; localtb[ntb++] = b[6]; localtb[ntb++] = b[3]; localtb[ntb++] = b[0]; localtb[ntb++] = b[2]; localtb[ntb++] = b[7]; localtb[ntb++] = b[3]; localtb[ntb++] = b[3]; localtb[ntb++] = b[2]; localtb[ntb++] = b[2]; localtb[ntb++] = b[9]; return; } if(x < 0) posx = -x; else posx = x; if(posx == 0){ localtb[ntb++] = b[0]; return; } int m; while (posx != 0) { m = (int)(posx % base); posx /= base; localtb[ntb++] = b[m]; } } /** Sorts the sign out and adds it to the temporary buffer. */ private void sign(int s){ int signLength = 0; byte signByte1 = bSpace; byte signByte2 = bSpace; byte localtb[] = tb; if(s < 0){ signLength = 1; signByte1 = bMinus; } else if(s > 0){ if(show_plus){ signLength = 1; signByte1 = bPlus; } else if(show_space){ signLength = 1; signByte1 = bSpace; } } else { if(fmt == 'o' && alternate && ntb > 0 && localtb[ntb-1] != b0) { signLength = 1; signByte1 = b0; } else if (fmt == 'x' && alternate) { signLength = 2; signByte2 = b0; signByte1 = bx; } else if (fmt == 'X' && alternate) { signLength = 2; signByte2 = b0; signByte1 = bX; } } /* Now sort out the precision and leading zeros. */ int w = 0; if(leading_zeroes) w = width; else if((fmt == 'd' || fmt == 'i' || fmt == 'x' || fmt == 'X' || fmt == 'o') && precision > 0) w = precision; /* Add leading zeros. */ ntb = repeat(b0, w - (ntb - ntbStart) - ntbE - signLength, localtb, ntb); /* Add the sign. */ if(signLength > 0){ localtb[ntb++] = signByte1; if(signLength > 1){ localtb[ntb++] = signByte2; } } } /** Parses the format string. */ private void parseFormat(String s){ width = 0; precision = -1; leading_zeroes = false; show_plus = false; alternate = false; show_space = false; left_align = false; fmt = ' '; preCount = 0; postCount = 0; /* sets the format of the output. */ int length = s.length(); int parse_state = 0; /* * The parse states are listed here: * 0 = prefix * 1 = flags * 2 = width * 3 = precision * 4 = format * 5 = end */ int i = 0; while(parse_state == 0){ char ci = s.charAt(i); if(i >= length) parse_state = 5; else if(ci == '%'){ if(i < length - 1){ if(s.charAt(i + 1) == '%'){ pre[preCount++] = (byte)'%'; i++; } else { parse_state = 1; } } else { throw new java.lang.IllegalArgumentException(); } } else { pre[preCount++] = (byte)ci; } i++; } while(parse_state == 1){ char ci = s.charAt(i); if(i >= length) parse_state = 5; else if(ci == ' ') show_space = true; else if(ci == '-') left_align = true; else if(ci == '+') show_plus = true; else if(ci == '0') leading_zeroes = true; else if(ci == '#') alternate = true; else { parse_state = 2; i--; } i++; } while(parse_state == 2){ char ci = s.charAt(i); if(i >= length) parse_state = 5; else if('0' <= ci && ci <= '9'){ width = width * 10 + ci - '0'; i++; } else if(ci == '.'){ parse_state = 3; precision = 0; i++; } else parse_state = 4; } while(parse_state == 3){ char ci = s.charAt(i); if(i >= length){ parse_state = 5; } else if('0' <= ci && ci <= '9'){ precision = precision * 10 + ci - '0'; i++; } else { parse_state = 4; } } if(parse_state == 4){ if(i >= length) parse_state = 5; else fmt = s.charAt(i); i++; } if(i < length){ for(int j = i; j < length; j++){ post[postCount++] = (byte)s.charAt(j); } } } /** Set the output stream. */ public void setOutputStream(OutputStream os){ outputStream = os; } /** Set the end of line characters explicitly. */ public void setEolIdentifier(int opsys){ switch(opsys){ case UNIX: eolLength = 1; eol0 = (byte)'\n'; break; case PC: eolLength = 2; eol0 = (byte)'\r'; eol1 = (byte)'\n'; break; case MAC: eolLength = 1; eol0 = (byte)'\r'; break; default: eolLength = 1; eol0 = (byte)'\n'; break; } } /** * Maintain the versions of the file we are about to for writing. */ private static boolean shuffleVersions(String file){ File f = new File(file); if(!f.exists()){ return true; } int version = 99; boolean found = false; do { String fileVersion = file + String.format("_%02d", version); File vf = new File(fileVersion); if(vf.exists()){ found = true; }else{ version--; } } while(!found && version >= 0); version++; String fileVersion = file + String.format("_%02d", version); File newf = new File(fileVersion); return f.renameTo(newf); } /** Opens outputstream for writing. */ public static FILE write(String file){ // see if we could shuffle the files // if not we can't open the file if(!shuffleVersions(file)){ return null; } try { FileOutputStream fileOutputStream = new FileOutputStream(file); FILE output = new FILE(); output.outputStream = fileOutputStream; output.setCharactersInBuffer(0); return output; } catch(Exception e){ setException(e); return null; } } /** Adds end of line. */ private void eol(){ buffer[charactersInBuffer++] = eol0; if(eolLength == 2){ buffer[charactersInBuffer++] = eol1; } } /** Flush buffer if next lot of output is too long. */ private void flushIfFull(int len){ if(len + charactersInBuffer >= bufferSize){ flush(); } } /** Flush buffer. */ private void flush(){ if(charactersInBuffer > 0){ try { outputStream.write(buffer, 0, charactersInBuffer); charactersInBuffer = 0; } catch(Exception e){ System.out.println("Error flushing outputStream " + e); } } } /** Constructor for stdout stderr output. */ public FILE(OutputStream os){ outputStream = os; setCharactersInBuffer(0); autoFlush = true; } /** Destructor just flushes the buffer. */ protected void finalize(){ if(outputStream != null){ flush(); } } public static void setTryFiles(boolean t){ tryFiles = t; } public static boolean getTryFiles(){ return tryFiles; } /** Set the debug state. */ public static void setDebug(boolean state){ debug = state; } /** Set the docuemnt base. */ public static void setDocumentBase(URL url){ if(debug){ System.out.println("setDocumentBase to " + url); } documentBase = url; } /** Set the docuemnt base. */ public static void setCodeBase(URL url){ if(debug){ System.out.println("setCodeBase to " + url); } codeBase = url; } /** Set the exception. */ private static void setException(Exception e){ exception = e; } /** * Get the exception (if any) that was thrown when FILE.open() * was called. */ public static Exception getException(){ return exception; } /** Set the input stream. */ private void setInputStream(InputStream is){ inputStream = is; } /** Get the input stream associated with this file. */ public InputStream getInputStream(){ return inputStream; } public void setCharactersInBuffer(int i){ charactersInBuffer = i; } /** * Make the default constructor private to ensure this class * cannot be instantiated directly. */ private FILE(){ } /** * Constructor that will take an InputStream and create a FILE * object that will read from it. */ public FILE(InputStream is){ setInputStream(is); setException(null); } /** * Fill the input buffer. */ private void fill(){ int charactersRead; // put the last character from the buffer into position 0, // so that unget will work when we unget the first character // after a buffer fill buffer[0] = buffer[charactersInBuffer - 1]; try { do { charactersRead = inputStream.read(buffer, 1, bufferSize); } while (charactersRead == 0); //System.out.println("fill() returned " + charactersRead + // " characters"); if (charactersRead > 0) { charactersInBuffer = charactersRead + 1; nextCharacter = 1; } }catch(Exception e){ setException(e); } } /** * Read a single character. */ public int read(){ if (nextCharacter >= charactersInBuffer) { //System.out.print("before fill() nextCharacter " + nextCharacter); //System.out.println(" charactersInBuffer " + charactersInBuffer); fill(); //System.out.print("after fill() nextCharacter " + nextCharacter); //System.out.println(" charactersInBuffer " + charactersInBuffer); if (nextCharacter >= charactersInBuffer){ //System.out.println("trying to return EOF"); return EOF; } } // convert to unsigned int return buffer[nextCharacter++] & 255; } /** Skip some bytes. */ public int skip(int byteCount){ if(nextCharacter + byteCount < charactersInBuffer){ nextCharacter += byteCount; }else{ while(byteCount-- > 0){ int b = read(); if(b == EOF){ return EOF; } } } return 0; } /** * unget a single character. */ public void ungetc(int c){ if(nextCharacter > 0){ nextCharacter--; } } /** * Return the length of the current line. */ public int getLineLength(){ return lineLength; } /** * Advance the FILE object to the next line. * * A return value of EOF indicates that there are no more lines. */ public boolean nextLine(){ fieldsDetermined = false; int ch = 0; lineLength = 0; do { ch = read(); // all of the special cases are less than '\r' // this saves us 3 comparisons per character // tab is less than \r (thank you paul mortenson) if(ch <= '\r' && ch != '\t'){ if( ch == '\n' ) { return true; } else if( ch == '\r' ) { ch = read(); if( ch != '\n' && ch != EOF){ ungetc(ch); } return true; } else if( ch == EOF ) { // if we see EOF and the line has some contents // we need to return those contents and // give the real EOF next time. //System.out.println("EOF seen: lineLength " + lineLength); return lineLength > 0; } } else{ lineBuffer[lineLength++] = (byte)ch; } } while( lineLength < lineBufferSize); /* skip to the end of the line! */ do { ch = read(); } while( (ch != '\n') && (ch != '\r') && (ch != EOF) ); if( ch == '\r' ) { ch = read(); if( ch != '\n' ){ ungetc(ch); } } return true; } /** * Provide a method that will return the current line * as a String. This has the same functionality as the BufferedReader * readLine() method. There is no need to use this at all. */ public String readLine(){ if(nextLine()) return getCurrentLineAsString(); return null; } /** * Return the current line as a String. */ public String getCurrentLineAsString(){ return new String(lineBuffer, 0, lineLength); } /** * Return the specified character from the current line. */ public char getChar(int offset){ if(offset < 0 || offset >= lineLength) return (char)EOF; return (char)lineBuffer[offset]; } /** * Does the current line contain the specified string at the specified * location. */ public boolean currentLineContains(String string, int offset){ int stringLength = string.length(); if(stringLength + offset > lineLength) return false; for(int i = 0; i < stringLength; i++){ if(string.charAt(i) != lineBuffer[i + offset]){ return false; } } return true; } /** * Return a substring of the current line. */ public String getSubstring(int offset, int length){ if(lineLength <= offset) return ""; if(offset + length > lineLength) return new String(lineBuffer, offset, lineLength - offset); return new String(lineBuffer, offset, length); } /** * Read an integer from the current line buffer. */ public int readInteger(int offset, int length){ int end = offset + length; // make sure we don't read past the end of the line. if(end > lineLength){ end = lineLength; } boolean negative = false; int currentPosition = offset; for(/* nothing */; currentPosition < end; currentPosition++){ byte b = lineBuffer[currentPosition]; if(b == ' ' || b == '+'){ continue; }else if(b == '-'){ negative = true; }else{ break; } } int total = 0; for(/* nothing */; currentPosition < end; currentPosition++){ byte b = lineBuffer[currentPosition]; if(b >= '0' && b <= '9'){ total = (10 * total) + (b - '0'); }else{ break; } } if(negative){ return -total; }else{ return total; } } /** * Specific method for reading a 3 column integer. */ public int readInteger3(int offset){ // not yet implemented return readInteger(offset, 3); } /** * Read a double precision number from the specified region. */ public double readDouble(int offset, int length){ int end = offset + length; // make sure we don't read past the end of the line. if(end > lineLength){ end = lineLength; } boolean negative = false; int currentPosition = offset; long total = 0; boolean dotSeen = false; int decimalPlaces = 0; boolean exponentSeen = false; boolean exponentNegative = false; int exponentTotal = 0; for(/* nothing */; currentPosition < end; currentPosition++){ byte b = lineBuffer[currentPosition]; if(b >= '0' && b <= '9'){ if(exponentSeen){ exponentTotal = exponentTotal * 10 + (b - '0'); }else{ total = total * 10 + (b - '0'); if(dotSeen){ decimalPlaces++; } } }else if(b == ' ' || b =='+'){ continue; }else if(b == '-'){ if(exponentSeen){ exponentNegative = true; }else{ negative = true; } }else if(b == '.'){ dotSeen = true; }else if(b == 'e' || b == 'E'){ exponentSeen = true; }else{ break; } } // fix up sign on exponentTotal if(exponentSeen && exponentNegative){ exponentTotal = -exponentTotal; } // now turn the integer into a double by taking into // account the number of decimal places that were seen. double result = (double)total; decimalPlaces -= exponentTotal; switch(decimalPlaces){ case 1: result *= 0.1; break; case 2: result *= 0.01; break; case 3: result *= 0.001; break; case 4: result *= 0.0001; break; case 5: result *= 0.00001; break; case 6: result *= 0.000001; break; case 7: result *= 0.0000001; break; case 8: result *= 0.00000001; break; case 9: result *= 0.000000001; break; // anything else and we have to do it manually default: if(decimalPlaces > 0){ while(decimalPlaces-- > 0){ result *= 0.1; } }else{ while(decimalPlaces++ < 0){ result *= 10.0; } } } if(negative){ return -result; }else{ return result; } } /** Split the line into fields on white space characters. */ private void determineFields(){ int len = getLineLength(); boolean inField = false; int currentLen = 0; fieldCount = 0; for(int i = 0; i < len; i++){ byte c = lineBuffer[i]; if(c != ' ' && c != '\t'){ if(inField){ currentLen++; }else{ inField = true; fieldStartPosition[fieldCount] = i; currentLen = 1; fieldCount++; } }else{ if(inField){ fieldLength[fieldCount-1] = currentLen; inField = false; } } } // if the field goes up to the end of line we // need to terminate the field. if(inField){ fieldLength[fieldCount-1] = currentLen; } fieldsDetermined = true; } /** Return the number of fields on the current line. */ public int getFieldCount(){ if(!fieldsDetermined){ determineFields(); } return fieldCount; } /** Get the start position of the field. */ public int getFieldStart(int f){ if(!fieldsDetermined){ determineFields(); } if(f >= fieldCount|| f < 0) return -1; return fieldStartPosition[f]; } /** Get the start position of the field. */ public int getFieldLength(int f){ if(!fieldsDetermined){ determineFields(); } if(f >= fieldCount|| f < 0) return 0; return fieldLength[f]; } /** Get the field as a string. */ public String getField(int f){ if(!fieldsDetermined){ determineFields(); } if(f >= fieldCount|| f < 0) return null; int start = getFieldStart(f); int len = getFieldLength(f); return getSubstring(start, len); } /** Get the specified character from the field. */ public byte getFieldChar(int f, int c){ if(!fieldsDetermined){ determineFields(); } if(f >= fieldCount|| f < 0){ System.out.println("invalid field: " + f); return (byte)-1; } if(c >= 0 && c < getFieldLength(f)) return lineBuffer[getFieldStart(f) + c]; System.out.println("invalid char: field " + f + " char " + c); return (byte)-1; } /** * Try and make path relative to current directory. */ public static String getRelativePath(String path){ try { File currentDir = new File("."); String currentPath = null; currentPath = currentDir.getCanonicalPath(); if(currentPath != null){ // get canonical path of file, to // fix case problems with drive letters in Windows. File pathFile = new File(path); String canonicalPath = pathFile.getCanonicalPath(); if(canonicalPath.startsWith(currentPath)){ String relativePath = canonicalPath.substring(currentPath.length()); // hmm, unix doesn't have trailing / on // current dir canonical path, so remove any // that are left on the front of the path here. while(relativePath.charAt(0) == '/' || relativePath.charAt(0) =='\\'){ relativePath = relativePath.substring(1); } return relativePath; } return path; } }catch(Exception e){ return path; } return path; } /** * Return a FILE object for reading the specified resource. * * If the resource looks like it is a URL name (begins with * something like 'http:' or 'ftp:') then an attempt will * be made to open it as a URL. Otherwise the resource will * be opened as a file. */ public static FILE open(String resource){ FILE input = null; if(debug){ System.out.println("FILE.open tryfiles=" + tryFiles); System.out.println("FILE.open resource=" + resource); } if(input == null && (resource.endsWith(".properties") || resource.endsWith(".gif") || resource.endsWith(".jpg") || resource.endsWith(".jpeg"))){ // instantiate a file so we can use // getResourceAsStream() // one last chance it might be in a jar file InputStream inputStream = null; if(debug){ System.out.println("attempt to open as absolute resource"); } inputStream = openResource("/" + resource); if(inputStream == null){ if(debug){ System.out.println("attempt to open as relative resource"); } inputStream = openResource(resource); } if(inputStream != null){ if(debug){ System.out.println("opened as resource"); } input = new FILE(inputStream); } } if(input == null && documentBase != null){ if(debug){ System.out.println("documentBase " + documentBase); } try { URL url = new URL(documentBase, resource); if(debug){ System.out.println("attempting to open url " + url); } input = open(url); if(debug && input != null){ System.out.println("url successfully opened"); } }catch(Exception e){ if(debug){ System.out.println("**** URL exception " + e); } } } if(input == null && codeBase != null){ if(debug){ System.out.println("codeBase " + codeBase); } try { URL url = new URL(codeBase, resource); if(debug){ System.out.println("attempting to open url " + url); } input = open(url); if(debug && input != null){ System.out.println("url successfully opened"); } }catch(Exception e){ if(debug){ System.out.println("**** URL exception " + e); } } } if(input == null && stringIsURL(resource)){ try { if(debug){ System.out.println("attempting to open as absolute url"); } input = openURL(resource); if(debug && input != null){ System.out.println("successfully opened as absolute url"); } }catch(Exception e){ input = null; if(debug){ System.out.println("**** absolute url exception " + e); } } } if(input == null && tryFiles){ try { if(debug){ System.out.println("attempting to open as file"); } input = openFile(resource); if(debug && input != null){ System.out.println("successfully opened as file"); } }catch(Exception e){ input = null; if(debug){ System.out.println("**** open file exception " + e); } } } if(input != null && debug){ System.out.println("opened as resource"); } if(resource.endsWith(".gz")){ try { GZIPInputStream gis = new GZIPInputStream(input.inputStream); input.inputStream = gis; }catch(Exception e){ input = null; if(debug){ System.out.println("failed to open gzip'ed inputStream"); System.out.println(e); } } } // finally try and see if there is a resource with // .gz extension that corresponds to the resource // we were asked to open // i.e. we were asked to open 1abc.pdb, but it // didn't exist. Does 1abc.pdb.gz exist instead? if(input == null && !resource.endsWith(".gz")){ input = open(resource + ".gz"); if(input != null){ return input; } } return input; } /** Try and open the file as a resource from a jar file. */ private static InputStream openResource(String resource){ // netscape requires that you must get resources in this // fashion. getSystemResourceAsStream will always fail. // this method will also fail if we try and open // a file that has an extension that isn't in the approved // list... in a word - ridiculous. FILE dummy = new FILE(); InputStream inputStream = dummy.getClass().getResourceAsStream(resource); return inputStream; } /** Does the resource look like it is a URL? */ public static boolean stringIsURL(String resource){ int length = resource.length(); int i = 0; for(/* nothing */; i < length - 3; i++){ char c = resource.charAt(i); if((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')){ continue; }else if(c == ':'){ return true; } } return false; } /** * Create a FILE object to read from the specified File object. */ public static FILE open(File file){ try { FileInputStream fileInputStream = new FileInputStream(file); return new FILE(fileInputStream); }catch(Exception e){ setException(e); if(debug){ System.out.println("open(File): " + e); } return null; } } /** * Create a FILE object to read from the specified filename. */ public static FILE openFile(String filename){ try { FILE f = null; File file = new File(filename); if(file.isAbsolute()){ f = open(file); }else{ if(documentBase != null){ String docBase = documentBase.toString(); if(docBase.startsWith("file:/")){ docBase = docBase.substring(6); file = new File(docBase, filename); f = open(file); } } if(f == null && codeBase != null){ String cBase = codeBase.toString(); if(cBase.startsWith("file:/")){ cBase = cBase.substring(6); file = new File(cBase, filename); f = open(file); } } if(f == null){ file = new File(filename); f = open(file); } } return f; }catch(Exception e){ setException(e); if(debug){ System.out.println("openFile: " + e); } return null; } } /** * Create a FILE object to read from the specified urlname. */ public static FILE openURL(String urlname){ try { URL url = new URL(urlname); return open(url); }catch(Exception e){ setException(e); return null; } } /** * Create a FILE object to read from the specified URL. */ public static FILE open(URL url){ try { InputStream urlInputStream = url.openStream(); //urlConnection.getInputStream(); //System.out.println("open(URL) urlInputStream " + urlInputStream); return new FILE(urlInputStream); }catch(Exception e){ if(debug){ System.out.println("open(URL) exception " + e); } setException(e); return null; } } /** * Close a file object. * * This also closes the underlying InputStream. * Returns true if there is a problem closing the FILE. */ public void close(){ if(inputStream != null){ try { inputStream.close(); //return false; }catch(Exception e){ setException(e); //return true; } } if(outputStream != null){ try { flush(); outputStream.close(); //return false; }catch(Exception e){ setException(e); //return true; } } } /** Convenience method to load a properties file. */ public static Properties loadProperties(String resource){ Properties properties = new Properties(); FILE propertyStream = open(resource); if(debug){ System.out.println("loadProperties from " + propertyStream); } if(propertyStream != null){ try { properties.load(propertyStream); }catch(IOException e){ e.printStackTrace(); }finally{ propertyStream.close(); } } return properties; } /* Some utility methods for decoding numbers. */ /** Synonym for readIntegerFromField. */ public int getInteger(int f){ return readIntegerFromField(f); } public float getFloat(int f){ return (float)getDouble(f); } /** Synonym for readDoubleFromField. */ public double getDouble(int f){ return readDoubleFromField(f); } /** Read an integer from the specified field. */ public int readIntegerFromField(int f){ if(!fieldsDetermined){ determineFields(); } if(f < 0 || f >= fieldCount){ System.out.println("readIntegerFromField: attempt " + "to read from field " + f); return 0; }else{ int start = getFieldStart(f); int len = getFieldLength(f); return readInteger(start, len); } } /** Read a double from the specified field. */ public double readDoubleFromField(int f){ if(!fieldsDetermined){ determineFields(); } if(f < 0 || f >= fieldCount){ System.out.println("readDoubleFromField: attempt " + "to read from field " + f); return 0.0; }else{ int start = getFieldStart(f); int len = getFieldLength(f); return readDouble(start, len); } } /** Read an integer from the specified string. */ public static int readInteger(String string){ int stringLength = string.length(); int value = 0; boolean negative = false; for(int i = 0; i < stringLength; i++){ char c = string.charAt(i); if(c >= '0' && c <= '9'){ value = (10 * value) + (c - '0'); }else if(c == '-'){ negative = true; } } if(negative) return -value; return value; } /** Read a double value from a string. */ public static double readDouble(String string){ try { // try to convert to double.. return Double.valueOf(string.trim()).doubleValue(); } catch (Exception e) { return 0.0; } } /** Convenience methods for splitting strings into fields. */ public static String[] split(String string){ return split(string, null); } /** Convenience methods for splitting strings into fields. */ public static String[] split(String string, String separators){ StringTokenizer tokenizer = null; if(separators == null){ tokenizer = new StringTokenizer(string); }else{ tokenizer = new StringTokenizer(string, separators); } int fieldCount = tokenizer.countTokens(); String fields[] = new String[fieldCount]; int field = 0; while(tokenizer.hasMoreElements()){ fields[field++] = tokenizer.nextToken(); } return fields; } /** * Read the data from the specified FILE object. */ public static int readFrom(FILE file){ int bytesRead = 0; while(file.read() != FILE.EOF){ bytesRead++; } return bytesRead; } }