/******************************************************************************* * Copyright (c) 2005, 2007 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 * *******************************************************************************/ package org2.eclipse.dltk.internal.core.util; import java.io.BufferedInputStream; import java.io.DataInput; import java.io.EOFException; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintStream; import java.io.UTFDataFormatException; import java.io.UnsupportedEncodingException; import java.util.StringTokenizer; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.ResourceAttributes; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org2.eclipse.dltk.compiler.CharOperation; import com.aptana.editor.php.epl.PHPEplPlugin; /** * This code is taken and trimmed from the DLTK project. */ @SuppressWarnings({"unchecked", "rawtypes"}) public class Util { private static final int DEFAULT_READING_SIZE = 8192; public interface Displayable { String displayString(Object o); } 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 char NEW_FORMAT_MARK = '+'; private static final char ARGUMENTS_DELIMITER = '#'; private static final String ARGUMENTS_DELIMITER_STR = String.valueOf(ARGUMENTS_DELIMITER); private static final String EMPTY_ARGUMENT = " "; //$NON-NLS-1$ public final static String UTF_8 = "UTF-8"; //$NON-NLS-1$ /** * 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(); } /** * Combines two hash codes to make a new one. */ public static int combineHashCodes(int hashCode1, int hashCode2) { return hashCode1 * 17 + hashCode2; } /** * Sort the strings in the given collection. */ private static void quickSort(String[] sortedCollection, int left, int right) { int original_left = left; int original_right = right; String mid = sortedCollection[(left + right) / 2]; do { while (sortedCollection[left].compareTo(mid) < 0) { left++; } while (mid.compareTo(sortedCollection[right]) < 0) { right--; } if (left <= right) { 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); } } /** * Compares two arrays using equals() on the elements. Neither can be null. Only the first len elements are * compared. Return false if either array is shorter than len. */ public static boolean equalArrays(Object[] a, Object[] b, int len) { if (a == b) return true; if (a.length < len || b.length < len) 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. 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(Object[] a, Object[] b) { if (a == b) return true; if (a == null || b == null) return false; 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; } private static boolean isNewProblemArgumentsFormat(String[] arguments) { for (int i = 0; i < arguments.length; ++i) { if (arguments[i].indexOf(ARGUMENTS_DELIMITER) != -1) { return true; } } return false; } /** * Put all the arguments in one String. */ public static String getProblemArgumentsForMarker(String[] arguments) { if (isNewProblemArgumentsFormat(arguments)) { return encodeProblemArguments(arguments); } StringBuffer args = new StringBuffer(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(); } /** * @param arguments * @return */ private static String encodeProblemArguments(String[] arguments) { StringBuffer args = new StringBuffer(); args.append(NEW_FORMAT_MARK); args.append(arguments.length); for (int j = 0; j < arguments.length; j++) { args.append(ARGUMENTS_DELIMITER); args.append(arguments[j].length()); args.append(ARGUMENTS_DELIMITER); args.append(arguments[j]); } return args.toString(); } public static String[] getProblemArgumentsFromMarker(String argumentsString) { if (argumentsString == null || argumentsString.length() == 0) return null; if (argumentsString.charAt(0) == NEW_FORMAT_MARK) { return decodeProblemArguments(argumentsString); } int index = argumentsString.indexOf(':'); if (index == -1) return null; int length = argumentsString.length(); int numberOfArg; try { numberOfArg = Integer.parseInt(argumentsString.substring(0, index)); } catch (NumberFormatException e) { return null; } argumentsString = argumentsString.substring(index + 1, length); String[] args = new String[length]; int count = 0; StringTokenizer tokenizer = new StringTokenizer(argumentsString, ARGUMENTS_DELIMITER_STR); while (tokenizer.hasMoreTokens()) { String argument = tokenizer.nextToken(); if (argument.equals(EMPTY_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; } /** * @param s * @return */ private static String[] decodeProblemArguments(String s) { int begin = 1; int pos = s.indexOf(ARGUMENTS_DELIMITER, begin); if (pos == -1) { return null; } final int numberOfArg; try { numberOfArg = Integer.parseInt(s.substring(begin, pos)); } catch (NumberFormatException e) { return null; } begin = pos; final String[] args = new String[numberOfArg]; final int length = s.length(); for (int i = 0; i < numberOfArg; ++i) { if (begin >= length || s.charAt(begin) != ARGUMENTS_DELIMITER) { return null; } ++begin; pos = s.indexOf(ARGUMENTS_DELIMITER, begin); if (pos == -1) { return null; } final int argLen; try { argLen = Integer.parseInt(s.substring(begin, pos)); } catch (NumberFormatException e) { return null; } begin = pos + 1; if (begin + argLen > length) { return null; } args[i] = s.substring(begin, begin + argLen); begin += argLen; } if (begin != length) { return null; } return args; } /** * 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(char[] text) { // find the first line separator int length = text.length; if (length > 0) { char nextChar = text[0]; for (int i = 0; i < length; i++) { char currentChar = nextChar; nextChar = i < length - 1 ? text[i + 1] : ' '; switch (currentChar) { case '\n': return "\n"; //$NON-NLS-1$ case '\r': return nextChar == '\n' ? "\r\n" : "\r"; //$NON-NLS-1$ //$NON-NLS-2$ } } } // not found return null; } public static void verbose(String log) { verbose(log, System.out); } public static synchronized void verbose(String log, PrintStream printStream) { int start = 0; do { int end = log.indexOf('\n', start); printStream.print(Thread.currentThread()); printStream.print(" "); //$NON-NLS-1$ printStream.print(log.substring(start, end == -1 ? log.length() : end + 1)); start = end + 1; } while (start != 0); printStream.println(); } private static boolean isFatalException(CoreException e) { return e.getCause() instanceof FileNotFoundException; } public static char[] getResourceContentsAsCharArray(IFile file, String encoding) throws CoreException { // Get resource contents InputStream stream = null; int tryCount = 10; try { while (stream == null) { try { stream = file.getContents(true); } catch (CoreException e) { // Some times for RSE we can get here if connection is not // established yet, or if connection are lost. if (isFatalException(e) || --tryCount == 0) { throw new CoreException(new Status(IStatus.ERROR, PHPEplPlugin.PLUGIN_ID, "Could not read the file content")); //$NON-NLS-1$ } } } char[] data = getInputStreamAsCharArray(stream, -1, encoding); return data; } catch (IOException ioe) { throw new CoreException(new Status(IStatus.ERROR, PHPEplPlugin.PLUGIN_ID, ioe.getMessage())); } finally { if (stream != null) { try { stream.close(); } catch (IOException e) { // ignore } } } } /* * a character array. If a length is specified (ie. if length != -1), this represents the number of bytes in the * stream. 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; try { try { reader = encoding == null ? new InputStreamReader(toBufferedInputStream(stream)) : new InputStreamReader(stream, encoding); } catch (UnsupportedEncodingException e) { // encoding is not supported reader = new InputStreamReader(toBufferedInputStream(stream)); } char[] contents; int totalRead = 0; if (length == -1) { contents = CharOperation.NO_CHAR; } else { // length is a good guess when the encoding produces less or the // same amount of characters than the file length contents = new char[length]; // best guess } while (true) { int amountRequested; if (totalRead < length) { // until known length is met, reuse same array sized eagerly amountRequested = length - totalRead; } else { // reading beyond known length int current = reader.read(); if (current < 0) break; amountRequested = Math.max(stream.available(), DEFAULT_READING_SIZE); // read at least 8K // resize contents if needed if (totalRead + 1 + amountRequested > contents.length) System.arraycopy(contents, 0, contents = new char[totalRead + 1 + amountRequested], 0, totalRead); // add current character contents[totalRead++] = (char) current; // coming from // totalRead==length } // read as many chars as possible int amountRead = reader.read(contents, totalRead, amountRequested); if (amountRead < 0) break; totalRead += amountRead; } // Do not keep first character for UTF-8 BOM encoding int start = 0; if (totalRead > 0 && UTF_8.equals(encoding)) { if (contents[0] == 0xFEFF) { // if BOM char then skip totalRead--; start = 1; } } // resize contents if necessary if (totalRead < contents.length) System.arraycopy(contents, start, contents = new char[totalRead], 0, totalRead); return contents; } finally { if (reader != null) { reader.close(); } } } private static InputStream toBufferedInputStream(InputStream stream) { if (stream instanceof BufferedInputStream) { return stream; } else { return new BufferedInputStream(stream, DEFAULT_READING_SIZE); } } /** * Returns the toString() of the given full path minus the first given number of segments. The returned string is * always a relative path (it has no leading slash) */ public static String relativePath(IPath fullPath, int skipSegmentCount) { boolean hasTrailingSeparator = fullPath.hasTrailingSeparator(); String[] segments = fullPath.segments(); // compute length int length = 0; int max = segments.length; if (max > skipSegmentCount) { for (int i1 = skipSegmentCount; i1 < max; i1++) { length += segments[i1].length(); } // add the separator lengths length += max - skipSegmentCount - 1; } if (hasTrailingSeparator) length++; char[] result = new char[length]; int offset = 0; int len = segments.length - 1; if (len >= skipSegmentCount) { // append all but the last segment, with separators for (int i = skipSegmentCount; i < len; i++) { int size = segments[i].length(); segments[i].getChars(0, size, result, offset); offset += size; result[offset++] = '/'; } // append the last segment int size = segments[len].length(); segments[len].getChars(0, size, result, offset); offset += size; } if (hasTrailingSeparator) result[offset++] = '/'; return new String(result); } private static void quickSort(char[][] list, int left, int right) { int original_left = left; int original_right = right; char[] mid = list[(left + right) / 2]; do { while (compare(list[left], mid) < 0) { left++; } while (compare(mid, list[right]) < 0) { right--; } if (left <= right) { 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(Comparable[] sortedCollection, int left, int right) { int original_left = left; int original_right = right; Comparable mid = sortedCollection[(left + right) / 2]; do { while (sortedCollection[left].compareTo(mid) < 0) { left++; } while (mid.compareTo(sortedCollection[right]) < 0) { right--; } if (left <= right) { 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(int[] list, int left, int right) { int original_left = left; int original_right = right; int mid = list[(left + right) / 2]; do { while (list[left] < mid) { left++; } while (mid < list[right]) { right--; } if (left <= right) { 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(Object[] sortedCollection, int left, int right, Comparer comparer) { int original_left = left; int original_right = right; Object mid = sortedCollection[(left + right) / 2]; do { while (comparer.compare(sortedCollection[left], mid) < 0) { left++; } while (comparer.compare(mid, sortedCollection[right]) < 0) { right--; } if (left <= right) { 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); } } public static void sort(char[][] list) { if (list.length > 1) quickSort(list, 0, list.length - 1); } /** * Sorts an array of Comparable objects in place. */ public static void sort(Comparable[] objects) { if (objects.length > 1) quickSort(objects, 0, objects.length - 1); } public static void sort(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(Object[] objects, Comparer comparer) { if (objects.length > 1) quickSort(objects, 0, objects.length - 1, comparer); } /** * Sorts an array of strings in place using quicksort. */ public static void sort(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(Comparable[] objects) { int len = objects.length; 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(Object[] objects, Comparer comparer) { int len = objects.length; 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(String[] objects) { int len = objects.length; String[] copy = new String[len]; System.arraycopy(objects, 0, copy, 0, len); sort(copy); return copy; } /** * 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(byte[] a, byte[] b) { if (a == b) return 0; if (a == null) return -1; if (b == null) return 1; int len = Math.min(a.length, b.length); for (int i = 0; i < len; ++i) { 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(char[] str1, char[] str2) { int len1 = str1.length; int len2 = str2.length; int n = Math.min(len1, len2); int i = 0; while (n-- != 0) { char c1 = str1[i]; char c2 = str2[i++]; if (c1 != c2) { return c1 - c2; } } return len1 - len2; } /** * Returns true if the given folder name is valid for a package, false if it is not. */ public static boolean isValidFolderNameForPackage(String folderName) { // TODO check if folder is valid for a package return true; } /** * Return a new array which is the split of the given string using the given divider. The given end is exclusive and * the given start is inclusive. <br> * <br> * For example: * <ol> * <li> * * <pre> * divider = 'b' * string = "abbaba" * start = 2 * end = 5 * result => { "", "a", "" } * </pre> * * </li> * </ol> * * @param divider * the given divider * @param string * the given string * @param start * the given starting index * @param end * the given ending index * @return a new array which is the split of the given string using the given divider * @throws ArrayIndexOutOfBoundsException * if start is lower than 0 or end is greater than the array length */ public static final String[] splitOn(char divider, String string, int start, int end) { int length = string == null ? 0 : string.length(); if (length == 0 || start > end) return CharOperation.NO_STRINGS; int wordCount = 1; for (int i = start; i < end; i++) if (string.charAt(i) == divider) wordCount++; String[] split = new String[wordCount]; int last = start, currentWord = 0; for (int i = start; i < end; i++) { if (string.charAt(i) == divider) { split[currentWord++] = string.substring(last, i); last = i + 1; } } split[currentWord] = string.substring(last, end); return split; } /** * Returns the concatenation of the given array parts using the given separator between each part. <br> * <br> * For example:<br> * <ol> * <li> * * <pre> * array = {"a", "b"} * separator = '.' * => result = "a.b" * </pre> * * </li> * <li> * * <pre> * array = {} * separator = '.' * => result = "" * </pre> * * </li> * </ol> * * @param array * the given array * @param separator * the given separator * @return the concatenation of the given array parts using the given separator between each part */ public static final String concatWith(String[] array, char separator) { StringBuffer buffer = new StringBuffer(); for (int i = 0, length = array.length; i < length; i++) { buffer.append(array[i]); if (i < length - 1) buffer.append(separator); } return buffer.toString(); } /** * Returns the concatenation of the given array parts using the given separator between each part and appending the * given name at the end. <br> * <br> * For example:<br> * <ol> * <li> * * <pre> * name = "c" * array = { "a", "b" } * separator = '.' * => result = "a.b.c" * </pre> * * </li> * <li> * * <pre> * name = null * array = { "a", "b" } * separator = '.' * => result = "a.b" * </pre> * * </li> * <li> * * <pre> * name = " c" * array = null * separator = '.' * => result = "c" * </pre> * * </li> * </ol> * * @param array * the given array * @param name * the given name * @param separator * the given separator * @return the concatenation of the given array parts using the given separator between each part and appending the * given name at the end */ public static final String concatWith(String[] array, String name, char separator) { if (array == null || array.length == 0) return name; if (name == null || name.length() == 0) return concatWith(array, separator); StringBuffer buffer = new StringBuffer(); for (int i = 0, length = array.length; i < length; i++) { buffer.append(array[i]); buffer.append(separator); } buffer.append(name); return buffer.toString(); } /** * Returns a new array adding the second array at the end of first array. It answers null if the first and second * are null. If the first array is null or if it is empty, then a new array is created with second. If the second * array is null, then the first array is returned. <br> * <br> * For example: * <ol> * <li> * * <pre> * first = null * second = "a" * => result = {"a"} * </pre> * <li> * * <pre> * first = {"a"} * second = null * => result = {"a"} * </pre> * * </li> * <li> * * <pre> * first = {"a"} * second = {"b"} * => result = {"a", "b"} * </pre> * * </li> * </ol> * * @param first * the first array to concatenate * @param second * the array to add at the end of the first array * @return a new array adding the second array at the end of first array, or null if the two arrays are null. */ public static final String[] arrayConcat(String[] first, String second) { if (second == null) return first; if (first == null) return new String[] { second }; int length = first.length; if (first.length == 0) { return new String[] { second }; } String[] result = new String[length + 1]; System.arraycopy(first, 0, result, 0, length); result[length] = second; return result; } public static boolean isReadOnly(IResource resource) { if (resource != null) { ResourceAttributes resourceAttributes = resource.getResourceAttributes(); if (resourceAttributes == null) return false; // not supported on this platform for this // resource return resourceAttributes.isReadOnly(); } return true; } public static void setReadOnly(IResource resource, boolean readOnly) { ResourceAttributes resourceAttributes = resource.getResourceAttributes(); if (resourceAttributes == null) return; // not supported on this platform for this resource resourceAttributes.setReadOnly(readOnly); try { resource.setResourceAttributes(resourceAttributes); } catch (CoreException e) { // ignore } } /* * Returns whether the given compound name starts with the given prefix. Returns true if the n first elements of the * prefix are equals and the last element of the prefix is a prefix of the corresponding element in the compound * name. */ public static boolean startsWithIgnoreCase(String[] compoundName, String[] prefix) { int prefixLength = prefix.length; int nameLength = compoundName.length; if (prefixLength > nameLength) return false; for (int i = 0; i < prefixLength - 1; i++) { if (!compoundName[i].equalsIgnoreCase(prefix[i])) return false; } return compoundName[prefixLength - 1].toLowerCase().startsWith(prefix[prefixLength - 1].toLowerCase()); } /** * 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() */ public final static char[] readUTF(DataInput in) throws IOException { int utflen = in.readUnsignedShort(); char str[] = new char[utflen]; int count = 0; int strlen = 0; while (count < utflen) { 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: // xxxxxxx 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; } /** * 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(OutputStream out, char[] str) throws IOException { int strlen = str.length; int utflen = 0; for (int i = 0; i < strlen; i++) { 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++) { 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 } /** * Scans the given string for an identifier starting at the given index and returns the index of the last character. * Stop characters are: ";", ":", "<", ">", "/", ".". * * @param string * the signature string * @param start * the 0-based character index of the first character * @return the 0-based character index of the last character * @exception IllegalArgumentException * if this is not an identifier */ public static int scanIdentifier(char[] string, int start) { // need a minimum 1 char if (start >= string.length) { throw new IllegalArgumentException(); } int p = start; while (true) { char c = string[p]; if (c == '<' || c == '>' || c == ':' || c == ';' || c == '.' || c == '/') { return p - 1; } p++; if (p == string.length) { return p - 1; } } } }