/******************************************************************************* * 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.jdt.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.jdt.core.compiler.CharOperation; public class Util implements SuffixConstants { public interface Displayable { String displayString(Object o); } static { relocalize(); } /* Bundle containing messages */ protected static ResourceBundle bundle; private final static String bundleName = "org.eclipse.jdt.internal.compiler.util.messages"; //$NON-NLS-1$ private static final int DEFAULT_READING_SIZE = 8192; private final static char[] DOUBLE_QUOTES = "''".toCharArray(); //$NON-NLS-1$ 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[] SINGLE_QUOTE = "'".toCharArray(); //$NON-NLS-1$ /** * Lookup the message with the given ID in this catalog */ public static String bind(String id) { return bind(id, (String[]) null); } /** * 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 argument) { return bind(id, new String[] { argument }); } /** * 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 argument1, String argument2) { return bind(id, new String[] { argument1, argument2 }); } /** * 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[] arguments) { 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$ } return bindMessage(message, arguments); } /** * Bind some message with given string values. */ public static String bindMessage(String message, String[] arguments) { // for compatibility with MessageFormat which eliminates double quotes in original message char[] messageWithNoDoubleQuotes = CharOperation.replace(message.toCharArray(), DOUBLE_QUOTES, SINGLE_QUOTE); if (arguments == 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+arguments.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); if (arguments[index] == null) { output.append('{').append(argId).append('}'); // leave parameter in since no better arg '{0}' } else { output.append(arguments[index]); } } catch (NumberFormatException nfe) { // could be nested message ID {compiler.name} boolean done = false; 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(); } /** * 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(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(".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; } /* TODO (philippe) should consider promoting it to CharOperation * Returns whether the given resource path matches one of the inclusion/exclusion * patterns. * NOTE: should not be asked directly using pkg root pathes * @see IClasspathEntry#getInclusionPatterns * @see IClasspathEntry#getExclusionPatterns */ public final static boolean isExcluded(char[] path, char[][] inclusionPatterns, char[][] exclusionPatterns, boolean isFolderPath) { if (inclusionPatterns == null && exclusionPatterns == null) return false; inclusionCheck: if (inclusionPatterns != null) { for (int i = 0, length = inclusionPatterns.length; i < length; i++) { char[] pattern = inclusionPatterns[i]; char[] folderPattern = pattern; if (isFolderPath) { int lastSlash = CharOperation.lastIndexOf('/', pattern); if (lastSlash != -1 && lastSlash != pattern.length-1){ // trailing slash -> adds '**' for free (see http://ant.apache.org/manual/dirtasks.html) int star = CharOperation.indexOf('*', pattern, lastSlash); if ((star == -1 || star >= pattern.length-1 || pattern[star+1] != '*')) { folderPattern = CharOperation.subarray(pattern, 0, lastSlash); } } } if (CharOperation.pathMatch(folderPattern, path, true, '/')) { break inclusionCheck; } } return true; // never included } if (isFolderPath) { path = CharOperation.concat(path, new char[] {'*'}, '/'); } exclusionCheck: if (exclusionPatterns != null) { for (int i = 0, length = exclusionPatterns.length; i < length; i++) { if (CharOperation.pathMatch(exclusionPatterns[i], path, true, '/')) { return true; } } } return false; } /** * 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; } /** * 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; } /** * 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; } } /** * 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; } } /** * 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(); } }