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