/******************************************************************************* | |
* Copyright (c) 2000, 2004 IBM Corporation and others. | |
* All rights reserved. This program and the accompanying materials | |
* are made available under the terms of the Common Public License v1.0 | |
* which accompanies this distribution, and is available at | |
* http://www.eclipse.org/legal/cpl-v10.html | |
* | |
* Contributors: | |
* IBM Corporation - initial API and implementation | |
*******************************************************************************/ | |
package org.eclipse.wst.jsdt.internal.compiler.util; | |
import java.io.BufferedInputStream; | |
import java.io.ByteArrayInputStream; | |
import java.io.File; | |
import java.io.FileInputStream; | |
import java.io.IOException; | |
import java.io.InputStream; | |
import java.io.InputStreamReader; | |
import java.util.Locale; | |
import java.util.MissingResourceException; | |
import java.util.ResourceBundle; | |
import java.util.zip.ZipEntry; | |
import java.util.zip.ZipFile; | |
import org.eclipse.wst.jsdt.core.compiler.CharOperation; | |
public class Util implements SuffixConstants { | |
public interface Displayable { | |
String displayString(Object o); | |
} | |
public static String LINE_SEPARATOR = System.getProperty("line.separator"); //$NON-NLS-1$ | |
public static char[] LINE_SEPARATOR_CHARS = LINE_SEPARATOR.toCharArray(); | |
private final static char[] DOUBLE_QUOTES = "''".toCharArray(); //$NON-NLS-1$ | |
private final static char[] SINGLE_QUOTE = "'".toCharArray(); //$NON-NLS-1$ | |
private static final int DEFAULT_READING_SIZE = 8192; | |
/* Bundle containing messages */ | |
protected static ResourceBundle bundle; | |
private final static String bundleName = | |
"org.eclipse.wst.jsdt.internal.compiler.util.messages"; //$NON-NLS-1$ | |
static { | |
relocalize(); | |
} | |
/** | |
* Lookup the message with the given ID in this catalog and bind its | |
* substitution locations with the given strings. | |
*/ | |
public static String bind(String id, String binding1, String binding2) { | |
return bind(id, new String[] { binding1, binding2 }); | |
} | |
/** | |
* Lookup the message with the given ID in this catalog and bind its | |
* substitution locations with the given string. | |
*/ | |
public static String bind(String id, String binding) { | |
return bind(id, new String[] { binding }); | |
} | |
/** | |
* Lookup the message with the given ID in this catalog and bind its | |
* substitution locations with the given string values. | |
*/ | |
public static String bind(String id, String[] bindings) { | |
if (id == null) | |
return "No message available"; //$NON-NLS-1$ | |
String message = null; | |
try { | |
message = bundle.getString(id); | |
} catch (MissingResourceException e) { | |
// If we got an exception looking for the message, fail gracefully by just returning | |
// the id we were looking for. In most cases this is semi-informative so is not too bad. | |
return "Missing message: " + id + " in: " + bundleName; //$NON-NLS-2$ //$NON-NLS-1$ | |
} | |
// for compatibility with MessageFormat which eliminates double quotes in original message | |
char[] messageWithNoDoubleQuotes = | |
CharOperation.replace(message.toCharArray(), DOUBLE_QUOTES, SINGLE_QUOTE); | |
if (bindings == null) return new String(messageWithNoDoubleQuotes); | |
int length = messageWithNoDoubleQuotes.length; | |
int start = 0; | |
int end = length; | |
StringBuffer output = null; | |
while (true) { | |
if ((end = CharOperation.indexOf('{', messageWithNoDoubleQuotes, start)) > -1) { | |
if (output == null) output = new StringBuffer(length+bindings.length*20); | |
output.append(messageWithNoDoubleQuotes, start, end - start); | |
if ((start = CharOperation.indexOf('}', messageWithNoDoubleQuotes, end + 1)) > -1) { | |
int index = -1; | |
String argId = new String(messageWithNoDoubleQuotes, end + 1, start - end - 1); | |
try { | |
index = Integer.parseInt(argId); | |
output.append(bindings[index]); | |
} catch (NumberFormatException nfe) { // could be nested message ID {compiler.name} | |
boolean done = false; | |
if (!id.equals(argId)) { | |
String argMessage = null; | |
try { | |
argMessage = bundle.getString(argId); | |
output.append(argMessage); | |
done = true; | |
} catch (MissingResourceException e) { | |
// unable to bind argument, ignore (will leave argument in) | |
} | |
} | |
if (!done) output.append(messageWithNoDoubleQuotes, end + 1, start - end); | |
} catch (ArrayIndexOutOfBoundsException e) { | |
output.append("{missing " + Integer.toString(index) + "}"); //$NON-NLS-2$ //$NON-NLS-1$ | |
} | |
start++; | |
} else { | |
output.append(messageWithNoDoubleQuotes, end, length); | |
break; | |
} | |
} else { | |
if (output == null) return new String(messageWithNoDoubleQuotes); | |
output.append(messageWithNoDoubleQuotes, start, length - start); | |
break; | |
} | |
} | |
return output.toString(); | |
} | |
/** | |
* Lookup the message with the given ID in this catalog | |
*/ | |
public static String bind(String id) { | |
return bind(id, (String[]) null); | |
} | |
/** | |
* Creates a NLS catalog for the given locale. | |
*/ | |
public static void relocalize() { | |
try { | |
bundle = ResourceBundle.getBundle(bundleName, Locale.getDefault()); | |
} catch(MissingResourceException e) { | |
System.out.println("Missing resource : " + bundleName.replace('.', '/') + ".properties for locale " + Locale.getDefault()); //$NON-NLS-1$//$NON-NLS-2$ | |
throw e; | |
} | |
} | |
/** | |
* Returns the given bytes as a char array using a given encoding (null means platform default). | |
*/ | |
public static char[] bytesToChar(byte[] bytes, String encoding) throws IOException { | |
return getInputStreamAsCharArray(new ByteArrayInputStream(bytes), bytes.length, encoding); | |
} | |
/** | |
* Returns the contents of the given file as a byte array. | |
* @throws IOException if a problem occured reading the file. | |
*/ | |
public static byte[] getFileByteContent(File file) throws IOException { | |
InputStream stream = null; | |
try { | |
stream = new BufferedInputStream(new FileInputStream(file)); | |
return getInputStreamAsByteArray(stream, (int) file.length()); | |
} finally { | |
if (stream != null) { | |
try { | |
stream.close(); | |
} catch (IOException e) { | |
// ignore | |
} | |
} | |
} | |
} | |
/** | |
* Returns the contents of the given file as a char array. | |
* When encoding is null, then the platform default one is used | |
* @throws IOException if a problem occured reading the file. | |
*/ | |
public static char[] getFileCharContent(File file, String encoding) throws IOException { | |
InputStream stream = null; | |
try { | |
stream = new BufferedInputStream(new FileInputStream(file)); | |
return getInputStreamAsCharArray(stream, (int) file.length(), encoding); | |
} finally { | |
if (stream != null) { | |
try { | |
stream.close(); | |
} catch (IOException e) { | |
// ignore | |
} | |
} | |
} | |
} | |
/* | |
* NIO support to get input stream as byte array. | |
* Not used as with JDK 1.4.2 this support is slower than standard IO one... | |
* Keep it as comment for future in case of next JDK versions improve performance | |
* in this area... | |
* | |
public static byte[] getInputStreamAsByteArray(FileInputStream stream, int length) | |
throws IOException { | |
FileChannel channel = stream.getChannel(); | |
int size = (int)channel.size(); | |
if (length >= 0 && length < size) size = length; | |
byte[] contents = new byte[size]; | |
ByteBuffer buffer = ByteBuffer.wrap(contents); | |
channel.read(buffer); | |
return contents; | |
} | |
*/ | |
/** | |
* Returns the given input stream's contents as a byte array. | |
* If a length is specified (ie. if length != -1), only length bytes | |
* are returned. Otherwise all bytes in the stream are returned. | |
* Note this doesn't close the stream. | |
* @throws IOException if a problem occured reading the stream. | |
*/ | |
public static byte[] getInputStreamAsByteArray(InputStream stream, int length) | |
throws IOException { | |
byte[] contents; | |
if (length == -1) { | |
contents = new byte[0]; | |
int contentsLength = 0; | |
int amountRead = -1; | |
do { | |
int amountRequested = Math.max(stream.available(), DEFAULT_READING_SIZE); // read at least 8K | |
// resize contents if needed | |
if (contentsLength + amountRequested > contents.length) { | |
System.arraycopy( | |
contents, | |
0, | |
contents = new byte[contentsLength + amountRequested], | |
0, | |
contentsLength); | |
} | |
// read as many bytes as possible | |
amountRead = stream.read(contents, contentsLength, amountRequested); | |
if (amountRead > 0) { | |
// remember length of contents | |
contentsLength += amountRead; | |
} | |
} while (amountRead != -1); | |
// resize contents if necessary | |
if (contentsLength < contents.length) { | |
System.arraycopy( | |
contents, | |
0, | |
contents = new byte[contentsLength], | |
0, | |
contentsLength); | |
} | |
} else { | |
contents = new byte[length]; | |
int len = 0; | |
int readSize = 0; | |
while ((readSize != -1) && (len != length)) { | |
// See PR 1FMS89U | |
// We record first the read size. In this case len is the actual read size. | |
len += readSize; | |
readSize = stream.read(contents, len, length - len); | |
} | |
} | |
return contents; | |
} | |
/* | |
* NIO support to get input stream as char array. | |
* Not used as with JDK 1.4.2 this support is slower than standard IO one... | |
* Keep it as comment for future in case of next JDK versions improve performance | |
* in this area... | |
public static char[] getInputStreamAsCharArray(FileInputStream stream, int length, String encoding) | |
throws IOException { | |
FileChannel channel = stream.getChannel(); | |
int size = (int)channel.size(); | |
if (length >= 0 && length < size) size = length; | |
Charset charset = encoding==null?systemCharset:Charset.forName(encoding); | |
if (charset != null) { | |
MappedByteBuffer bbuffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, size); | |
CharsetDecoder decoder = charset.newDecoder(); | |
CharBuffer buffer = decoder.decode(bbuffer); | |
char[] contents = new char[buffer.limit()]; | |
buffer.get(contents); | |
return contents; | |
} | |
throw new UnsupportedCharsetException(SYSTEM_FILE_ENCODING); | |
} | |
*/ | |
/** | |
* Returns the given input stream's contents as a character array. | |
* If a length is specified (ie. if length != -1), only length chars | |
* are returned. Otherwise all chars in the stream are returned. | |
* Note this doesn't close the stream. | |
* @throws IOException if a problem occured reading the stream. | |
*/ | |
public static char[] getInputStreamAsCharArray(InputStream stream, int length, String encoding) | |
throws IOException { | |
InputStreamReader reader = null; | |
reader = encoding == null | |
? new InputStreamReader(stream) | |
: new InputStreamReader(stream, encoding); | |
char[] contents; | |
if (length == -1) { | |
contents = CharOperation.NO_CHAR; | |
int contentsLength = 0; | |
int amountRead = -1; | |
do { | |
int amountRequested = Math.max(stream.available(), DEFAULT_READING_SIZE); // read at least 8K | |
// resize contents if needed | |
if (contentsLength + amountRequested > contents.length) { | |
System.arraycopy( | |
contents, | |
0, | |
contents = new char[contentsLength + amountRequested], | |
0, | |
contentsLength); | |
} | |
// read as many chars as possible | |
amountRead = reader.read(contents, contentsLength, amountRequested); | |
if (amountRead > 0) { | |
// remember length of contents | |
contentsLength += amountRead; | |
} | |
} while (amountRead != -1); | |
// Do not keep first character for UTF-8 BOM encoding | |
int start = 0; | |
if (contentsLength > 0 && "UTF-8".equals(encoding)) { //$NON-NLS-1$ | |
if (contents[0] == 0xFEFF) { // if BOM char then skip | |
contentsLength--; | |
start = 1; | |
} | |
} | |
// resize contents if necessary | |
if (contentsLength < contents.length) { | |
System.arraycopy( | |
contents, | |
start, | |
contents = new char[contentsLength], | |
0, | |
contentsLength); | |
} | |
} else { | |
contents = new char[length]; | |
int len = 0; | |
int readSize = 0; | |
while ((readSize != -1) && (len != length)) { | |
// See PR 1FMS89U | |
// We record first the read size. In this case len is the actual read size. | |
len += readSize; | |
readSize = reader.read(contents, len, length - len); | |
} | |
// Do not keep first character for UTF-8 BOM encoding | |
int start = 0; | |
if (length > 0 && "UTF-8".equals(encoding)) { //$NON-NLS-1$ | |
if (contents[0] == 0xFEFF) { // if BOM char then skip | |
len--; | |
start = 1; | |
} | |
} | |
// See PR 1FMS89U | |
// Now we need to resize in case the default encoding used more than one byte for each | |
// character | |
if (len != length) | |
System.arraycopy(contents, start, (contents = new char[len]), 0, len); | |
} | |
return contents; | |
} | |
/** | |
* Returns the contents of the given zip entry as a byte array. | |
* @throws IOException if a problem occured reading the zip entry. | |
*/ | |
public static byte[] getZipEntryByteContent(ZipEntry ze, ZipFile zip) | |
throws IOException { | |
InputStream stream = null; | |
try { | |
stream = new BufferedInputStream(zip.getInputStream(ze)); | |
return getInputStreamAsByteArray(stream, (int) ze.getSize()); | |
} finally { | |
if (stream != null) { | |
try { | |
stream.close(); | |
} catch (IOException e) { | |
// ignore | |
} | |
} | |
} | |
} | |
/** | |
* Returns true iff str.toLowerCase().endsWith(".jar") || str.toLowerCase().endsWith(".zip") | |
* implementation is not creating extra strings. | |
*/ | |
public final static boolean isArchiveFileName(String name) { | |
int nameLength = name == null ? 0 : name.length(); | |
int suffixLength = SUFFIX_JAR.length; | |
if (nameLength < suffixLength) return false; | |
// try to match as JAR file | |
for (int i = 0; i < suffixLength; i++) { | |
char c = name.charAt(nameLength - i - 1); | |
int suffixIndex = suffixLength - i - 1; | |
if (c != SUFFIX_jar[suffixIndex] && c != SUFFIX_JAR[suffixIndex]) { | |
// try to match as ZIP file | |
suffixLength = SUFFIX_ZIP.length; | |
if (nameLength < suffixLength) return false; | |
for (int j = 0; j < suffixLength; j++) { | |
c = name.charAt(nameLength - j - 1); | |
suffixIndex = suffixLength - j - 1; | |
if (c != SUFFIX_zip[suffixIndex] && c != SUFFIX_ZIP[suffixIndex]) return false; | |
} | |
return true; | |
} | |
} | |
return true; | |
} | |
/** | |
* Returns true iff str.toLowerCase().endsWith(".class") | |
* implementation is not creating extra strings. | |
*/ | |
public final static boolean isClassFileName(String name) { | |
int nameLength = name == null ? 0 : name.length(); | |
int suffixLength = SUFFIX_CLASS.length; | |
if (nameLength < suffixLength) return false; | |
for (int i = 0; i < suffixLength; i++) { | |
char c = name.charAt(nameLength - i - 1); | |
int suffixIndex = suffixLength - i - 1; | |
if (c != SUFFIX_class[suffixIndex] && c != SUFFIX_CLASS[suffixIndex]) return false; | |
} | |
return true; | |
} | |
/** | |
* Returns true iff str.toLowerCase().endsWith(".class") | |
* implementation is not creating extra strings. | |
*/ | |
public final static boolean isClassFileName(char[] name) { | |
int nameLength = name == null ? 0 : name.length; | |
int suffixLength = SUFFIX_CLASS.length; | |
if (nameLength < suffixLength) return false; | |
for (int i = 0, offset = nameLength - suffixLength; i < suffixLength; i++) { | |
char c = name[offset + i]; | |
if (c != SUFFIX_class[i] && c != SUFFIX_CLASS[i]) return false; | |
} | |
return true; | |
} | |
/** | |
* Returns true iff str.toLowerCase().endsWith(".java") | |
* implementation is not creating extra strings. | |
*/ | |
public final static boolean isJavaFileName(String name) { | |
int nameLength = name == null ? 0 : name.length(); | |
int suffixLength = SUFFIX_JAVA.length; | |
if (nameLength < suffixLength) return false; | |
for (int i = 0; i < suffixLength; i++) { | |
char c = name.charAt(nameLength - i - 1); | |
int suffixIndex = suffixLength - i - 1; | |
if (c != SUFFIX_java[suffixIndex] && c != SUFFIX_JAVA[suffixIndex]) return false; | |
} | |
return true; | |
} | |
/** | |
* Returns true iff str.toLowerCase().endsWith(".java") | |
* implementation is not creating extra strings. | |
*/ | |
public final static boolean isJavaFileName(char[] name) { | |
int nameLength = name == null ? 0 : name.length; | |
int suffixLength = SUFFIX_JAVA.length; | |
if (nameLength < suffixLength) return false; | |
for (int i = 0, offset = nameLength - suffixLength; i < suffixLength; i++) { | |
char c = name[offset + i]; | |
if (c != SUFFIX_java[i] && c != SUFFIX_JAVA[i]) return false; | |
} | |
return true; | |
} | |
/** | |
* Converts an array of Objects into String. | |
*/ | |
public static String toString(Object[] objects) { | |
return toString(objects, | |
new Displayable(){ | |
public String displayString(Object o) { | |
if (o == null) return "null"; //$NON-NLS-1$ | |
return o.toString(); | |
} | |
}); | |
} | |
/** | |
* Converts an array of Objects into String. | |
*/ | |
public static String toString(Object[] objects, Displayable renderer) { | |
if (objects == null) return ""; //$NON-NLS-1$ | |
StringBuffer buffer = new StringBuffer(10); | |
for (int i = 0; i < objects.length; i++){ | |
if (i > 0) buffer.append(", "); //$NON-NLS-1$ | |
buffer.append(renderer.displayString(objects[i])); | |
} | |
return buffer.toString(); | |
} | |
/** | |
* Converts a boolean value into Boolean. | |
* @param bool The boolean to convert | |
* @return The corresponding Boolean object (TRUE or FALSE). | |
*/ | |
public static Boolean toBoolean(boolean bool) { | |
if (bool) { | |
return Boolean.TRUE; | |
} else { | |
return Boolean.FALSE; | |
} | |
} | |
} |