package liquibase.util; import java.io.*; import java.nio.charset.Charset; import java.util.Set; import liquibase.changelog.ChangeSet; import liquibase.configuration.GlobalConfiguration; import liquibase.configuration.LiquibaseConfiguration; import liquibase.exception.UnexpectedLiquibaseException; import liquibase.logging.LogFactory; import liquibase.resource.ResourceAccessor; import liquibase.resource.UtfBomAwareReader; /** * Utilities for working with streams. */ public class StreamUtil { public static String getLineSeparator() { return LiquibaseConfiguration.getInstance().getConfiguration(GlobalConfiguration.class).getOutputLineSeparator(); } /** * Reads a stream until the end of file into a String and uses the machines * default encoding to convert to characters the bytes from the Stream. * * @param ins The InputStream to read. * @return The contents of the input stream as a String * @throws IOException If there is an error reading the stream. */ public static String getStreamContents(InputStream ins) throws IOException { return getReaderContents(new UtfBomAwareReader(ins)); } /** * Reads a stream until the end of file into a String and uses the machines * default encoding to convert to characters the bytes from the Stream. * * @param ins The InputStream to read. * @param charsetName The name of a supported {@link java.nio.charset.Charset </code>charset<code>} * @return The contents of the input stream as a String * @throws IOException If there is an error reading the stream. */ public static String getStreamContents(InputStream ins, String charsetName) throws IOException { UtfBomAwareReader reader; if (ins == null) { throw new IOException("No stream to open"); } try { if (charsetName == null) { reader = new UtfBomAwareReader(ins); } else { String charsetCanonicalName = Charset.forName(charsetName).name(); String encoding; reader = new UtfBomAwareReader(ins, charsetName); encoding = Charset.forName(reader.getEncoding()).name(); if (charsetCanonicalName.startsWith("UTF") && !charsetCanonicalName.equals(encoding)) { reader.close(); throw new IllegalArgumentException("Expected encoding was '" + charsetCanonicalName + "' but a BOM was detected for '" + encoding + "'"); } } return getReaderContents(reader); } finally { ins.close(); } } /** * Reads all the characters into a String. * * @param reader The Reader to read. * @return The contents of the input stream as a String * @throws IOException If there is an error reading the stream. */ public static String getReaderContents(Reader reader) throws IOException { try { StringBuffer result = new StringBuffer(); char[] buffer = new char[2048]; int read; while ((read = reader.read(buffer)) > -1) { result.append(buffer, 0, read); } return result.toString(); } finally { closeQuietly(reader); } } public static void copy(InputStream inputStream, OutputStream outputStream) throws IOException { byte[] bytes = new byte[1024]; int r = inputStream.read(bytes); while (r > 0) { outputStream.write(bytes, 0, r); r = inputStream.read(bytes); } } public static long getContentLength(InputStream in) throws IOException { long length = 0; byte[] buf = new byte[4096]; int bytesRead = in.read(buf); while (bytesRead > 0) { length += bytesRead; bytesRead = in.read(buf); } return length; } public static long getContentLength(Reader reader) throws IOException { long length = 0; char[] buf = new char[2048]; int charsRead = reader.read(buf); while (charsRead > 0) { length += charsRead; charsRead = reader.read(buf); } return length; } public static void closeQuietly(Reader input) { closeQuietly((Closeable) input); } public static void closeQuietly(InputStream input) { closeQuietly((Closeable) input); } public static void closeQuietly(Closeable input) { try { if (input != null) { input.close(); } } catch (IOException ioe) { // ignore } } public static InputStream openStream(String path, Boolean relativeToChangelogFile, ChangeSet changeSet, ResourceAccessor resourceAccessor) throws IOException { InputStream stream = openFromClasspath(path, relativeToChangelogFile, changeSet, resourceAccessor); if (stream == null) { stream = openFromFileSystem(path, relativeToChangelogFile, changeSet, resourceAccessor); } return stream; } /** * Tries to load the file from the file system. * * @param file The name of the file to search for * @return True if the file was found, false otherwise. */ private static InputStream openFromFileSystem(String file, Boolean relativeToChangelogFile, ChangeSet changeSet, ResourceAccessor resourceAccessor) throws IOException { if (resourceAccessor == null) { return null; } if (relativeToChangelogFile != null && relativeToChangelogFile) { String base; if (changeSet.getChangeLog() == null) { base = changeSet.getFilePath(); } else { base = changeSet.getChangeLog().getPhysicalFilePath().replaceAll("\\\\","/"); } if (!base.contains("/")) { base = "."; } file = base.replaceFirst("/[^/]*$", "") + "/" + file; } return singleInputStream(file, resourceAccessor); } public static InputStream singleInputStream(String path, ResourceAccessor resourceAccessor) throws IOException { Set<InputStream> streams = resourceAccessor.getResourcesAsStream(path); if (streams == null || streams.size() == 0) { return null; } if (streams.size() != 1) { if (streams.size() > 1 && path != null && path.startsWith("liquibase/parser/core/xml/") && path.endsWith(".xsd")) { LogFactory.getLogger().debug("Found " + streams.size() + " files that match " + path+", but choosing one at random."); InputStream returnStream = null; for (InputStream stream : streams) { if (returnStream == null) { returnStream = stream; } else { stream.close(); } } } else { for (InputStream stream : streams) { stream.close(); } throw new IOException("Found " + streams.size() + " files that match " + path); } } return streams.iterator().next(); } /** * Tries to load a file using the FileOpener. * <p/> * If the fileOpener can not be found then the attempt to load from the * classpath the return is false. * * @param file The file name to try and find. * @return True if the file was found and loaded, false otherwise. */ private static InputStream openFromClasspath(String file, Boolean relativeToChangelogFile, ChangeSet changeSet, ResourceAccessor resourceAccessor) throws IOException { if (resourceAccessor == null) { return null; } if (relativeToChangelogFile != null && relativeToChangelogFile) { String base; if (changeSet.getChangeLog() == null) { base = changeSet.getFilePath(); } else { base = changeSet.getChangeLog().getPhysicalFilePath().replaceAll("\\\\","/"); } if (base == null || !base.contains("/")) { base = "."; } file = base.replaceFirst("/[^/]*$", "") + "/" + file; } return singleInputStream(file, resourceAccessor); } }