/******************************************************************************* * 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 Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.erlide.common.util; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.io.DataInput; import java.io.EOFException; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.UTFDataFormatException; import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.CharacterCodingException; import java.nio.charset.Charset; import java.nio.charset.CharsetDecoder; import java.nio.charset.CharsetEncoder; import java.util.Locale; import java.util.MissingResourceException; import java.util.ResourceBundle; import java.util.StringTokenizer; import org.eclipse.core.runtime.Assert; import com.ericsson.otp.erlang.OtpErlangAtom; import com.ericsson.otp.erlang.OtpErlangBinary; import com.ericsson.otp.erlang.OtpErlangException; import com.ericsson.otp.erlang.OtpErlangList; import com.ericsson.otp.erlang.OtpErlangLong; import com.ericsson.otp.erlang.OtpErlangObject; import com.ericsson.otp.erlang.OtpErlangRangeException; import com.ericsson.otp.erlang.OtpErlangString; import com.ericsson.otp.erlang.OtpErlangTuple; /** * Provides convenient utility methods to other types in this package. */ public final class Util { public interface Comparable { /** * Returns 0 if this and c are equal, >0 if this is greater than c, or * <0 if this is less than c. */ int compareTo(Comparable c); } public interface Comparer { /** * Returns 0 if a and b are equal, >0 if a is greater than b, or <0 if a * is less than b. */ int compare(Object a, Object b); } private static final String ARGUMENTS_DELIMITER = "#"; //$NON-NLS-1$ /* Bundle containing messages */ static ResourceBundle bundle; private static final String BUNDLE_NAME = "org.erlide.common.util"; //$NON-NLS-1$ private static final char[] DOUBLE_QUOTES = "''".toCharArray(); //$NON-NLS-1$ private static final String EMPTY_ARGUMENT = " "; //$NON-NLS-1$ // public static final String[] fgEmptyStringArray = new String[0]; private static final char[] SINGLE_QUOTE = "'".toCharArray(); //$NON-NLS-1$ static { relocalize(); } private Util() { // cannot be instantiated } /** * Lookup the message with the given PLUGIN_ID in this catalog */ public static String bind(final String id) { return bind(id, (String[]) null); } /** * Lookup the message with the given PLUGIN_ID in this catalog and bind its * substitution locations with the given string. */ public static String bind(final String id, final String binding) { return bind(id, new String[] { binding }); } /** * Lookup the message with the given PLUGIN_ID in this catalog and bind its * substitution locations with the given strings. */ public static String bind(final String id, final String binding1, final String binding2) { return bind(id, new String[] { binding1, binding2 }); } /** * Lookup the message with the given PLUGIN_ID in this catalog and bind its * substitution locations with the given string values. */ public static String bind(final String id, final String[] bindings) { if (id == null) { return "No message available"; //$NON-NLS-1$ } String message = null; try { message = bundle.getString(id); } catch (final 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: " + BUNDLE_NAME; //$NON-NLS-2$ //$NON-NLS-1$ } // for compatibility with MessageFormat which eliminates double quotes // in original message final char[] messageWithNoDoubleQuotes = CharOperation.replace( message.toCharArray(), DOUBLE_QUOTES, SINGLE_QUOTE); if (bindings == null) { return new String(messageWithNoDoubleQuotes); } final int length = messageWithNoDoubleQuotes.length; int start = 0; int end = length; final StringBuilder output = new StringBuilder(); while (true) { end = CharOperation.indexOf('{', messageWithNoDoubleQuotes, start); if (end > -1) { output.append(messageWithNoDoubleQuotes, start, end - start); if ((start = CharOperation.indexOf('}', messageWithNoDoubleQuotes, end + 1)) > -1) { int index = -1; final String argId = new String(messageWithNoDoubleQuotes, end + 1, start - end - 1); try { index = Integer.parseInt(argId); output.append(bindings[index]); } catch (final NumberFormatException nfe) { // could be // nested // message PLUGIN_ID // {compiler.name} boolean done = false; if (!argId.equals(id)) { String argMessage = null; try { argMessage = bundle.getString(argId); output.append(argMessage); done = true; } catch (final MissingResourceException e) { // unable to bind argument, ignore (will leave // argument in) } } if (!done) { output.append(messageWithNoDoubleQuotes, end + 1, start - end); } } catch (final ArrayIndexOutOfBoundsException e) { output.append("{missing " + Integer.toString(index) + "}"); //$NON-NLS-2$ //$NON-NLS-1$ } start++; } else { output.append(messageWithNoDoubleQuotes, end, length); break; } } else { output.append(messageWithNoDoubleQuotes, start, length - start); break; } } return output.toString(); } /** * Compares two byte arrays. Returns <0 if a byte in a is less than the * corresponding byte in b, or if a is shorter, or if a is null. Returns >0 * if a byte in a is greater than the corresponding byte in b, or if a is * longer, or if b is null. Returns 0 if they are equal or both null. */ public static int compare(final byte[] a, final byte[] b) { if (a == b) { return 0; } if (a == null) { return -1; } if (b == null) { return 1; } final int len = Math.min(a.length, b.length); for (int i = 0; i < len; ++i) { final int diff = a[i] - b[i]; if (diff != 0) { return diff; } } if (a.length > len) { return 1; } if (b.length > len) { return -1; } return 0; } /** * Compares two strings lexicographically. The comparison is based on the * Unicode value of each character in the strings. * * @return the value <code>0</code> if the str1 is equal to str2; a value * less than <code>0</code> if str1 is lexicographically less than * str2; and a value greater than <code>0</code> if str1 is * lexicographically greater than str2. */ public static int compare(final char[] str1, final char[] str2) { final int len1 = str1.length; final int len2 = str2.length; int n = Math.min(len1, len2); int i = 0; while (n-- != 0) { final char c1 = str1[i]; final char c2 = str2[i++]; if (c1 != c2) { return c1 - c2; } } return len1 - len2; } /** * Concatenate two strings with a char in between. * * @see #concat(String, String) */ public static String concat(String s1, final char c, String s2) { if (s1 == null) { s1 = "null"; //$NON-NLS-1$ } if (s2 == null) { s2 = "null"; //$NON-NLS-1$ } final int l1 = s1.length(); final int l2 = s2.length(); final char[] buf = new char[l1 + 1 + l2]; s1.getChars(0, l1, buf, 0); buf[l1] = c; s2.getChars(0, l2, buf, l1 + 1); return new String(buf); } /** * Concatenate two strings. Much faster than using +, which: - creates a * StringBuilder, - which is synchronized, - of default size, so the * resulting char array is often larger than needed. This implementation * creates an extra char array, since the String constructor copies its * argument, but there's no way around this. */ public static String concat(String s1, String s2) { if (s1 == null) { s1 = "null"; //$NON-NLS-1$ } if (s2 == null) { s2 = "null"; //$NON-NLS-1$ } final int l1 = s1.length(); final int l2 = s2.length(); final char[] buf = new char[l1 + l2]; s1.getChars(0, l1, buf, 0); s2.getChars(0, l2, buf, l1); return new String(buf); } /** * Concatenate three strings. * * @see #concat(String, String) */ public static String concat(String s1, String s2, String s3) { if (s1 == null) { s1 = "null"; //$NON-NLS-1$ } if (s2 == null) { s2 = "null"; //$NON-NLS-1$ } if (s3 == null) { s3 = "null"; //$NON-NLS-1$ } final int l1 = s1.length(); final int l2 = s2.length(); final int l3 = s3.length(); final char[] buf = new char[l1 + l2 + l3]; s1.getChars(0, l1, buf, 0); s2.getChars(0, l2, buf, l1); s3.getChars(0, l3, buf, l1 + l2); return new String(buf); } /** * Returns true iff str.toLowerCase().endsWith(end.toLowerCase()) * implementation is not creating extra strings. */ public static final boolean endsWithIgnoreCase(final String str, final String end) { if (str == null && end == null) { return true; } if (str == null) { return false; } if (end == null) { return false; } if (str.equals(end)) { return true; } final int strLength = str.length(); final int endLength = end.length(); // return false if the string is smaller than the end. if (endLength > strLength) { return false; } // return false if any character of the end are // not the same in lower case. for (int i = 1; i <= endLength; i++) { if (Character.toLowerCase(end.charAt(endLength - i)) != Character .toLowerCase(str.charAt(strLength - i))) { return false; } } return true; } /** * Compares two arrays using equals() on the elements. Either or both arrays * may be null. Returns true if both are null. Returns false if only one is * null. If both are arrays, returns true iff they have the same length and * all elements are equal. */ public static boolean equalArraysOrNull(final int[] a, final int[] b) { if (a == b) { return true; } if (a == null || b == null) { return false; } final int len = a.length; if (len != b.length) { return false; } for (int i = 0; i < len; ++i) { if (a[i] != b[i]) { return false; } } return true; } /** * Compares two arrays using equals() on the elements. Either or both arrays * may be null. Returns true if both are null. Returns false if only one is * null. If both are arrays, returns true iff they have the same length and * all elements compare true with equals. */ public static boolean equalArraysOrNull(final Object[] a, final Object[] b) { if (a == b) { return true; } if (a == null || b == null) { return false; } final int len = a.length; if (len != b.length) { return false; } for (int i = 0; i < len; ++i) { if (a[i] == null) { if (b[i] != null) { return false; } } else { if (!a[i].equals(b[i])) { return false; } } } return true; } /** * Compares two arrays using equals() on the elements. The arrays are first * sorted. Either or both arrays may be null. Returns true if both are null. * Returns false if only one is null. If both are arrays, returns true iff * they have the same length and iff, after sorting both arrays, all * elements compare true with equals. The original arrays are left * untouched. */ public static boolean equalArraysOrNullSortFirst(Comparable[] a, Comparable[] b) { if (a == b) { return true; } if (a == null || b == null) { return false; } final int len = a.length; if (len != b.length) { return false; } if (len >= 2) { // only need to sort if more than two items a = sortCopy(a); b = sortCopy(b); } for (int i = 0; i < len; ++i) { if (!a[i].equals(b[i])) { return false; } } return true; } /** * Compares two objects using equals(). Either or both array may be null. * Returns true if both are null. Returns false if only one is null. * Otherwise, return the result of comparing with equals(). */ public static boolean equalOrNull(final Object a, final Object b) { if (a == b) { return true; } if (a == null || b == null) { return false; } return a.equals(b); } /** * Given a qualified name, extract the last component. If the input is not * qualified, the same string is answered. */ public static String extractLastName(final String qualifiedName) { final int i = qualifiedName.lastIndexOf('.'); if (i == -1) { return qualifiedName; } return qualifiedName.substring(i + 1); } /** * Finds the first line separator used by the given text. * * @return</code> "\n"</code> or</code> "\r"</code> or</code> "\r\n" * </code>, or <code>null</code> if none found */ public static String findLineSeparator(final char[] text) { // find the first line separator final int length = text.length; if (length > 0) { char nextChar = text[0]; for (int i = 0; i < length; i++) { final char currentChar = nextChar; nextChar = i < length - 1 ? text[i + 1] : ' '; if (currentChar == '\n') { return "\n"; //$NON-NLS-1$ } if (currentChar == '\r') { return nextChar == '\n' ? "\r\n" : "\r"; //$NON-NLS-1$ //$NON-NLS-2$ } } } // not found return null; } // /** // * Returns the line separator used by the given buffer. Uses the given // text // * if none found. // * // * @return</code> "\n"</code> or</code> "\r"</code> or</code> "\r\n" // * </code> // */ // private static String getLineSeparator(char[] text, char[] buffer) { // // search in this buffer's contents first // String lineSeparator = findLineSeparator(buffer); // if (lineSeparator == null) { // // search in the given text // lineSeparator = findLineSeparator(text); // if (lineSeparator == null) { // // default to system line separator // return "\n"; // } // } // return lineSeparator; // } /** * Returns the number of parameter types in a method signature. */ public static int getParameterCount(final char[] sig) { int i = CharOperation.indexOf('(', sig) + 1; Assert.isTrue(i != 0); int count = 0; final int len = sig.length; for (;;) { if (i == len) { break; } final char c = sig[i]; if (c == ')') { break; } if (c == '[') { ++i; } else if (c == 'L') { ++count; i = CharOperation.indexOf(';', sig, i + 1) + 1; Assert.isTrue(i != 0); } else { ++count; ++i; } } return count; } /** * Put all the arguments in one String. */ public static String getProblemArgumentsForMarker(final String[] arguments) { final StringBuilder args = new StringBuilder(10); args.append(arguments.length); args.append(':'); for (int j = 0; j < arguments.length; j++) { if (j != 0) { args.append(ARGUMENTS_DELIMITER); } if (arguments[j].length() == 0) { args.append(EMPTY_ARGUMENT); } else { args.append(arguments[j]); } } return args.toString(); } /** * Separate all the arguments of a String made by * getProblemArgumentsForMarker */ public static String[] getProblemArgumentsFromMarker(String argumentsString) { if (argumentsString == null) { return null; } final int index = argumentsString.indexOf(':'); if (index == -1) { return null; } final int length = argumentsString.length(); int numberOfArg; try { numberOfArg = Integer.parseInt(argumentsString.substring(0, index)); } catch (final NumberFormatException e) { return null; } argumentsString = argumentsString.substring(index + 1, length); String[] args = new String[length]; int count = 0; final StringTokenizer tokenizer = new StringTokenizer(argumentsString, ARGUMENTS_DELIMITER); while (tokenizer.hasMoreTokens()) { String argument = tokenizer.nextToken(); if (EMPTY_ARGUMENT.equals(argument)) { argument = ""; //$NON-NLS-1$ } args[count++] = argument; } if (count != numberOfArg) { return null; } System.arraycopy(args, 0, args = new String[count], 0, count); return args; } /** * Validate the given compilation unit name. A compilation unit name must * obey the following rules: * <ul> * <li>it must not be null * <li>it must include the <code>".erl"</code> suffix * <li>its prefix must be a valid identifier * </ul> * </p> * * @param name * the name of a compilation unit * @return a boolean */ public static boolean isValidModuleFileName(final String name) { if (!name.endsWith(".erl")) { return false; } final int pos = name.lastIndexOf('.'); return isValidModuleName(name.substring(0, pos)); } public static boolean isValidModuleName(final String name) { return name.matches("[a-z][a-zA-Z0-9_]*"); } // /** // * Normalizes the cariage returns in the given text. They are all changed // to // * use the given buffer's line separator. // */ // public static char[] normalizeCRs(char[] text, char[] buffer) { // final CharArrayBuffer result = new CharArrayBuffer(); // int lineStart = 0; // final int length = text.length; // if (length == 0) { // return text; // } // final String lineSeparator = getLineSeparator(text, buffer); // char nextChar = text[0]; // for (int i = 0; i < length; i++) { // final char currentChar = nextChar; // nextChar = (i < length - 1) ? text[i + 1] : ' '; // switch (currentChar) { // case '\n': // int lineLength = i - lineStart; // char[] line = new char[lineLength]; // System.arraycopy(text, lineStart, line, 0, lineLength); // result.append(line); // result.append(lineSeparator); // lineStart = i + 1; // break; // case '\r': // lineLength = i - lineStart; // if (lineLength >= 0) { // line = new char[lineLength]; // System.arraycopy(text, lineStart, line, 0, lineLength); // result.append(line); // result.append(lineSeparator); // if (nextChar == '\n') { // nextChar = ' '; // lineStart = i + 2; // } else { // // when line separator are mixed in the same file // // \r might not be followed by a \n. If not, we // // should increment // // lineStart by one and not by two. // lineStart = i + 1; // } // } else { // // when line separator are mixed in the same file // // we need to prevent NegativeArraySizeException // lineStart = i + 1; // } // break; // } // } // char[] lastLine; // if (lineStart > 0) { // final int lastLineLength = length - lineStart; // if (lastLineLength > 0) { // lastLine = new char[lastLineLength]; // System.arraycopy(text, lineStart, lastLine, 0, lastLineLength); // result.append(lastLine); // } // return result.getContents(); // } // return text; // } // // /** // * Normalizes the cariage returns in the given text. They are all changed // to // * use given buffer's line sepatator. // */ // public static String normalizeCRs(String text, String buffer) { // return new String( // normalizeCRs(text.toCharArray(), buffer.toCharArray())); // } /** * Returns the length of the common prefix between s1 and s2. */ public static int prefixLength(final char[] s1, final char[] s2) { int len = 0; final int max = Math.min(s1.length, s2.length); for (int i = 0; i < max && s1[i] == s2[i]; ++i) { ++len; } return len; } /** * Returns the length of the common prefix between s1 and s2. */ public static int prefixLength(final String s1, final String s2) { int len = 0; final int max = Math.min(s1.length(), s2.length()); for (int i = 0; i < max && s1.charAt(i) == s2.charAt(i); ++i) { ++len; } return len; } private static void quickSort(final char[][] list, int left, int right) { final int original_left = left; final int original_right = right; final char[] mid = list[left / 2 + right / 2]; do { while (compare(list[left], mid) < 0) { left++; } while (compare(mid, list[right]) < 0) { right--; } if (left <= right) { final char[] tmp = list[left]; list[left] = list[right]; list[right] = tmp; left++; right--; } } while (left <= right); if (original_left < right) { quickSort(list, original_left, right); } if (left < original_right) { quickSort(list, left, original_right); } } /** * Sort the comparable objects in the given collection. */ private static void quickSort(final Comparable[] sortedCollection, int left, int right) { final int original_left = left; final int original_right = right; final Comparable mid = sortedCollection[left + right >>> 1]; do { while (sortedCollection[left].compareTo(mid) < 0) { left++; } while (mid.compareTo(sortedCollection[right]) < 0) { right--; } if (left <= right) { final Comparable tmp = sortedCollection[left]; sortedCollection[left] = sortedCollection[right]; sortedCollection[right] = tmp; left++; right--; } } while (left <= right); if (original_left < right) { quickSort(sortedCollection, original_left, right); } if (left < original_right) { quickSort(sortedCollection, left, original_right); } } private static void quickSort(final int[] list, int left, int right) { final int original_left = left; final int original_right = right; final int mid = list[left + right >>> 1]; do { while (list[left] < mid) { left++; } while (mid < list[right]) { right--; } if (left <= right) { final int tmp = list[left]; list[left] = list[right]; list[right] = tmp; left++; right--; } } while (left <= right); if (original_left < right) { quickSort(list, original_left, right); } if (left < original_right) { quickSort(list, left, original_right); } } /** * Sort the objects in the given collection using the given comparer. */ private static void quickSort(final Object[] sortedCollection, int left, int right, final Comparer comparer) { final int original_left = left; final int original_right = right; final Object mid = sortedCollection[left + right >>> 1]; do { while (comparer.compare(sortedCollection[left], mid) < 0) { left++; } while (comparer.compare(mid, sortedCollection[right]) < 0) { right--; } if (left <= right) { final Object tmp = sortedCollection[left]; sortedCollection[left] = sortedCollection[right]; sortedCollection[right] = tmp; left++; right--; } } while (left <= right); if (original_left < right) { quickSort(sortedCollection, original_left, right, comparer); } if (left < original_right) { quickSort(sortedCollection, left, original_right, comparer); } } /** * Sort the objects in the given collection using the given sort order. */ private static void quickSort(final Object[] sortedCollection, int left, int right, final int[] sortOrder) { final int original_left = left; final int original_right = right; final int mid = sortOrder[left / 2 + right / 2]; do { while (sortOrder[left] < mid) { left++; } while (mid < sortOrder[right]) { right--; } if (left <= right) { final Object tmp = sortedCollection[left]; sortedCollection[left] = sortedCollection[right]; sortedCollection[right] = tmp; final int tmp2 = sortOrder[left]; sortOrder[left] = sortOrder[right]; sortOrder[right] = tmp2; left++; right--; } } while (left <= right); if (original_left < right) { quickSort(sortedCollection, original_left, right, sortOrder); } if (left < original_right) { quickSort(sortedCollection, left, original_right, sortOrder); } } /** * Sort the strings in the given collection. */ private static void quickSort(final String[] sortedCollection, int left, int right) { final int original_left = left; final int original_right = right; final String mid = sortedCollection[left + right >>> 1]; do { while (sortedCollection[left].compareTo(mid) < 0) { left++; } while (mid.compareTo(sortedCollection[right]) < 0) { right--; } if (left <= right) { final String tmp = sortedCollection[left]; sortedCollection[left] = sortedCollection[right]; sortedCollection[right] = tmp; left++; right--; } } while (left <= right); if (original_left < right) { quickSort(sortedCollection, original_left, right); } if (left < original_right) { quickSort(sortedCollection, left, original_right); } } /** * Sort the strings in the given collection in reverse alphabetical order. */ private static void quickSortReverse(final String[] sortedCollection, int left, int right) { final int original_left = left; final int original_right = right; final String mid = sortedCollection[left + right >>> 1]; do { while (sortedCollection[left].compareTo(mid) > 0) { left++; } while (mid.compareTo(sortedCollection[right]) > 0) { right--; } if (left <= right) { final String tmp = sortedCollection[left]; sortedCollection[left] = sortedCollection[right]; sortedCollection[right] = tmp; left++; right--; } } while (left <= right); if (original_left < right) { quickSortReverse(sortedCollection, original_left, right); } if (left < original_right) { quickSortReverse(sortedCollection, left, original_right); } } /** * Reads in a string from the specified data input stream. The string has * been encoded using a modified UTF-8 format. * <p> * The first two bytes are read as if by <code>readUnsignedShort</code>. * This value gives the number of following bytes that are in the encoded * string, not the length of the resulting string. The following bytes are * then interpreted as bytes encoding characters in the UTF-8 format and are * converted into characters. * <p> * This method blocks until all the bytes are read, the end of the stream is * detected, or an exception is thrown. * * @param in * a data input stream. * @return a Unicode string. * @exception EOFException * if the input stream reaches the end before all the bytes. * @exception IOException * if an I/O error occurs. * @exception UTFDataFormatException * if the bytes do not represent a valid UTF-8 encoding of a * Unicode string. * @see java.io.DataInputStream#readUnsignedShort() */ static final char[] readUTF(final DataInput in) throws IOException { final int utflen = in.readUnsignedShort(); char[] str = new char[utflen]; int count = 0; int strlen = 0; while (count < utflen) { final int c = in.readUnsignedByte(); int char2, char3; switch (c >> 4) { case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: // 0xxxxxxx count++; str[strlen++] = (char) c; break; case 12: case 13: // 110x xxxx 10xx xxxx count += 2; if (count > utflen) { throw new UTFDataFormatException(); } char2 = in.readUnsignedByte(); if ((char2 & 0xC0) != 0x80) { throw new UTFDataFormatException(); } str[strlen++] = (char) ((c & 0x1F) << 6 | char2 & 0x3F); break; case 14: // 1110 xxxx 10xx xxxx 10xx xxxx count += 3; if (count > utflen) { throw new UTFDataFormatException(); } char2 = in.readUnsignedByte(); char3 = in.readUnsignedByte(); if ((char2 & 0xC0) != 0x80 || (char3 & 0xC0) != 0x80) { throw new UTFDataFormatException(); } str[strlen++] = (char) ((c & 0x0F) << 12 | (char2 & 0x3F) << 6 | (char3 & 0x3F) << 0); break; default: // 10xx xxxx, 1111 xxxx throw new UTFDataFormatException(); } } if (strlen < utflen) { System.arraycopy(str, 0, str = new char[strlen], 0, strlen); } return str; } /** * Creates a NLS catalog for the given locale. */ public static void relocalize() { try { bundle = ResourceBundle.getBundle(BUNDLE_NAME, Locale.getDefault()); } catch (final MissingResourceException e) { System.out .println("Missing resource : " + BUNDLE_NAME.replace('.', '/') + ".properties for locale " + Locale.getDefault()); //$NON-NLS-1$//$NON-NLS-2$ throw e; } } public static void sort(final char[][] list) { if (list.length > 1) { quickSort(list, 0, list.length - 1); } } /** * Sorts an array of Comparable objects in place. */ public static void sort(final Comparable[] objects) { if (objects.length > 1) { quickSort(objects, 0, objects.length - 1); } } public static void sort(final int[] list) { if (list.length > 1) { quickSort(list, 0, list.length - 1); } } /** * Sorts an array of objects in place. The given comparer compares pairs of * items. */ public static void sort(final Object[] objects, final Comparer comparer) { if (objects.length > 1) { quickSort(objects, 0, objects.length - 1, comparer); } } /** * Sorts an array of objects in place, using the sort order given for each * item. */ public static void sort(final Object[] objects, final int[] sortOrder) { if (objects.length > 1) { quickSort(objects, 0, objects.length - 1, sortOrder); } } /** * Sorts an array of strings in place using quicksort. */ public static void sort(final String[] strings) { if (strings.length > 1) { quickSort(strings, 0, strings.length - 1); } } /** * Sorts an array of Comparable objects, returning a new array with the * sorted items. The original array is left untouched. */ public static Comparable[] sortCopy(final Comparable[] objects) { final int len = objects.length; final Comparable[] copy = new Comparable[len]; System.arraycopy(objects, 0, copy, 0, len); sort(copy); return copy; } /** * Sorts an array of Strings, returning a new array with the sorted items. * The original array is left untouched. */ public static Object[] sortCopy(final Object[] objects, final Comparer comparer) { final int len = objects.length; final Object[] copy = new Object[len]; System.arraycopy(objects, 0, copy, 0, len); sort(copy, comparer); return copy; } /** * Sorts an array of Strings, returning a new array with the sorted items. * The original array is left untouched. */ public static String[] sortCopy(final String[] objects) { final int len = objects.length; final String[] copy = new String[len]; System.arraycopy(objects, 0, copy, 0, len); sort(copy); return copy; } /** * Sorts an array of strings in place using quicksort in reverse * alphabetical order. */ public static void sortReverseOrder(final String[] strings) { if (strings.length > 1) { quickSortReverse(strings, 0, strings.length - 1); } } /** * Converts a String[] to char[][]. */ public static char[][] toCharArrays(final String[] a) { final int len = a.length; final char[][] result = new char[len][]; for (int i = 0; i < len; ++i) { result[i] = toChars(a[i]); } return result; } /** * Converts a String to char[]. */ public static char[] toChars(final String s) { final int len = s.length(); final char[] chars = new char[len]; s.getChars(0, len, chars, 0); return chars; } /** * Converts a String to char[][], where segments are separate by '.'. */ public static char[][] toCompoundChars(final String s) { final int len = s.length(); if (len == 0) { return CharOperation.NO_CHAR_CHAR; } int segCount = 1; for (int off = s.indexOf('.'); off != -1; off = s.indexOf('.', off + 1)) { ++segCount; } final char[][] segs = new char[segCount][]; int start = 0; for (int i = 0; i < segCount; ++i) { final int dot = s.indexOf('.', start); final int end = dot == -1 ? s.length() : dot; segs[i] = new char[end - start]; s.getChars(start, end, segs[i], 0); start = end + 1; } return segs; } /** * Converts a char[] to String. */ public static String toString(final char[] c) { return new String(c); } /** * Converts a char[][] to String, where segments are separated by '.'. */ public static String toString(final char[][] c) { final StringBuilder sb = new StringBuilder(); for (int i = 0, max = c.length; i < max; ++i) { if (i != 0) { sb.append('.'); } sb.append(c[i]); } return sb.toString(); } /** * Converts a char[][] and a char[] to String, where segments are separated * by '.'. */ public static String toString(final char[][] c, final char[] d) { if (c == null) { return new String(d); } final StringBuilder sb = new StringBuilder(); for (final char[] element : c) { sb.append(element); sb.append('.'); } sb.append(d); return sb.toString(); } /** * Writes a string to the given output stream using UTF-8 encoding in a * machine-independent manner. * <p> * First, two bytes are written to the output stream as if by the * <code>writeShort</code> method giving the number of bytes to follow. This * value is the number of bytes actually written out, not the length of the * string. Following the length, each character of the string is output, in * sequence, using the UTF-8 encoding for the character. * * @param str * a string to be written. * @return the number of bytes written to the stream. * @exception IOException * if an I/O error occurs. * */ public static int writeUTF(final OutputStream out, final char[] str) throws IOException { final int strlen = str.length; int utflen = 0; for (int i = 0; i < strlen; i++) { final int c = str[i]; if (c >= 0x0001 && c <= 0x007F) { utflen++; } else if (c > 0x07FF) { utflen += 3; } else { utflen += 2; } } if (utflen > 65535) { throw new UTFDataFormatException(); } out.write(utflen >>> 8 & 0xFF); out.write(utflen >>> 0 & 0xFF); if (strlen == utflen) { for (int i = 0; i < strlen; i++) { out.write(str[i]); } } else { for (int i = 0; i < strlen; i++) { final int c = str[i]; if (c >= 0x0001 && c <= 0x007F) { out.write(c); } else if (c > 0x07FF) { out.write(0xE0 | c >> 12 & 0x0F); out.write(0x80 | c >> 6 & 0x3F); out.write(0x80 | c >> 0 & 0x3F); } else { out.write(0xC0 | c >> 6 & 0x1F); out.write(0x80 | c >> 0 & 0x3F); } } } return utflen + 2; // the number of bytes written to the stream } public interface Displayable { String displayString(Object o); } public static final String LINE_SEPARATOR = System .getProperty("line.separator"); //$NON-NLS-1$ static final char[] LINE_SEPARATOR_CHARS = LINE_SEPARATOR.toCharArray(); private static final int DEFAULT_READING_SIZE = 8192; /** * Returns the given bytes as a char array using a given encoding (null * means platform default). */ public static char[] bytesToChar(final byte[] bytes, final 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(final 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 (final 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(final File file, final 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 (final IOException e) { // ignore } } } } public static char[] getFileCharContent(final String path, final String encoding) throws IOException { InputStream stream = null; try { stream = new BufferedInputStream(new FileInputStream(path)); return getInputStreamAsCharArray(stream, -1, encoding); } finally { if (stream != null) { try { stream.close(); } catch (final IOException e) { } } } } /* * 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(final InputStream stream, final int length) throws IOException { byte[] contents; if (length == -1) { contents = new byte[0]; int contentsLength = 0; int amountRead = -1; do { final 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(final InputStream stream, final int length, final String encoding) throws IOException { InputStreamReader reader = null; reader = encoding == null ? new InputStreamReader(stream) : new InputStreamReader(stream, encoding); char[] contents; try { if (length == -1) { contents = CharOperation.NO_CHAR; int contentsLength = 0; int amountRead = -1; do { final int amountRequested = Math.max(stream.available(), DEFAULT_READING_SIZE); // read // at // least // 8K // resize contents if needed if (contentsLength + amountRequested > contents.length) { final char[] old = contents; contents = new char[contentsLength + amountRequested]; System.arraycopy(old, 0, contents, 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 ("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) { final char[] old = contents; contents = new char[contentsLength]; System.arraycopy(old, start, contents, 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 ("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) { contents = new char[len]; System.arraycopy(contents, start, contents, 0, len); } } } finally { reader.close(); } return contents; } /** * Converts an array of Objects into String. */ public static String toString(final Object[] objects) { return toString(objects, new Displayable() { @Override public String displayString(final Object o) { if (o == null) { return "null"; //$NON-NLS-1$ } return o.toString(); } }); } /** * Converts an array of Objects into String. */ public static String toString(final Object[] objects, final Displayable renderer) { if (objects == null) { return ""; //$NON-NLS-1$ } final StringBuilder buffer = new StringBuilder(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(final boolean bool) { if (bool) { return Boolean.TRUE; } return Boolean.FALSE; } /** * Get the string value of an Erlang string, empty if empty list * * @param o * Erlang string or list * @return string value */ public static String stringValue(final OtpErlangObject o) { if (o instanceof OtpErlangString) { final OtpErlangString s = (OtpErlangString) o; return s.stringValue(); } else if (o instanceof OtpErlangList) { final OtpErlangList l = (OtpErlangList) o; if (l.arity() == 0) { return ""; } try { return l.stringValue(); } catch (final OtpErlangException e) { ErlLogger.getInstance().error(e); return null; } } else if (o instanceof OtpErlangBinary) { final OtpErlangBinary b = (OtpErlangBinary) o; String result; result = decode(b.binaryValue(), "UTF-8"); if (result == null) { result = decode(b.binaryValue(), "ISO-8859-1"); } if (result == null) { ErlLogger.getInstance().warn( "bad binary value in stringValue (can't decode): " + o); } return result; } ErlLogger.getInstance().warn("bad value in stringValue: " + o); return null; } public static String decode(final byte[] binaryValue, final String encoding) { final Charset charset = Charset.forName(encoding); final CharsetDecoder decoder = charset.newDecoder(); try { final ByteBuffer bbuf = ByteBuffer.wrap(binaryValue); final CharBuffer cbuf = decoder.decode(bbuf); return cbuf.toString(); } catch (final CharacterCodingException e) { return null; } } public static byte[] encode(final String string, final String encoding) { final Charset charset = Charset.forName(encoding); final CharsetEncoder encoder = charset.newEncoder(); try { final CharBuffer cbuf = CharBuffer.wrap(string); final ByteBuffer bbuf = encoder.encode(cbuf); return bbuf.array(); } catch (final CharacterCodingException e) { return null; } } public static OtpErlangList listValue(final OtpErlangObject o) { if (o instanceof OtpErlangList) { return (OtpErlangList) o; } else if (o instanceof OtpErlangString) { final OtpErlangString erlangString = (OtpErlangString) o; final int[] codePoints = OtpErlangString .stringToCodePoints(erlangString.stringValue()); final OtpErlangObject elements[] = new OtpErlangObject[codePoints.length]; for (int i = 0; i < codePoints.length; i++) { elements[i] = new OtpErlangLong(codePoints[i]); } return new OtpErlangList(elements); } return null; } /** * Return true if it's the atom ok or a tuple {ok, ...} * * @param o * atom or tuple * @return true if ok */ public static boolean isOk(final OtpErlangObject o) { return isTag(o, "ok"); } /** * return true if it's the atom error or a tuple {error, ...} * * @param o * atom or tuple * @return true if error */ public static boolean isError(final OtpErlangObject o) { return isTag(o, "error"); } public static boolean isTag(final OtpErlangObject o, final String string) { OtpErlangAtom tag = null; if (o instanceof OtpErlangAtom) { tag = (OtpErlangAtom) o; } else if (o instanceof OtpErlangTuple) { final OtpErlangTuple t = (OtpErlangTuple) o; if (t.elementAt(0) instanceof OtpErlangAtom) { tag = (OtpErlangAtom) t.elementAt(0); } } return tag != null && string.equals(tag.atomValue()); } public static String ioListToString(final OtpErlangObject o) { return ioListToString(o, Integer.MAX_VALUE - 1); } public static String ioListToString(final OtpErlangObject o, final int maxLength) { StringBuilder sb = new StringBuilder(); sb = ioListToStringBuilder(o, sb, maxLength); return sb.toString(); } public static String getInputStreamAsString(final InputStream is, final String encoding) { final StringBuilder out = new StringBuilder(); final byte[] b = new byte[4096]; try { for (int n; (n = is.read(b)) != -1;) { out.append(new String(b, 0, n, encoding)); } } catch (final IOException e) { } return out.toString(); } private static StringBuilder ioListToStringBuilder(final OtpErlangObject o, StringBuilder sb, final int maxLength) { if (sb.length() >= maxLength) { return sb; } if (o instanceof OtpErlangLong) { final OtpErlangLong l = (OtpErlangLong) o; try { sb.append(l.charValue()); } catch (final OtpErlangRangeException e) { } } else if (o instanceof OtpErlangString) { final OtpErlangString s = (OtpErlangString) o; sb.append(s.stringValue()); } else if (o instanceof OtpErlangList) { final OtpErlangList l = (OtpErlangList) o; for (final OtpErlangObject i : l) { if (sb.length() < maxLength) { ioListToStringBuilder(i, sb, maxLength); } } if (sb.length() < maxLength) { ioListToStringBuilder(l.getLastTail(), sb, maxLength); } } else if (o instanceof OtpErlangBinary) { final OtpErlangBinary b = (OtpErlangBinary) o; try { final String s = new String(b.binaryValue(), "ISO-8859-1"); sb.append(s); } catch (final UnsupportedEncodingException e) { } } else if (o != null) { sb.append(o.toString()); } if (sb.length() > maxLength) { sb = new StringBuilder(sb.substring(0, maxLength)); sb.append("... <truncated>"); } return sb; } public static String normalizeSpaces(final String string) { return string.replaceAll("[\t\n\r ]+", " "); } }