blob: fddb1acc8376910f1e0137861b1bda53ab4f16c4 [file] [log] [blame]
/*
* Copyright 2006 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.gwt.util.tools;
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.LineNumberReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.RandomAccessFile;
import java.io.Reader;
import java.io.StringReader;
import java.io.Writer;
import java.net.Socket;
import java.net.URI;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
/**
* A smattering of useful functions.
*/
public final class Utility {
/**
* Per thread MD5 instance.
*/
private static final ThreadLocal<MessageDigest> perThreadMd5 =
new ThreadLocal<MessageDigest>() {
@Override
protected MessageDigest initialValue() {
try {
return MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
return null;
}
};
};
public static char[] HEX_CHARS = new char[] {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D',
'E', 'F'};
private static String sInstallPath = null;
/**
* Helper that ignores exceptions during close, because what are you going to
* do?
*/
public static void close(InputStream is) {
try {
if (is != null) {
is.close();
}
} catch (IOException e) {
}
}
/**
* Helper that ignores exceptions during close, because what are you going to
* do?
*/
public static void close(OutputStream os) {
try {
if (os != null) {
os.close();
}
} catch (IOException e) {
}
}
/**
* Helper that ignores exceptions during close, because what are you going to
* do?
*/
public static void close(RandomAccessFile f) {
if (f != null) {
try {
f.close();
} catch (IOException e) {
}
}
}
/**
* Helper that ignores exceptions during close, because what are you going to
* do?
*/
public static void close(Reader reader) {
try {
if (reader != null) {
reader.close();
}
} catch (IOException e) {
}
}
/**
* Helper that ignores exceptions during close, because what are you going to
* do?
*/
public static void close(Socket socket) {
try {
if (socket != null) {
socket.close();
}
} catch (IOException e) {
}
}
/**
* Helper that ignores exceptions during close, because what are you going to
* do?
*/
public static void close(Writer writer) {
try {
if (writer != null) {
writer.close();
}
} catch (IOException e) {
}
}
/**
* @param parent Parent directory
* @param fileName New file name
* @param overwrite Is overwriting an existing file allowed?
* @return Handle to the file
* @throws IOException If the file cannot be created, or if the file already
* existed and overwrite was false.
*/
public static File createNormalFile(File parent, String fileName,
boolean overwrite, boolean ignore) throws IOException {
File file = new File(parent, fileName);
if (file.createNewFile()) {
System.out.println("Created file " + file);
return file;
}
if (!file.exists() || file.isDirectory()) {
throw new IOException(file.getPath() + " : could not create normal file.");
}
if (ignore) {
System.out.println(file + " already exists; skipping");
return null;
}
if (!overwrite) {
throw new IOException(
file.getPath()
+ " : already exists; please remove it or use the -overwrite or -ignore option.");
}
System.out.println("Overwriting existing file " + file);
return file;
}
/**
* @param parent Parent directory of the requested directory.
* @param dirName Requested name for the directory.
* @param create Create the directory if it does not already exist?
* @return A {@link File} representing a directory that now exists.
* @throws IOException If the directory is not found and/or cannot be created.
*/
public static File getDirectory(File parent, String dirName, boolean create)
throws IOException {
File dir = new File(parent, dirName);
boolean alreadyExisted = dir.exists();
if (create) {
// No need to check mkdirs result because we check for dir.exists()
dir.mkdirs();
}
if (!dir.exists() || !dir.isDirectory()) {
if (create) {
throw new IOException(dir.getPath() + " : could not create directory.");
} else {
throw new IOException(dir.getPath() + " : could not find directory.");
}
}
if (create && !alreadyExisted) {
System.out.println("Created directory " + dir);
}
return dir;
}
/**
* @param dirPath Requested path for the directory.
* @param create Create the directory if it does not already exist?
* @return A {@link File} representing a directory that now exists.
* @throws IOException If the directory is not found and/or cannot be created.
*/
public static File getDirectory(String dirPath, boolean create)
throws IOException {
return getDirectory(null, dirPath, create);
}
/**
* Gets the contents of a file from the class path as a String. Note: this
* method is only guaranteed to work for resources in the same class loader
* that contains this {@link Utility} class.
*
* @param partialPath the partial path to the resource on the class path
* @return the contents of the file
* @throws IOException if the file could not be found or an error occurred
* while reading it
*/
public static String getFileFromClassPath(String partialPath)
throws IOException {
InputStream in = Utility.class.getClassLoader().getResourceAsStream(
partialPath);
try {
if (in == null) {
throw new FileNotFoundException(partialPath);
}
ByteArrayOutputStream os = new ByteArrayOutputStream();
streamOut(in, os, 1024);
return new String(os.toByteArray(), "UTF-8");
} finally {
close(in);
}
}
public static String getInstallPath() {
if (sInstallPath == null) {
computeInstallationPath();
}
return sInstallPath;
}
/**
* Generate MD5 digest.
*
* @param input input data to be hashed.
* @return MD5 digest.
*/
public static byte[] getMd5Digest(byte[] input) {
MessageDigest md5 = perThreadMd5.get();
md5.reset();
md5.update(input);
return md5.digest();
}
/**
* A 4-digit hex result.
*/
public static void hex4(char c, StringBuffer sb) {
sb.append(HEX_CHARS[(c & 0xF000) >> 12]);
sb.append(HEX_CHARS[(c & 0x0F00) >> 8]);
sb.append(HEX_CHARS[(c & 0x00F0) >> 4]);
sb.append(HEX_CHARS[c & 0x000F]);
}
/**
* Creates a randomly-named temporary directory.
*
* @param baseDir base directory to contain the new directory. May be
* {@code null}, in which case the directory given by the
* {@code java.io.tmpdir} system property will be used.
* @param prefix the initial characters of the new directory name
* @return a newly-created temporary directory; the caller must delete this
* directory (either when done or on VM exit)
*/
public static File makeTemporaryDirectory(File baseDir, String prefix)
throws IOException {
if (baseDir == null) {
baseDir = new File(System.getProperty("java.io.tmpdir"));
}
// No need to check the result of this mkdirs call because
// we will detect the subsequent failure
baseDir.mkdirs();
// Try this a few times due to non-atomic delete+mkdir operations.
for (int tries = 0; tries < 3; ++tries) {
File result = File.createTempFile(prefix, null, baseDir);
if (!result.delete()) {
throw new IOException("Couldn't delete temporary file "
+ result.getAbsolutePath() + " to replace with a directory.");
}
if (result.mkdirs()) {
// Success.
return result;
}
}
throw new IOException(
"Couldn't create temporary directory after 3 tries in "
+ baseDir.getAbsolutePath());
}
public static void streamOut(File file, OutputStream out, int bufferSize)
throws IOException {
FileInputStream fis = null;
try {
fis = new FileInputStream(file);
streamOut(fis, out, bufferSize);
} finally {
com.google.gwt.util.tools.Utility.close(fis);
}
}
public static void streamOut(InputStream in, OutputStream out, int bufferSize)
throws IOException {
assert (bufferSize >= 0);
byte[] buffer = new byte[bufferSize];
int bytesRead = 0;
while (true) {
bytesRead = in.read(buffer);
if (bytesRead >= 0) {
// Copy the bytes out.
out.write(buffer, 0, bytesRead);
} else {
// End of input stream.
return;
}
}
}
/**
* Returns a string representation of the byte array as a series of
* hexadecimal characters.
*
* @param bytes byte array to convert
* @return a string representation of the byte array as a series of
* hexadecimal characters
*/
public static String toHexString(byte[] bytes) {
char[] hexString = new char[2 * bytes.length];
int j = 0;
for (int i = 0; i < bytes.length; i++) {
hexString[j++] = HEX_CHARS[(bytes[i] & 0xF0) >> 4];
hexString[j++] = HEX_CHARS[bytes[i] & 0x0F];
}
return new String(hexString);
}
public static void writeTemplateBinaryFile(File file, byte[] contents) throws IOException {
FileOutputStream o = new FileOutputStream(file);
o.write(contents);
close(o);
}
public static void writeTemplateFile(File file, String contents,
Map<String, String> replacements) throws IOException {
String replacedContents = contents;
Set<Entry<String, String>> entries = replacements.entrySet();
for (Iterator<Entry<String, String>> iter = entries.iterator(); iter.hasNext();) {
Entry<String, String> entry = iter.next();
String replaceThis = entry.getKey();
String withThis = entry.getValue();
withThis = withThis.replaceAll("\\\\", "\\\\\\\\");
withThis = withThis.replaceAll("\\$", "\\\\\\$");
replacedContents = replacedContents.replaceAll(replaceThis, withThis);
}
PrintWriter pw = new PrintWriter(file);
LineNumberReader lnr = new LineNumberReader(new StringReader(replacedContents));
for (String line = lnr.readLine(); line != null; line = lnr.readLine()) {
pw.println(line);
}
close(pw);
}
private static void computeInstallationPath() {
try {
String override = System.getProperty("gwt.devjar");
if (override == null) {
String partialPath = Utility.class.getName().replace('.', '/').concat(
".class");
URL url = Utility.class.getClassLoader().getResource(partialPath);
if (url != null && "jar".equals(url.getProtocol())) {
String path = url.toString();
String jarPath = path.substring(path.indexOf("file:"),
path.lastIndexOf('!'));
File devJarFile = new File(URI.create(jarPath));
if (!devJarFile.isFile()) {
throw new IOException("Could not find jar file; "
+ devJarFile.getCanonicalPath()
+ " does not appear to be a valid file");
}
String dirPath = jarPath.substring(0, jarPath.lastIndexOf('/') + 1);
File installDirFile = new File(URI.create(dirPath));
if (!installDirFile.isDirectory()) {
throw new IOException("Could not find installation directory; "
+ installDirFile.getCanonicalPath()
+ " does not appear to be a valid directory");
}
sInstallPath = installDirFile.getCanonicalPath().replace(
File.separatorChar, '/');
} else {
throw new IOException(
"Cannot determine installation directory; apparently not running from a jar");
}
} else {
override = override.replace('\\', '/');
int pos = override.lastIndexOf('/');
if (pos < 0) {
sInstallPath = "";
} else {
sInstallPath = override.substring(0, pos);
}
}
} catch (IOException e) {
throw new RuntimeException(
"Installation problem detected, please reinstall GWT", e);
}
}
}