001package bradleyross.library.helpers;
002import java.net.URL;
003import java.net.URLConnection;
004import java.io.File;
005import java.io.FileReader;
006import java.io.OutputStream;
007import java.io.InputStream;
008import java.io.FileInputStream;
009import java.io.IOException;
010import java.io.FileNotFoundException;
011import java.util.Map;
012import java.util.List;
013import java.io.FileOutputStream;
014import java.awt.image.BufferedImage;
015
016import javax.imageio.ImageIO;
017import java.awt.Image;
018import java.awt.Graphics;
019/**
020 * Methods to read and write files designed to be
021 * used within other programs.
022 * <p>Note: {@link java.net.SocketTimeoutException} is a subclass of
023 *    {@link java.io.IOException}.</p>
024 * <p>Note: Using the {@link URLConnection#setReadTimeout(int) } and
025 *    {@link URLConnection#setConnectTimeout(int) } methods should
026 *    prevent the methods in this class from hanging.</p>
027 * <p>It looks like the best approach is to rerun the process if an
028 *    error occurs.  Start with small timeout values and increase them
029 *    on each iteration.</p>
030 * @author Bradley Ross
031 */
032public class FileHelpers
033{
034        /**
035         * Since all of the methods and fields are static, the constructor
036         * should never be used.
037         */
038        protected FileHelpers() { ; }
039        /**
040         * Number of milliseconds to wait for a read or
041         * socket operation.
042         * @see #getTimeout()
043         * @see #setTimeout(int)
044         * @see URLConnection#setReadTimeout(int)
045         * @see URLConnection#setConnectTimeout(int)
046         * @deprecated
047         */
048        protected static int timeout = 10000;
049        /**
050         * Length of time allowed for connect operations.
051         * 
052         * @see URLConnection#setConnectTimeout(int)
053         * @see #setConnectTimeout(int)
054         * @see #getConnectTimeout()
055         */
056        protected static int connectTimeout = 500;
057        /**
058         * Length of time allowed for read operations.
059         * 
060         * @see URLConnection#setReadTimeout(int)
061         * @see #setReadTimeout(int)
062         * @see #getReadTimeout()
063         */
064        protected static int readTimeout = 500;
065        /**
066         * Timeout for entire transfer in milliseconds.
067         * @see #getCombinedTime()
068         * @see #setConnectTimeout(int)
069         */
070        protected static long combinedTime = 80000;
071        /**
072         * Getter for connectTimeout.
073         * 
074         * @see #connectTimeout
075         * @return Timeout value for connect operations
076         */
077        public static int getConnectTimeout()
078        { return connectTimeout; }
079        /**
080         * Setter for connectTimeout.
081         * 
082         * @see #connectTimeout
083         * @param value Value to be used for timeout for socket operations.
084         */
085        public static void setConnectTimeout(int value)
086        { connectTimeout = value; }
087        /**
088         * Getter for readTimeout.
089         * 
090         * @see #readTimeout
091         * @return Timeout value for socket operations
092         */
093        public static int getReadTimeout()
094        { return readTimeout; }
095        /**
096         * Setter for readTimeout.
097         * 
098         * @see #readTimeout
099         * @param value Value to be used for timeout for read operations
100         */
101        public static void setReadTimeout(int value)
102        { readTimeout = value; }
103        /** 
104         * Getter for combinedTime
105         * @return Milliseconds allowed for entire file transfer
106         * @see #combinedTime
107         */
108        public static long getCombinedTime()
109        { return combinedTime; }
110        /**
111         * Setter for combinedTimeout
112         * @param value Number of milliseconds to be allowed for the entire file transfer
113         * @see #combinedTime
114         */
115        public static void setCombinedTime(long value)
116        { combinedTime = value; }
117        /** 
118         * Setter for timeout.
119         * 
120         * @param value Milliseconds to wait for read or socket operation
121         * @see #timeout
122         * @deprecated
123         */
124        public void setTimeout(int value)
125        { timeout = value; }
126        /**
127         * Getter for timeout
128         * 
129         * @return Milliseconds to wait for read or socket operation
130         * @see #timeout
131         * @deprecated
132         */
133        public int getTimeout()
134        { return timeout; }
135        /** 
136         * Determines quantity of diagnostic output to be generated. 
137         * @see #getDebugLevel()
138         * @see #setDebugLevel(int)
139         */
140        protected static int debugLevel = 0;
141        /**
142         * Getter for debugLevel
143         * @see #debugLevel
144         * @return Value of debugLevel
145         */
146        public static int getDebugLevel()
147        { return debugLevel; }
148        /**
149         * Setter for debugLevel
150         * @see #debugLevel
151         * @param value to be used for debugLevel
152         */
153        public static void setDebugLevel(int value)
154        { debugLevel = value; }
155        /**
156         * Calculates 2 to the power i as a helper in determining timings
157         * for some of the operations.
158         * @param i Power to which 2 is raised  
159         * @return  2 to the power i
160         */
161        protected static int power(int i)
162        {
163                if (i <= 0) { return 1; }
164                else if (i == 1) { return 2; }
165                else if (i == 2) { return 4; }
166                else if (i > 32) { return Integer.MAX_VALUE; }
167                else
168                {
169                        int working = 2;
170                        for (int i2 = 1; i2 < i; i2++)
171                        { working = working * 2; }
172                        return working;
173                }
174        }
175        /**
176         * Reads the contents of a text file into a String object
177         * given the name of the file.
178         * @param name  File name
179         * @return Contents of file
180         */
181        public static String readTextFile(String name)
182        throws java.io.IOException
183        {
184                File inputFile = new File(name);
185                if (!inputFile.exists())
186                {
187                        return (String) null;
188                }
189                FileReader reader = null;
190                StringBuffer working = new StringBuffer();
191                char[] buffer = new char[4096];
192                try
193                {
194                        reader = new FileReader(inputFile);
195                        while (true)
196                        {
197                                int length = reader.read(buffer, 0, buffer.length);
198                                if (length < 0) { break; }
199                                working.append(buffer, 0, length);
200                        }
201                        reader.close();
202                }
203                catch (java.io.IOException e)
204                {
205                        if (Thread.currentThread().isInterrupted()) 
206                        { 
207                                System.out.println("*****Interrupted");
208                                Thread.yield(); 
209                                return (String) null; 
210                        }
211                        System.out.println("Exception encountered " + e.getClass().getName());
212                        System.out.println(e.getMessage());
213                        e.printStackTrace(System.out);
214                        /*
215                         * Throwing the exception to the calling program added
216                         * 06-November-2008.
217                         */
218                        throw e;
219                }
220                return new String(working);
221        }
222        /**
223         * Read the contents referenced by a URL into a String
224         * object.
225         * <p>The java.net.URL object required as a parameter is
226         *    created by a call of the type
227         *    <code>java.net.URL url =
228         *    new java.net.URL(urlString)</code> where urlString
229         *    is a String object containing the URL identifier.</p>
230         * <p>This should only be used for text files.  Results are
231         *    unpredictable and probably undesirable when working
232         *    with binary files.</p>
233         *
234         * <ul>
235         * <li><p>ftp:  <code>ftp://userid:password@host:port/file</code></p>
236         *     </li>
237         * <li><p>file:  <code>file:// (file name starting with / indicating root)</code></p>
238         *     </li>
239         * </ul>
240         * @param address URL of desired contents
241         * @return String
242         */
243        public static String readTextFile(java.net.URL address)
244        throws java.io.IOException
245        {
246                StringBuffer working = new StringBuffer();
247                long startTime = System.currentTimeMillis();
248                long currentTime = System.currentTimeMillis();
249                long endTime = System.currentTimeMillis() + combinedTime;
250                int totalLength = 0;
251                boolean success = false;
252                int tries = 0;
253                for (int i = 0; i < 4; i++)
254                {
255                        tries++;
256                        try
257                        {
258                                URLConnection connection = null;
259                                connection = address.openConnection();
260                                connection.setConnectTimeout(connectTimeout * power(i));
261                                connection.setReadTimeout(readTimeout * power(i));
262                                connection.connect();
263                                java.io.FilterInputStream contents = 
264                                        (java.io.FilterInputStream) connection.getContent();
265                                if (debugLevel > 0)
266                                {
267                                        System.out.println("Class is " + contents.getClass().getName());
268                                        System.out.println("Superclass is " +
269                                                        contents.getClass().getSuperclass().getName());
270                                }
271                                byte[] line = new byte[500];
272                                java.io.BufferedInputStream buffer = new 
273                                java.io.BufferedInputStream(contents);
274                                int charsRead;
275                                /*  When obtaining the contents, the class of contents is
276                                 *   sun.net.www.content.text.PlainTextInputStream
277                                 *   whose superclass is
278                                 *   java.io.FilterInputStream
279                                 */
280                                while (true)
281                                {
282                                        charsRead = buffer.read(line, 0, 490);
283                                        if (charsRead < 0) { break; }
284                                        // System.out.println("** " + Integer.toString(charsRead) + " **");
285                                        working.append(new String(line, 0, charsRead));
286                                        totalLength = totalLength + charsRead;
287                                        currentTime = System.currentTimeMillis();
288                                        if (currentTime > endTime)
289                                        { 
290                                                throw new java.io.IOException("Time limit of " +
291                                                                Long.toString(combinedTime) + " milliseconds exceeded" +
292                                                " readTextFile(URL)"); 
293                                        }
294                                }
295                                if (currentTime - startTime > 30000l)
296                                { 
297                                        System.out.println(Long.toString(currentTime - startTime) + " milliseconds for " +
298                                                        Integer.toString(totalLength) + " bytes"); 
299                                }
300                                if (debugLevel > 0)
301                                { 
302                                        System.out.print(" " + Integer.toString(totalLength) +
303                                        " bytes "); 
304                                }
305                                success = true;
306                        }
307                        catch (java.io.IOException e)
308                        {
309                                if (Thread.currentThread().isInterrupted()) 
310                                { 
311                                        System.out.println("***** Interrupted");
312                                        Thread.yield(); 
313                                        return (String) null; 
314                                }
315                                System.out.println("Exception encountered in readTextFile " +
316                                                "(Attempt " + Integer.toString(tries) + ") ");
317                                System.out.println("   " + e.getClass().getName() + " " + e.getMessage());
318                                if (tries > 4) { throw e; }
319                        }
320                        if (success) {break; }
321                }
322                if (success && tries > 1)
323                { System.out.println("Success after " + Integer.toString(tries) + " tries"); }
324                return new String(working);
325        }
326        /**
327         * Write the contents of a byte array to a URL as a binary data stream.
328         * <p>It appears that you can write to protocol ftp but not to protocol
329         *    file.</p>
330         * @param address URL of destination
331         * @param data Byte array to be sent
332         * @throws java.io.IOException
333         * @throws java.net.SocketTimeoutException
334         * @see java.net.URLConnection
335         */
336        public static void sendBytes (java.net.URL address, byte[] data) 
337        throws java.io.IOException
338        {
339                int tries = 0;
340                boolean success = false;
341                for (int i = 0; i < 4; i++)
342                {
343                        URLConnection connection = null;
344                        java.io.OutputStream output = null;
345                        try
346                        {
347                                connection = address.openConnection();
348                                connection.setDoInput(false);
349                                connection.setDoOutput(true);
350                                connection.setReadTimeout(readTimeout * power(i));
351                                connection.setConnectTimeout(connectTimeout * power(i));
352                                showProperties(connection);
353                                connection.connect();
354                                output = connection.getOutputStream();
355                                output.write(data);
356                                output.close();
357                                success = true;
358                        }
359                        catch (java.io.IOException e)
360                        {
361                                if (Thread.currentThread().isInterrupted()) 
362                                { 
363                                        System.out.println("***** Interrupted");
364                                        Thread.yield(); 
365                                        return; 
366                                }
367                                System.out.println("Error in sendBytes: " + e.getClass().getName() + " " +
368                                                e.getMessage());
369                        }
370                        tries++;
371                        if (success) { break; }
372                }
373                if (!success)
374                {
375                        System.out.println("Failure after " + Integer.toString(tries) + " tries");
376                        throw new IOException("Failure after " + Integer.toString(tries) + " tries");
377                }
378                if (success && tries > 1)
379                { 
380                        System.out.println("Success after " + Integer.toString(tries) + " tries"); 
381                }
382        }
383        /**
384         * Write the contents of a file to a URL, passing the data as
385         * a byte stream (binary data).
386         * @param address URL of destination
387         * @param data File object containing data to be sent
388         * @throws java.io.IOException
389         */
390        public static void sendBytes (java.net.URL address, java.io.File data) 
391        throws java.io.IOException
392        {
393                int tries = 0;
394                boolean success = false;
395                for (int i = 0; i < 4; i++)
396                {
397                        long startTime = System.currentTimeMillis();
398                        long currentTime = startTime;
399                        long endTime = startTime + combinedTime;
400                        URLConnection connection = null;
401                        java.io.FileInputStream input = null;
402                        java.io.OutputStream output = null;
403                        byte[] buffer = new byte[32768];
404                        int byteCounter = 0;
405                        int readCounter = 0;
406                        try
407                        {
408                                connection = address.openConnection();
409                                connection.setDoInput(false);
410                                connection.setDoOutput(true);
411                                /*
412                                 * I changed the multiplier from (i+1) to
413                                 * power(i)
414                                 */
415                                connection.setReadTimeout(readTimeout * power(i));
416                                connection.setConnectTimeout(connectTimeout * power(i));
417                                showProperties(connection);
418                                connection.connect();
419                                output = connection.getOutputStream();
420                                input = new java.io.FileInputStream(data);
421                                while (true)
422                                {
423                                        int length = input.read(buffer);
424                                        if (length < 0) { break; }
425                                        output.write(buffer, 0, length);
426                                        byteCounter = byteCounter + length;
427                                        readCounter++;
428                                        // Thread.yield();
429                                        if (System.currentTimeMillis() > endTime)
430                                        { 
431                                                throw new java.io.IOException("Time limit of " +
432                                                                Long.toString(combinedTime) + 
433                                                " milliseconds exceeded in sendBytes(URL, File)"); 
434                                        }
435                                }
436                                currentTime = System.currentTimeMillis();
437                                if (currentTime - startTime > 30000l)
438                                {
439                                        System.out.println("sendBytes(URL, File) required " + Long.toString(currentTime - startTime) +
440                                                        " milliseconds to process " + data.getCanonicalPath() + " : " +
441                                                        Integer.toString(byteCounter) + " bytes in " + Integer.toString(readCounter) +
442                                        " reads");
443                                        String name = address.getFile();
444                                        /*
445                                         * It is necessary to remove the portion of the name following the semicolon since the
446                                         * URL methods include the type designation (;type=i or ;type=a) with the name portion of the
447                                         * URL.
448                                         */
449                                        if (name.indexOf(";") > 0)
450                                        {
451                                                name = name.substring(0, name.indexOf(";"));
452                                        }
453                                        System.out.println("Guessed MIME type for " + name +
454                                                        " is " + URLConnection.guessContentTypeFromName(name));
455                                }
456                                input.close();
457                                output.close();
458                                success = true;
459                        }
460                        catch (java.io.IOException e)
461                        {
462                                if (Thread.interrupted()) { Thread.yield(); return; }
463                                System.out.println("Error in sendBytes: " + e.getClass().getName() + " " +
464                                                e.getMessage());
465                        }
466                        tries++;
467                        if (success) { break; }
468                }
469                if (!success)
470                { 
471                        System.out.println("Failure after " + Integer.toString(tries) + " tries");
472                        throw new IOException("Failure after " + Integer.toString(tries) + " tries");
473                }
474                if (success && tries > 1)
475                { 
476                        System.out.println("Success after " + Integer.toString(tries) + " tries");
477                }
478        }
479        /**
480         * Send the contents of an InputStream to a File.
481         * @param output File object for output.
482         * @param input InputStream for input.
483         */
484        public static void sendBytes (File output, InputStream input)
485        {
486                byte buffer[] = new byte[65536];
487                int charsRead = 0;
488                int totalChars = 0;
489                try
490                {
491                        FileOutputStream sender = new FileOutputStream(output);
492                        while (true)
493                        {
494                                charsRead = input.read(buffer);
495
496                                if (charsRead < 0)
497                                {
498                                        break;
499                                }
500                                totalChars = totalChars + charsRead;
501                                sender.write (buffer, 0, charsRead);
502                                System.out.println(Integer.toString(charsRead) + " " + Integer.toString(totalChars));
503                        }
504                        sender.close();
505                }
506                catch (FileNotFoundException e)
507                {
508                        System.out.println(e.getClass().getName() + " " + e.getMessage());
509                }
510                catch (IOException e)
511                {
512                        System.out.println(e.getClass().getName() + " " + e.getMessage());
513                }
514        }
515        /**
516         * Read the contents of a URL into a file, passing the
517         * data as  byte (binary) stream.
518         * 
519         * <p>In case of failure, the algorithm retries with
520         *    increased values of connectTimeout and
521         *    readTimeout.</p>
522         * <p>This modification may eventually be added to the
523         *    other methods.</p>
524         * @param url Location of data to be read
525         * @param file File where data is to be written
526         * @see URLConnection#setReadTimeout(int)
527         * @see URLConnection#setConnectTimeout(int)
528         * @throws java.io.IOException
529         */
530        public static void getBytes(URL url, File file)
531        throws java.io.IOException
532        {
533                boolean success = false;
534                int tries = 0;
535                int totalLength = 0;
536                URLConnection connection = null;
537                java.io.FileOutputStream output = null;
538                java.io.InputStream input = null;
539                byte[] buffer = new byte[32768];
540                long startTime = System.currentTimeMillis();
541                long endTime = startTime + combinedTime;
542                long currentTime = System.currentTimeMillis();
543                for (int i = 0; i < 4; i++) 
544                {
545                        if (output != null)
546                        {
547                                output.close();
548                                output = null;
549                        }
550                        if (input != null)
551                        {
552                                input = null; 
553                        }
554                        if (connection != null)
555                        {
556                                connection = null;
557                        }
558                        output = null;
559                        input = null;
560                        try {
561                                connection = url.openConnection();
562                                connection.setDoInput(true);
563                                connection.setDoOutput(false);
564                                connection.setReadTimeout(readTimeout * power(i));
565                                connection.setConnectTimeout(connectTimeout * power(i));
566                                showProperties(connection);
567                                connection.connect();
568                                input = connection.getInputStream();
569                                while (true) 
570                                {
571                                        /* 
572                                         * The read statement blocks until there
573                                         * are characters available to be read.
574                                         * 
575                                         * I thought that using 
576                                         * URLConnection.setReadTimeout
577                                         * would result in an exception if the
578                                         * time out was exceeded but it doesn't seem
579                                         * to be happening.
580                                         */
581                                        int length = input.read(buffer);
582                                        if (length < 0) 
583                                        {
584                                                break;
585                                        }
586                                        if (output == null) {
587                                                output = new java.io.FileOutputStream(file);
588                                        }
589                                        output.write(buffer, 0, length);
590                                        totalLength = totalLength + length;
591                                        Thread.yield();
592                                        if (debugLevel > 0) 
593                                        {
594                                                System.out.print("*");
595                                        }
596                                        currentTime = System.currentTimeMillis();
597                                        if (currentTime > endTime) {
598                                                throw new java.io.IOException("Time limit of "
599                                                                + Long.toString(combinedTime)
600                                                                + " milliseconds exceeded "
601                                                                + "in getBytes(URL,File)");
602                                        }
603                                } /*  End of while loop */
604
605                                input.close();
606                                if (output != null) 
607                                {
608                                        output.close(); 
609                                }
610                                success = true;
611                        } 
612                        catch (java.io.IOException e) 
613                        {
614                                if (Thread.currentThread().isInterrupted()) 
615                                {
616                                        System.out.println("***** Interrupted");
617                                        Thread.yield();
618                                        return;
619                                }
620                                if (i == 3 || debugLevel > 0)
621                                {
622                                        System.out.println("Error in getBytes: "
623                                                        + e.getClass().getName() + " " + e.getMessage());
624                                        System.out.println("     Number of bytes moved: "
625                                                        + Integer.toString(totalLength));
626                                }
627                                if (i == 3)
628                                {
629                                        throw e;
630                                }
631                        }
632                        tries = i + 1;
633                        if (success) { break; }
634                }
635                if (!success)
636                {
637                        System.out.println("Failure after " + Integer.toString(tries) + " tries"); 
638                }
639                else if (success && tries > 1 && debugLevel > 0)
640                {
641                        System.out.println("Success after " + Integer.toString(tries) + " tries");
642                }
643                if (debugLevel > 0)
644                { System.out.print("      " + Integer.toString(totalLength) + " bytes "); }
645                if (currentTime - startTime > 30000)
646                {
647                        System.out.println(Long.toString(currentTime - startTime) +
648                                        " milliseconds for " + Integer.toString(totalLength) +
649                        " bytes");
650                }
651        }
652        /**
653         * Read the contents of a  a file and then sends
654         * the data to a OutputStream object.
655         * 
656         * @param input File containing data to be written
657         * @param output OutputStream object where data is to be written
658         * @throws java.io.IOException
659         * @see File
660         */
661        public static void getBytes(File input, OutputStream output)
662        throws java.io.IOException
663        {
664                FileInputStream reader = new FileInputStream(input);
665                int totalLength = 0;
666                byte[] buffer = new byte[4096];
667                long startTime = System.currentTimeMillis();
668                long currentTime = startTime;
669                long endTime = startTime + combinedTime;
670                if (debugLevel > 1)
671                {
672                        System.out.println("getBytes(File,OutputStream: " + input.getName() + " exists: " +
673                                        Boolean.toString(input.exists()));
674                        System.out.println("getBytes(File,OutputStream): ");
675                }
676                try
677                {
678                        while (true)
679                        {
680                                /* 
681                                 * The read statement blocks until there
682                                 * are characters available to be read.
683                                 * 
684                                 * I thought that using 
685                                 * URLConnection.setReadTimeout
686                                 * would result in an exception if the
687                                 * time out was exceeded but it doesn't seem
688                                 * to be happening.
689                                 */
690                                int length = reader.read(buffer);
691                                if (debugLevel > 1)
692                                {
693                                        System.out.println(Integer.toString(length) + " bytes read");
694                                }
695                                if (length < 0) { break; }
696
697                                output.write(buffer, 0, length);
698                                totalLength = totalLength + length;
699                                Thread.yield();
700                                if (debugLevel > 0) { System.out.print("*"); }
701                                currentTime = System.currentTimeMillis();
702                                if (currentTime > endTime)
703                                { 
704                                        reader.close();
705                                        throw new java.io.IOException("Time limit of " +
706                                                        Long.toString(combinedTime) + 
707                                        " milliseconds exceeded in getBytes(File, OutputStream)"); 
708                                }
709                        }
710                        if (debugLevel > 0)
711                        { System.out.println("Total of " + Integer.toString(totalLength) + " bytes processed "); }
712                        if (currentTime - startTime > 30000)
713                        {
714                                System.out.println(Long.toString(currentTime - startTime) + " milliseconds for " +
715                                                Integer.toString(totalLength) + " bytes");
716                        }
717                }
718                catch (java.io.IOException e)
719                {
720                        if (Thread.currentThread().isInterrupted()) 
721                        { 
722                                System.out.println("***** Interrupted");
723                                Thread.yield(); 
724                                return; 
725                        }
726                        System.out.println("Error in getBytes: " + e.getClass().getName() + " " +
727                                        e.getMessage());
728                        System.out.println("Number of bytes moved: " +
729                                        Integer.toString(totalLength));
730                        throw e;
731                }
732                reader.close();
733        }       
734        /**
735         * Read the contents of a  a URL and then sends
736         * the data to a OutputStream object.
737         * 
738         * @param url URL of item to be copied
739         * @param output OutputStream object where data is to be written
740         * @throws java.io.IOException
741         * 
742         */
743        public static void getBytes(URL url, OutputStream output)
744        throws IOException
745        {
746                boolean success = false;
747                int tries = 0;
748                int totalLength = 0;
749                URLConnection connection = null;
750                java.io.InputStream input = null;
751                byte[] buffer = new byte[32768];
752                long startTime = System.currentTimeMillis();
753                long endTime = startTime + combinedTime;
754                long currentTime = System.currentTimeMillis();
755                for (int i = 0; i < 4; i++) 
756                {
757                        if (input != null)
758                        {
759                                input = null; 
760                        }
761                        if (connection != null)
762                        {
763                                connection = null;
764                        }
765                        input = null;
766                        try {
767                                connection = url.openConnection();
768                                connection.setDoInput(true);
769                                connection.setDoOutput(false);
770                                connection.setReadTimeout(readTimeout * power(i));
771                                connection.setConnectTimeout(connectTimeout * power(i));
772                                showProperties(connection);
773                                connection.connect();
774                                input = connection.getInputStream();
775
776                                while (true) {
777                                        /* 
778                                         * The read statement blocks until there
779                                         * are characters available to be read.
780                                         * 
781                                         * I thought that using 
782                                         * URLConnection.setReadTimeout
783                                         * would result in an exception if the
784                                         * time out was exceeded but it doesn't seem
785                                         * to be happening.
786                                         */
787                                        int length = input.read(buffer);
788                                        if (length < 0) {
789                                                break;
790                                        }
791
792                                        output.write(buffer, 0, length);
793                                        totalLength = totalLength + length;
794                                        Thread.yield();
795                                        if (debugLevel > 0) {
796                                                System.out.print("*");
797                                        }
798                                        currentTime = System.currentTimeMillis();
799                                        if (currentTime > endTime) {
800                                                throw new java.io.IOException("Time limit of "
801                                                                + Long.toString(combinedTime)
802                                                                + " milliseconds exceeded "
803                                                                + "in getBytes(URL,File)");
804                                        }
805                                } /*  End of while loop */
806
807                                input.close();
808                                if (output != null) 
809                                {
810                                        output.close(); 
811                                }
812                                success = true;
813                        } 
814                        catch (java.io.IOException e) 
815                        {
816                                if (Thread.currentThread().isInterrupted()) {
817                                        System.out.println("***** Interrupted");
818                                        Thread.yield();
819                                        return;
820                                }
821                                System.out.println("Error in getBytes: "
822                                                + e.getClass().getName() + " " + e.getMessage());
823                                System.out.println("     Number of bytes moved: "
824                                                + Integer.toString(totalLength));
825                                if (i == 3)
826                                {
827                                        throw e;
828                                }
829                        }
830                        tries = i + 1;
831                        if (success) { break; }
832                }
833                if (!success)
834                {
835                        System.out.println("Failure after " + Integer.toString(tries) + " tries"); 
836                }
837                else if (success && tries > 1)
838                {
839                        System.out.println("Success after " + Integer.toString(tries) + " tries");
840                }
841                if (debugLevel > 0)
842                { System.out.print("      " + Integer.toString(totalLength) + " bytes "); }
843                if (currentTime - startTime > 30000)
844                {
845                        System.out.println(Long.toString(currentTime - startTime) +
846                                        " milliseconds for " + Integer.toString(totalLength) +
847                        " bytes");
848                }
849        }
850        /**
851         * Copy a file.
852         * 
853         *    
854         * @param input File object for file to be copied
855         * @param output File object for copy of file to be created
856         * @throws IOException
857         * @see FileOutputStream
858         */
859        public static void copyFile (File input, File output)
860        throws IOException
861        {
862                boolean test;
863                if (!input.exists())
864                {
865                        throw new IOException("copyFile: input file does not exist");
866                }
867                if (!input.canRead())
868                {
869                        throw new IOException("copyFile: unable to read input file");
870                }
871                if (!output.getParentFile().exists())
872                {
873                        System.out.println("Creating directory " + output.getParent());
874                        fixDirs(output);
875                }
876                if (!output.exists())
877                {
878                        test = output.createNewFile();
879                        if (!test)
880                        {
881                                System.out.println("Error creating new file");
882                                throw new IOException("Unable to create new file");
883                        }
884                }
885                if (!output.canWrite())
886                {
887                        throw new IOException("copyFile: unable to write output file");
888                }
889                if (debugLevel > 1)
890                {
891                        System.out.println("copyFile(File,File): Starting copyFile");
892                        System.out.println("*** " + input.getName() + " exists: " + Boolean.toString(input.exists()));
893                        System.out.println("***     " + input.getCanonicalPath());
894                        System.out.println("***     length:    " + Long.toString(input.length()));
895                        System.out.println("***     canRead(): " + Boolean.toString(input.canRead()));
896                        System.out.println("*** " + output.getName() + " exists: " + Boolean.toString(output.exists()));
897                }
898                FileOutputStream outputStream = new FileOutputStream(output);
899                getBytes(input, outputStream);
900                outputStream.close();
901        }
902        /**
903         * Copy a file.
904         * 
905         * @param inputFileName Name of file to be copied
906         * @param outputFileName Name of copy of file to be created
907         * @throws IOException
908         * @see  bradleyross.library.helpers.tests.TestCopyFile
909         */
910        public static void copyFile (String inputFileName, String outputFileName)
911        throws IOException
912        {
913                File input = new File(inputFileName);
914                File output = new File(outputFileName);
915                copyFile(input, output);
916        }
917        /**
918         * Checks to see if directories for a file exist, and then
919         * creates directories as appropriate.
920         * @param newFile File object to be created
921         * @throws java.io.IOException
922         * @see File
923         */
924        public static void fixDirs(File newFile)
925        throws java.io.IOException
926        {
927                try
928                {
929                        File directory = newFile.getParentFile();
930                        if (directory == null) { return; }
931                        if (directory.exists()) 
932                        { return; }
933                        else
934                        { 
935                                directory.mkdirs();
936                                return;
937                        }
938                }
939                catch (Exception e)
940                {
941                        System.out.println("Error in fixDirs: unable to create directories " +
942                                        "for " + newFile.toString());
943                        System.out.println(e.getClass().getName() + " " +
944                                        e.getMessage());
945                        e.printStackTrace(System.out);
946                        throw new java.io.IOException("Unable to create directories for " +
947                                        newFile.toString() + "  " +
948                                        e.getClass().getName() + " " + e.getMessage());
949                }
950        }       
951
952        /**
953         * Checks to see if directories for a file exist, and then
954         * creates directories as appropriate.
955         * @param name Name of file or directory to be created
956         * @throws java.io.IOException
957         * @see File
958         */
959        public static void fixDirs(String name)
960        throws IOException
961        {
962                try
963                {
964                        File newFile = new File(name);
965                        File directory = newFile.getParentFile();
966                        if (directory == null) { return; }
967                        if (directory.exists()) 
968                        { return; }
969                        else
970                        { 
971                                directory.mkdirs();
972                                return;
973                        }
974                }
975                catch (Exception e)
976                {
977                        System.out.println("Error in fixDirs: unable to create directories " +
978                                        "for " + name);
979                        System.out.println(e.getClass().getName() + " " +
980                                        e.getMessage());
981                        e.printStackTrace(System.out);
982                        throw new java.io.IOException("Unable to create directories for " +
983                                        e.getClass().getName() + " " + e.getMessage());
984                }
985        }
986        /**
987         * Send properties for connection to System.out
988         * <p>This is intended for diagnostic purposes.
989         * @param connection Connection object
990         */
991        public static void showProperties(URLConnection connection)
992        {
993                if (debugLevel <= 0) { return; }
994                Map<String,List<String>> properties = connection.getRequestProperties();
995                System.out.println("There are " + Integer.toString(properties.size()) + " properties");
996                Object keySet[] = properties.keySet().toArray();
997                for (int i = 0; i < keySet.length; i++)
998                {
999                        System.out.println((String) keySet[i]);
1000                }
1001        }
1002        /**
1003         * This is a test driver to check out the copyFile method.
1004         * 
1005         * @param args Names of original file and copy to be created
1006         */
1007        public static void main (String[] args)
1008        {
1009                String test = "1, 2, alpha, beta, gamma , ,,,";
1010                String tokens[] = lineSplitterCommas(test);
1011                System.out.println("There are " + Integer.toString(tokens.length) + " items");
1012                for (int i = 0; i < tokens.length; i++)
1013                {
1014                        System.out.print("*" + tokens[i] + "  ");
1015                }
1016                System.out.println();
1017                /**
1018                if (args.length < 2)
1019                {
1020                        System.out.println("Not enough arguments");
1021                }
1022                try 
1023                {
1024                        System.out.println("Copying " + args[0] + " to " + args[1]);
1025                        copyFile(args[0], args[1]);
1026                } 
1027                catch (IOException e) 
1028                {
1029                        System.out.println("Error in running driver");
1030                        System.out.println(e.getClass().getName() + " " + 
1031                                        e.getMessage());
1032                        e.printStackTrace();
1033                }
1034                 */
1035        }
1036        /**
1037         * Split a line into tokens using spaces as the field separators.
1038         *
1039         * <p>This method can be used as an example of processing regular
1040         *    expressions in Java.</p>
1041         * @param input Line to be split into tokens
1042         * @return Array of strings containing the tokens.
1043         */
1044        public static String[] lineSplitterSpaces (String input)
1045        {
1046                java.util.regex.Pattern pattern = java.util.regex.Pattern.compile ("\\s+");
1047                java.util.regex.Matcher matcher = pattern.matcher(input.trim());
1048                String result = matcher.replaceAll(" ");
1049                return result.split(" ");
1050        }       
1051        /**
1052         * Split a line into tokens using commas as the field separators.
1053         *
1054         * <p>The space is suffixed to the input parameter because the system
1055         *    doesn't seem to recognize a comma as the last character in the
1056         *    string as signifying a new token.</p>
1057         * @param input Line to be split into tokens
1058         * @return Array of strings containing the tokens.
1059         */
1060        public static String[] lineSplitterCommas (String input)
1061        {
1062                String tokens[] = (input + " ").split(",");
1063                for (int i = 0; i < tokens.length; i ++)
1064                {
1065                        tokens[i] = tokens[i].trim();
1066                }
1067                return  tokens;
1068        }
1069        /**
1070         * Create a file containing a thumbnail image from another file.
1071         * @param inputFile Name of file to be read
1072         * @param outputFile Name of file to be generated
1073         * @param sizeX Width of generated image in pixels
1074         * @param sizeY Height of generated image in pixels
1075         * @see BufferedImage
1076         * @see javax.imageio.ImageIO
1077         * @see java.awt.image.RenderedImage
1078         */
1079        public static void thumbnail(String inputFile, String outputFile, int sizeX, int sizeY) throws IOException {
1080                thumbnail(new File(inputFile), new File(outputFile), sizeX, sizeY);
1081        }
1082        /**
1083         * Create a file containing a thumbnail image from another file.
1084         * @param input  file to be read
1085         * @param output  file to be generated
1086         * @param sizeX Width of generated image in pixels
1087         * @param sizeY Height of generated image in pixels
1088         * @see BufferedImage
1089         * @see javax.imageio.ImageIO
1090         * @see java.awt.image.RenderedImage
1091         */
1092        public static void thumbnail(File input, File output, int sizeX, int sizeY) throws IOException
1093        {
1094                if (debugLevel > 2)
1095                {
1096                        System.out.println("Arguments: " + input.getCanonicalPath() + ", " + output.getCanonicalPath() + ", " +
1097                                        Integer.toString(sizeX) + ", " + Integer.toString(sizeY));
1098                }
1099                Integer targetX = 0;
1100                Integer targetY = 0;
1101                if (sizeX <= 0 && sizeY <= 0)
1102                {
1103                        throw new IOException("Either target width or target height must be positive");
1104                }
1105                else if (sizeX == 0 || sizeY == 0)
1106                {
1107                        throw new IOException("Zero not acceptable for target width or target height");
1108                }
1109                String inputFileName = input.getCanonicalPath();
1110                BufferedImage image = null;
1111                BufferedImage thumb = null;
1112                Image intermediate = null;
1113                String format = null;
1114                String suffix = null;
1115                FileOutputStream outputStream = null;
1116                String outputName = output.getName();
1117                suffix = outputName.substring(outputName.lastIndexOf(".") + 1);
1118                if (suffix == null)
1119                {
1120                        throw new IOException("No suffix found for output file " +
1121                                        outputName + " (null value)");
1122                }
1123                else if (suffix.equalsIgnoreCase("jpg") || suffix.equalsIgnoreCase("jpeg"))
1124                { format = "jpeg"; }
1125                else if (suffix.equalsIgnoreCase("png"))
1126                { format = "png"; }
1127                else if (suffix.equalsIgnoreCase("gif"))
1128                { format = "gif"; }
1129                else
1130                { throw new IOException("Unable to process suffix " + suffix + 
1131                                " for file " + outputName); }
1132                try 
1133                {
1134                        image = javax.imageio.ImageIO.read(new FileInputStream(input));
1135                } 
1136                catch (FileNotFoundException e) 
1137                {
1138                        throw new IOException("Unable to find file " + inputFileName + " " +
1139                                        e.getMessage());
1140                } 
1141                catch (IOException e) 
1142                {
1143                        throw new IOException("Unexpected error while reading file " + inputFileName + " " +
1144                                        e.getMessage());
1145                }
1146                if (sizeX <= 0 || sizeY <= 0)
1147                {
1148                        if (debugLevel > 2)
1149                        {
1150                                System.out.println("Must adjust one parameter");
1151                        }
1152                        Double ratio = (double) image.getWidth() / (double) image.getHeight();
1153                        if (sizeX < 0)
1154                        {
1155                                targetY = sizeY;
1156                                targetX = (int) (Math.floor((double) sizeY * ratio));
1157                        }
1158                        else if (sizeY < 0)
1159                        {
1160                                targetX = sizeX;
1161                                targetY = (int) (Math.floor((double) sizeX / ratio));
1162                        }
1163                }
1164                else if (sizeX > 0 && sizeY > 0)
1165                {
1166                        targetX = sizeX;
1167                        targetY = sizeY;
1168                }
1169                intermediate = image.getScaledInstance(targetX, targetY, BufferedImage.SCALE_FAST);
1170                thumb = new BufferedImage(targetX, targetY, BufferedImage.TYPE_INT_RGB);
1171                Graphics g = thumb.createGraphics();
1172                g.drawImage(intermediate, 0, 0, null);
1173                outputStream = new FileOutputStream(output);
1174                ImageIO.write(thumb, format, outputStream);
1175        }
1176        /**
1177         * Makes file with thumbnail image
1178         * @param input Input file
1179         * @param output Output file
1180         * @param size Height and width of output image in pixels
1181         * @throws IOException
1182         */
1183        public static void thumbnail(String input, String output, int size) throws IOException
1184        {
1185                thumbnail(new File(input), new File(output), size, size);
1186        }
1187        /**
1188         * Makes file with thumbnail image
1189         * @param input Input file
1190         * @param output Output file
1191         * @throws IOException
1192         */
1193        public static void thumbnail(String input, String output) throws IOException
1194        {
1195                thumbnail(new File(input), new File(output), 150, 150);
1196        }
1197}