Java tutorial
/* * TV-Browser * Copyright (C) 04-2003 Martin Oberhauser (martin_oat@yahoo.de) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * CVS information: * $RCSfile$ * $Source$ * $Date: 2010-11-21 15:38:33 +0100 (Sun, 21 Nov 2010) $ * $Author: bananeweizen $ * $Revision: 6835 $ */ package util.io; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.Reader; import java.io.Writer; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLConnection; import java.util.Calendar; import java.util.logging.Logger; import java.util.zip.GZIPInputStream; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import javax.imageio.ImageIO; import javax.swing.ImageIcon; import org.apache.commons.codec.binary.Base64; import org.apache.commons.lang.StringUtils; import tvbrowser.core.Settings; import util.ui.TimeFormatter; /** * A utilities class for I/O stuff. It constists of serveral static * methods that perform some usefull things. * * @author Til Schneider, www.murfman.de */ public class IOUtilities { /** The logger for this class. */ private static final Logger mLog = Logger.getLogger(IOUtilities.class.getName()); /** A Calendar for time stuff */ private static final Calendar CALENDAR = Calendar.getInstance(); /** Formatting the Time */ private static final TimeFormatter TIME_FORMATTER = new TimeFormatter(); /** * Downloads a file from a HTTP server. * * @param url The URL of the file to download. * @param targetFile The file where to store the downloaded data. * @throws IOException When download or saving failed. * @see #loadFileFromHttpServer(URL) */ public static void download(URL url, File targetFile) throws IOException { mLog.info("Downloading '" + url + "' to '" + targetFile.getAbsolutePath() + "'"); InputStream stream = null; try { stream = new BufferedInputStream(getStream(url), 0x4000); if (stream == null) { throw new IOException("Can't connect to '" + url + "'!"); } saveStream(stream, targetFile); } finally { try { if (stream != null) { stream.close(); } } catch (IOException exc) { } } } /** * Saves the data from an InputStream into a file. * * @param stream The stream to read the data from. * @param targetFile The file where to store the data. * @throws IOException When saving failed or when the InputStream throws an * IOException. */ public static void saveStream(InputStream stream, File targetFile) throws IOException { BufferedOutputStream out = null; try { out = new BufferedOutputStream(new FileOutputStream(targetFile), 0x4000); pipeStreams(stream, out); } finally { try { if (out != null) { out.close(); } } catch (IOException exc) { } } } /** * Gets an InputStream to the given URL. * <p> * The connection has the Settings.propDefaultNetworkConnectionTimeout * as connection timeout. * * @param page The page to get the stream to. * @param followRedirects If the stream should be also established if the page not exists * at the location but contains a redirect to an other location. * @return The stream to the page. * @throws IOException Thrown if something goes wrong. */ public static InputStream getStream(URL page, boolean followRedirects) throws IOException { return getStream(page, followRedirects, Settings.propDefaultNetworkConnectionTimeout.getInt()); } /** * Originally copied from javax.swing.JEditorPane. * <p> * Fetches a stream for the given URL, which is about to * be loaded by the <code>setPage</code> method. By * default, this simply opens the URL and returns the * stream. This can be reimplemented to do useful things * like fetch the stream from a cache, monitor the progress * of the stream, etc. * <p> * This method is expected to have the the side effect of * establishing the content type, and therefore setting the * appropriate <code>EditorKit</code> to use for loading the stream. * <p> * If this the stream was an http connection, redirects * will be followed and the resulting URL will be set as * the <code>Document.StreamDescriptionProperty</code> so that relative * URL's can be properly resolved. * * @param page the URL of the page * @param followRedirects Follow redirects. * @param timeout The read timeout. * @throws IOException if something went wrong. * @return a stream reading data from the specified URL. */ public static InputStream getStream(URL page, boolean followRedirects, int timeout) throws IOException { return getStream(page, followRedirects, timeout, null, null); } /** * Originally copied from javax.swing.JEditorPane. * <p> * Fetches a stream for the given URL, which is about to * be loaded by the <code>setPage</code> method. By * default, this simply opens the URL and returns the * stream. This can be reimplemented to do useful things * like fetch the stream from a cache, monitor the progress * of the stream, etc. * <p> * This method is expected to have the the side effect of * establishing the content type, and therefore setting the * appropriate <code>EditorKit</code> to use for loading the stream. * <p> * If this the stream was an http connection, redirects * will be followed and the resulting URL will be set as * the <code>Document.StreamDescriptionProperty</code> so that relative * URL's can be properly resolved. * * @param page the URL of the page * @param followRedirects Follow redirects. * @param timeout The read timeout. * @param userName The user name to use for the connection. * @param userPassword The password to use for the connection. * @throws IOException if something went wrong. * @return a stream reading data from the specified URL. */ public static InputStream getStream(URL page, boolean followRedirects, int timeout, String userName, String userPassword) throws IOException { URLConnection conn = page.openConnection(); if (userName != null && userPassword != null) { String password = userName + ":" + userPassword; String encodedPassword = new String(Base64.encodeBase64(password.getBytes())); conn.setRequestProperty("Authorization", "Basic " + encodedPassword); } if (timeout > 0) { conn.setReadTimeout(timeout); } if (followRedirects && (conn instanceof HttpURLConnection)) { HttpURLConnection hconn = (HttpURLConnection) conn; hconn.setInstanceFollowRedirects(false); int response = hconn.getResponseCode(); boolean redirect = (response >= 300 && response <= 399); // In the case of a redirect, we want to actually change the URL // that was input to the new, redirected URL if (redirect) { String loc = conn.getHeaderField("Location"); if (loc == null) { throw new FileNotFoundException( "URL points to a redirect without " + "target location: " + page); } if (loc.startsWith("http")) { page = new URL(loc); } else { page = new URL(page, loc); } return getStream(page, followRedirects, timeout, userName, userPassword); } } InputStream in = conn.getInputStream(); return in; } /** * Gets an InputStream to the given URL. * <p> * The connection has the Settings.propDefaultNetworkConnectionTimeout * as connection timeout. * * @param page The page to get the stream to. * @return The stream to the page. * @throws IOException Thrown if something goes wrong. */ public static InputStream getStream(URL page) throws IOException { return getStream(page, true, Settings.propDefaultNetworkConnectionTimeout.getInt()); } /** * Gets an InputStream to the given URL. * <p> * The connection has the given timeout * as connection timeout. * * @param page The page to get the stream to. * @param timeout The timeout for the connection, use 0 for no timeout. * @return The stream to the page. * @throws IOException Thrown if something goes wrong. */ public static InputStream getStream(URL page, int timeout) throws IOException { return getStream(page, true, timeout); } /** * Loads a file from a Http server. * <p> * The connection has the Settings.propDefaultNetworkConnectionTimeout * as connection timeout. * * @param url The URL of the file * @return The content of the file * @throws IOException When the download failed * @see #download(URL, File) */ public static byte[] loadFileFromHttpServer(URL url) throws IOException { return loadFileFromHttpServer(url, Settings.propDefaultNetworkConnectionTimeout.getInt()); } /** * Loads a file from a Http server with the given * read timeout. * * @param url The URL of the file * @param timeout The read timeout for the connection. * @return The content of the file * @throws IOException When the download failed * @see #download(URL, File) */ public static byte[] loadFileFromHttpServer(URL url, int timeout) throws IOException { InputStream in = null; try { in = IOUtilities.getStream(url, timeout); ByteArrayOutputStream out = new ByteArrayOutputStream(); pipeStreams(in, out); out.close(); return out.toByteArray(); } finally { if (in != null) { try { in.close(); } catch (IOException exc) { } } } } /** * Pipes all data from the specified InputStream to the specified OutputStream, * until the InputStream has no more data. * <p> * Note: None of the streams is closed! You have to do that for yourself! * * @param from The stream to read the data from. * @param to The stream to write the data to. * @throws IOException Thrown if something goes wrong. */ public static void pipeStreams(InputStream from, OutputStream to) throws IOException { int len; byte[] buffer = new byte[10240]; while ((len = (from.read(buffer))) != -1) { to.write(buffer, 0, len); } } /** * Pipes all data from the specified Reader to the specified Writer, * until the Reader has no more data. * <p> * Note: The Reader and the Writer are not closed! You have to do that for * yourself! * * @param reader The Reader to read the data from. * @param writer The Writer to write the data to. * @throws IOException Thrown if something goes wrong. */ public static void pipe(Reader reader, Writer writer) throws IOException { int len; char[] buffer = new char[10240]; while ((len = (reader.read(buffer))) != -1) { writer.write(buffer, 0, len); } } private static void copyFile(File src, File targetDir, boolean onlyNew) throws IOException { File destFile = new File(targetDir, src.getName()); copy(src, destFile, onlyNew); } private static File createDirectory(File targetDir, String dirName) throws IOException { File f = new File(targetDir.getAbsolutePath() + "/" + dirName); if (!f.exists()) { if (!f.mkdirs()) { throw new IOException("Could not create directory '" + f.getAbsolutePath() + "'"); } } return f; } /** * Copies files given in source to the target directory. * * @param src The files to copy. * @param targetDir The target dir of the files. * @throws IOException Thrown if something goes wrong. */ public static void copy(File[] src, File targetDir) throws IOException { copy(src, targetDir, false); } /** * Copies files given in source to the target directory. * * @param src The files to copy. * @param targetDir The target dir of the files. * @param onlyNew Overwrite only older files. * @throws IOException Thrown if something goes wrong. * @since 2.2.2/2.5.1 */ public static void copy(File[] src, File targetDir, boolean onlyNew) throws IOException { copy(targetDir, src, targetDir, onlyNew); } private static void copy(File firstTargetDir, File[] src, File targetDir, boolean onlyNew) throws IOException { // src might be null, if listFiles wasn't able to read the directory if (src == null) { return; } for (int i = 0; i < src.length; i++) { if (src[i].isDirectory() && !src[i].equals(targetDir) && !src[i].equals(firstTargetDir)) { File newDir = createDirectory(targetDir, src[i].getName()); copy(firstTargetDir, src[i].listFiles(), newDir, onlyNew); } else { copyFile(src[i], targetDir, onlyNew); } } } /** * Copies a file. * * @param src The file to read from * @param target The file to write to * @throws IOException If copying failed */ public static void copy(File src, File target) throws IOException { copy(src, target, false); } /** * Copies a file. * * @param src The file to read from * @param target The file to write to * @param onlyNew Overwrite only older files. * @throws IOException If copying failed * @since 2.2.2/2.5.1 */ public static void copy(File src, File target, boolean onlyNew) throws IOException { BufferedInputStream in = null; BufferedOutputStream out = null; try { FileOutputStream outFile = new FileOutputStream(target); in = new BufferedInputStream(new FileInputStream(src), 0x4000); out = new BufferedOutputStream(outFile, 0x4000); if (!onlyNew || target.length() < 1 || (src.lastModified() > target.lastModified())) { outFile.getChannel().truncate(0); pipeStreams(in, out); } in.close(); out.close(); } finally { if (in != null) { try { in.close(); } catch (IOException exc) { } } if (out != null) { try { out.close(); } catch (IOException exc) { } } } } /** * Deletes a directory with all its files and subdirectories. * * @param dir The directory to delete. * @throws IOException Thrown if something goes wrong. */ public static void deleteDirectory(File dir) throws IOException { if (!dir.exists()) { // Nothing to do return; } if (!dir.isDirectory()) { throw new IOException("File is not a directory: " + dir.getAbsolutePath()); } // Delete all the files and subdirectories File[] fileArr = dir.listFiles(); if (fileArr != null) { for (int i = 0; i < fileArr.length; i++) { if (fileArr[i].isDirectory()) { deleteDirectory(fileArr[i]); } else { if (!fileArr[i].delete()) { throw new IOException("Can't delete file: " + fileArr[i].getAbsolutePath()); } } } } // Delete the directory if (!dir.delete()) { throw new IOException("Can't delete directory: " + dir.getAbsolutePath()); } } /** * Ldt eine Datei aus einem Jar-File und gibt sie zurck. * <P> * Ist keine Datei mit diesem Namen im Jar-File, so wird versucht, sie vom * Dateisystem zu laden. * * @param fileName Der Name der Datei. (Ist case-sensitive!). * @param srcClass Eine Klasse, aus deren Jar-File das Image geladen werden soll. * @return The file loaded from the jar file as byte array. * * @throws IOException Wenn ein Fehler beim Laden der Datei auftrat. */ public static byte[] loadFileFromJar(String fileName, Class srcClass) throws IOException { if (fileName == null) { throw new IllegalArgumentException("fileName == null"); } if (StringUtils.isEmpty(fileName)) { throw new IllegalArgumentException("fileName is empty"); // if (srcClass == null) srcClass = IOUtilities.class; } // Der Dateiname muss mit einem '/' anfangen, sonst wird er nicht gefunden. InputStream in; if (srcClass == null) { in = new java.io.FileInputStream(fileName); } else { if ((fileName.charAt(0) != '/') && (fileName.charAt(0) != '\\')) { fileName = "/" + fileName; } in = srcClass.getResourceAsStream(fileName); } if (in == null) { throw new IOException("Resource not found: '" + fileName + "'"); } byte[] buffer = new byte[10240]; byte[] data = new byte[0]; int len; while ((len = in.read(buffer)) != -1) { // data Array vergrern byte[] oldData = data; data = new byte[oldData.length + len]; System.arraycopy(oldData, 0, data, 0, oldData.length); // Gerade gelesene Daten anhngen System.arraycopy(buffer, 0, data, oldData.length, len); } in.close(); return data; } /** * Unzips a file from a ZIP-Archive (.zip, .jar). * <p> * Currently not used. * * @param srcFile The ZIP-File to read the data from. * @param entryName The name of the file in the ZIP-archive to unzip. * @param targetFile The file where to store the data. * @throws IOException Thrown if something goes wrong. */ public static void unzip(File srcFile, String entryName, File targetFile) throws IOException { mLog.info("Unzipping '" + entryName + "' from '" + srcFile.getAbsolutePath() + "' to '" + targetFile.getAbsolutePath() + "'"); InputStream stream = null; try { ZipFile zipFile = new ZipFile(srcFile); ZipEntry entry = new ZipEntry(entryName); stream = zipFile.getInputStream(entry); if (stream == null) { throw new IOException("Can't unzip '" + entryName + "' from '" + srcFile.getAbsolutePath() + "'!"); } saveStream(stream, targetFile); } finally { try { if (stream != null) { stream.close(); } } catch (IOException exc) { } } } /** * Unzips a GZIP-file (.gz). * * @param srcFile The GZIP-File to unzip * @param targetFile The file where to store the data. * @throws IOException Thrown if something goes wrong. */ public static void ungzip(File srcFile, File targetFile) throws IOException { mLog.info("Ungzipping '" + srcFile.getAbsolutePath() + "' to '" + targetFile.getAbsolutePath() + "'"); InputStream stream = null; InputStream gzipStream = null; try { stream = new BufferedInputStream(new FileInputStream(srcFile), 0x4000); gzipStream = openSaveGZipInputStream(stream); saveStream(gzipStream, targetFile); } finally { try { if (gzipStream != null) { gzipStream.close(); } if (stream != null) { stream.close(); } } catch (IOException exc) { } } } /** * Appends an integer to a StringBuffer. If the length of the integer's * String representation is smaller than minChars, the missing chars will be * filled as nulls ('0') as postfix. * * @param buffer The buffer where to append the integer. * @param number The integer to append. * @param minChars The minimum number of chars. */ public static void append(StringBuffer buffer, int number, int minChars) { String asString = Integer.toString(number); for (int i = asString.length(); i < minChars; i++) { buffer.append('0'); } buffer.append(asString); } /** * Replaces a substring in the specified String. * * @param original The String to replace the substring in. * @param pattern The pattern to replace. * @param str The String to replace. This string may contain the pattern. * @return The result. */ public static String replace(String original, String pattern, String str) { StringBuffer buffer = new StringBuffer(original); replace(buffer, pattern, str); return buffer.toString(); } /** * Replaces in <code>buffer</code> the <code>pattern</code> by <code>str</code>. * * @param buffer The buffer to replace in. * @param pattern The pattern to replace. * @param str The str that should replace the pattern. */ public static void replace(StringBuffer buffer, String pattern, String str) { int offset = 0; int patternIdx; do { patternIdx = buffer.indexOf(pattern, offset); if (patternIdx != -1) { buffer.replace(patternIdx, patternIdx + pattern.length(), str); } offset = patternIdx + str.length(); } while (patternIdx != -1); } /** * Clears an StringBuffer * * @param buffer The buffer to clear */ public static void clear(StringBuffer buffer) { buffer.delete(0, buffer.length()); } /** * Gets the number of minutes since midnight * <p> * This method does not create any objects. * * @return The number of minutes since midnight as integer */ public static int getMinutesAfterMidnight() { synchronized (CALENDAR) { CALENDAR.setTimeInMillis(System.currentTimeMillis()); return CALENDAR.get(Calendar.HOUR_OF_DAY) * 60 + CALENDAR.get(Calendar.MINUTE); } } /** * Gets a String representation in the format h:mm for a time in minutes after * midnight. * * @param minutesAfterMidnight The time to get the String for * @return A String for the time */ public static String timeToString(int minutesAfterMidnight) { minutesAfterMidnight = minutesAfterMidnight % (24 * 60); int hours = minutesAfterMidnight / 60; int minutes = minutesAfterMidnight % 60; return TIME_FORMATTER.formatTime(hours, minutes); } /** * Encodes the specified String using a simple XOR encryption. * * @param text The text to encode * @param seed The seed of the Random object to use for getting the keys * @return The encoded String */ public static String xorEncode(String text, long seed) { java.util.Random rnd = new java.util.Random(seed); char[] charArr = new char[text.length()]; for (int i = 0; i < charArr.length; i++) { charArr[i] = (char) (text.charAt(i) ^ rnd.nextInt()); // XOR } return new String(charArr); } /** * Decodes the specified String using a simple XOR encryption. * * @param text The text to encode * @param seed The seed of the Random object to use for getting the keys * @return The decoded String */ public static String xorDecode(String text, long seed) { // We use XOR encoding -> encoding and decoding are the same return xorEncode(text, seed); } /** * Reads a number of bytes into an array. * * @param stream The stream to read from * @param length The number of bytes to read * @return An array containing the read bytes * @throws IOException When the end of the stream has been reached or * reading failed */ public static byte[] readBinaryData(InputStream stream, int length) throws IOException { byte[] data = new byte[length]; int offset = 0; while (offset < length) { int len = stream.read(data, offset, length - offset); if (len == -1) { throw new IOException("Unexpected end of stream"); } offset += len; } return data; } /** * Reads all Bytes from a File into an byte array * * @param file Read Bytes from this File * @return Byte-Array or NULL if too large * @throws IOException Read-Exception * * @since 2.5 */ public static byte[] getBytesFromFile(File file) throws IOException { InputStream is = new FileInputStream(file); // Get the size of the file long length = file.length(); // You cannot create an array using a long type. // It needs to be an int type. // Before converting to an int type, check // to ensure that file is not larger than Integer.MAX_VALUE. if (length > Integer.MAX_VALUE) { return null; } // Create the byte array to hold the data byte[] bytes = new byte[(int) length]; // Read in the bytes int offset = 0; int numRead = 0; while (offset < bytes.length && (numRead = is.read(bytes, offset, bytes.length - offset)) >= 0) { offset += numRead; } // Ensure all the bytes have been read in if (offset < bytes.length) { throw new IOException("Could not completely read file " + file.getName()); } // Close the input stream and return bytes is.close(); return bytes; } /** * Writes the given image icon to the given file in the given imageType. * * @param icon The icon to write. * @param imageType The image type. * @param targetFile The file to write the image to. * * @return <code>True</code> if the file could be written, <code>false</code> if something went wrong. * @since 2.6 */ public static boolean writeImageIconToFile(ImageIcon icon, String imageType, File targetFile) { try { BufferedImage iconimage = new BufferedImage(icon.getIconWidth(), icon.getIconWidth(), BufferedImage.TYPE_INT_ARGB); Graphics2D g2 = iconimage.createGraphics(); icon.paintIcon(null, g2, 0, 0); g2.dispose(); ImageIO.write(iconimage, imageType, targetFile); } catch (Exception e) { return false; } return true; } /** * Read the image from the given file to an icon image. * * @param srcFile The file to read from. * * @return The read icon image or <code>null</code> if something went wrong. * @since 2.6 */ public static ImageIcon readImageIconFromFile(File srcFile) { try { return new ImageIcon(ImageIO.read(srcFile)); } catch (Exception e) { } return null; } /** * This method tries to open an inputstream as gzip and uncompresses it. If it fails, * a normal inputstream is returned * * @param is Inputstream that could be compressed * @return uncompressed inputstream * @throws IOException Problems during opening of the Stream * @since 3.0 */ public static InputStream openSaveGZipInputStream(final InputStream is) throws IOException { final BufferedInputStream bis = new BufferedInputStream(is); bis.mark(64); try { final InputStream result = new GZIPInputStream(bis); return result; } catch (final IOException e) { e.printStackTrace(); bis.reset(); return bis; } } }