/******************************************************************************* * Copyright (c) 2005, 2017 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 org.eclipse.dltk.internal.core.util; import java.io.BufferedInputStream; import java.io.DataInput; import java.io.EOFException; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintStream; import java.io.UTFDataFormatException; import java.net.URI; import java.util.StringTokenizer; import org.eclipse.core.filesystem.EFS; import org.eclipse.core.filesystem.IFileStore; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.ProjectScope; import org.eclipse.core.resources.ResourceAttributes; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.preferences.IScopeContext; import org.eclipse.core.runtime.preferences.InstanceScope; import org.eclipse.dltk.compiler.CharOperation; import org.eclipse.dltk.core.DLTKContentTypeManager; import org.eclipse.dltk.core.DLTKCore; import org.eclipse.dltk.core.DLTKLanguageManager; import org.eclipse.dltk.core.IDLTKLanguageToolkit; import org.eclipse.dltk.core.IModelElement; import org.eclipse.dltk.core.IModelStatusConstants; import org.eclipse.dltk.core.IProjectFragment; import org.eclipse.dltk.core.IScriptProject; import org.eclipse.dltk.core.IScriptProjectFilenames; import org.eclipse.dltk.core.ModelException; import org.eclipse.dltk.core.RuntimePerformanceMonitor; import org.eclipse.dltk.core.RuntimePerformanceMonitor.PerformanceNode; import org.eclipse.dltk.core.environment.EnvironmentManager; import org.eclipse.dltk.core.environment.EnvironmentPathUtils; import org.eclipse.dltk.core.environment.IEnvironment; import org.eclipse.dltk.core.environment.IFileHandle; import org.eclipse.dltk.internal.core.ModelElement; import org.eclipse.dltk.internal.core.ProjectFragment; import org.eclipse.osgi.util.NLS; public class Util { 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$ /** * Converts an array of Objects into String. */ public static String toString(Object[] objects) { return toString(objects, 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(); } /* * Add a log entry */ public static void log(Throwable e, String message) { if (e == null && message == null) { return; } Throwable nestedException; if (e instanceof ModelException && (nestedException = ((ModelException) e) .getException()) != null) { e = nestedException; } IStatus status = new Status(IStatus.ERROR, DLTKCore.PLUGIN_ID, IStatus.ERROR, message != null ? message : e.toString(), e); DLTKCore.getDefault().getLog().log(status); } /** * 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; } /** * Returns the given file's contents as a byte array. */ public static byte[] getResourceContentsAsByteArray(IFile file) throws ModelException { PerformanceNode p = RuntimePerformanceMonitor.begin(); InputStream stream = null; try { stream = new BufferedInputStream(file.getContents(true)); } catch (CoreException e) { throw new ModelException(e); } try { byte[] size = org.eclipse.dltk.compiler.util.Util .getInputStreamAsByteArray(stream, -1); p.done("#", RuntimePerformanceMonitor.IOREAD, size.length); return size; } catch (IOException e) { throw new ModelException(e, IModelStatusConstants.IO_EXCEPTION); } finally { try { stream.close(); } catch (IOException e) { // ignore } } } public static byte[] getResourceContentsAsByteArray(File file) throws ModelException { InputStream stream = null; PerformanceNode p = RuntimePerformanceMonitor.begin(); try { stream = new BufferedInputStream(new FileInputStream(file)); } catch (FileNotFoundException e) { e.printStackTrace(); return null; // throw new ModelException("getResourceContentAsByteArray"); } try { byte[] data = org.eclipse.dltk.compiler.util.Util .getInputStreamAsByteArray(stream, -1); p.done("#", RuntimePerformanceMonitor.IOREAD, data.length); return data; } catch (IOException e) { throw new ModelException(e, IModelStatusConstants.IO_EXCEPTION); } finally { try { stream.close(); } catch (IOException e) { // ignore } } } /** * Returns the line separator found in the given text. If it is null, or not * found return the line delimitor for the given project. If the project is * null, returns the line separator for the workspace. If still null, return * the system line separator. */ public static String getLineSeparator(String text, IScriptProject project) { String lineSeparator = null; // line delimiter in given text if (text != null && text.length() != 0) { lineSeparator = findLineSeparator(text.toCharArray()); if (lineSeparator != null) return lineSeparator; } // line delimiter in project preference IScopeContext[] scopeContext; if (project != null) { scopeContext = new IScopeContext[] { new ProjectScope(project.getProject()) }; lineSeparator = Platform.getPreferencesService().getString( Platform.PI_RUNTIME, Platform.PREF_LINE_SEPARATOR, null, scopeContext); if (lineSeparator != null) return lineSeparator; } // line delimiter in workspace preference scopeContext = new IScopeContext[] { InstanceScope.INSTANCE }; lineSeparator = Platform.getPreferencesService().getString( Platform.PI_RUNTIME, Platform.PREF_LINE_SEPARATOR, null, scopeContext); if (lineSeparator != null) return lineSeparator; // system line delimiter return org.eclipse.dltk.compiler.util.Util.LINE_SEPARATOR; } /** * 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(); } /** * Returns the given file's contents as a character array. */ public static char[] getResourceContentsAsCharArray(IFile file) throws ModelException { // Get encoding from file String encoding = null; try { encoding = file.getCharset(); } catch (CoreException ce) { // do not use any encoding } return getResourceContentsAsCharArray(file, encoding); } public static char[] getResourceContentsAsCharArray(IFileHandle file) throws ModelException { // Get resource contents InputStream stream = null; PerformanceNode p = RuntimePerformanceMonitor.begin(); try { stream = new BufferedInputStream(file.openInputStream(null)); } catch (Exception e) { throw new ModelException(e, IModelStatusConstants.ELEMENT_DOES_NOT_EXIST); } try { char[] data = org.eclipse.dltk.compiler.util.Util .getInputStreamAsCharArray(stream, -1, null); p.done("#", RuntimePerformanceMonitor.IOREAD, data.length); return data; } catch (IOException e) { throw new ModelException(e, IModelStatusConstants.IO_EXCEPTION); } finally { try { stream.close(); } catch (IOException e) { // ignore } } } private static boolean isFatalException(CoreException e) { return e.getCause() instanceof FileNotFoundException; } public static char[] getResourceContentsAsCharArray(IFile file, String encoding) throws ModelException { // Get resource contents PerformanceNode p = RuntimePerformanceMonitor.begin(); 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 ModelException(e, IModelStatusConstants.ELEMENT_DOES_NOT_EXIST); } IStatus status = new Status(IStatus.WARNING, DLTKCore.PLUGIN_ID, NLS.bind(Messages.Util_errorReceivingFile, file.getFullPath(), String.valueOf(tryCount)), e); DLTKCore.getDefault().getLog().log(status); } } char[] data = org.eclipse.dltk.compiler.util.Util .getInputStreamAsCharArray(stream, -1, encoding); IEnvironment env = EnvironmentManager.getEnvironment(file); p.done("#", RuntimePerformanceMonitor.IOREAD, data.length, env); return data; } catch (IOException e) { throw new ModelException(e, IModelStatusConstants.IO_EXCEPTION); } finally { if (stream != null) { try { stream.close(); } catch (IOException e) { // ignore } } } } /** * 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); } /* * Returns whether the given model element is exluded from its root's * buildpath. It doesn't check whether the root itself is on the buildpath * or not */ public static final boolean isExcluded(IModelElement element) { int elementType = element.getElementType(); switch (elementType) { case IModelElement.SCRIPT_MODEL: case IModelElement.SCRIPT_PROJECT: case IModelElement.PROJECT_FRAGMENT: return false; case IModelElement.SCRIPT_FOLDER: IProjectFragment root = (IProjectFragment) element .getAncestor(IModelElement.PROJECT_FRAGMENT); IResource resource = element.getResource(); return resource != null && isExcluded(resource, root); case IModelElement.SOURCE_MODULE: root = (IProjectFragment) element .getAncestor(IModelElement.PROJECT_FRAGMENT); resource = element.getResource(); if (resource != null && isExcluded(resource, root)) return true; return isExcluded(element.getParent()); default: IModelElement cu = element.getAncestor(IModelElement.SOURCE_MODULE); return cu != null && isExcluded(cu); } } /* * Returns whether the given resource path matches one of the * inclusion/exclusion patterns. NOTE: should not be asked directly using * pkg root pathes */ public final static boolean isExcluded(IPath resourcePath, char[][] inclusionPatterns, char[][] exclusionPatterns, boolean isFolderPath) { if (inclusionPatterns == null && exclusionPatterns == null) return false; return org.eclipse.dltk.compiler.util.Util.isExcluded( resourcePath.toString().toCharArray(), inclusionPatterns, exclusionPatterns, isFolderPath); } /* * Returns whether the given resource matches one of the exclusion patterns. * NOTE: should not be asked directly using pkg root pathes * * @see IBuildpathEntry#getExclusionPatterns */ public final static boolean isExcluded(IResource resource, char[][] inclusionPatterns, char[][] exclusionPatterns) { IPath path = resource.getFullPath(); // ensure that folders are only excluded if all of their children are // excluded int resourceType = resource.getType(); return isExcluded(path, inclusionPatterns, exclusionPatterns, resourceType == IResource.FOLDER || resourceType == IResource.PROJECT); } public final static boolean isExcluded(IResource resource, IProjectFragment fragment) { IPath path = resource.getFullPath(); // ensure that folders are only excluded if all of their children are // excluded int resourceType = resource.getType(); return isExcluded(path, fragment, resourceType == IResource.FOLDER || resourceType == IResource.PROJECT); } public static boolean isValidSourceModule(IModelElement parent, IResource resource) { IDLTKLanguageToolkit toolkit = DLTKLanguageManager .getLanguageToolkit(parent); if (toolkit != null) { return DLTKContentTypeManager.isValidResourceForContentType(toolkit, resource); } else { toolkit = DLTKLanguageManager.findToolkitForResource(resource); if (toolkit != null) { return DLTKContentTypeManager .isValidResourceForContentType(toolkit, resource); } return false; } } public static boolean isValidSourceModule(IModelElement parent, IPath path) { IDLTKLanguageToolkit toolkit = DLTKLanguageManager .getLanguageToolkit(parent); if (toolkit != null) { return DLTKContentTypeManager.isValidFileNameForContentType(toolkit, path); } else { toolkit = DLTKLanguageManager.findToolkit(path); if (toolkit != null) { return DLTKContentTypeManager .isValidFileNameForContentType(toolkit, path); } return false; } } public static boolean isValidSourcePackageName(IModelElement parent, IPath path) { IDLTKLanguageToolkit toolkit = DLTKLanguageManager .getLanguageToolkit(parent); if (toolkit != null) { if (EnvironmentPathUtils.isFull(path)) { path = EnvironmentPathUtils.getLocalPath(path); } return toolkit.validateSourcePackage(path, EnvironmentManager.getEnvironment(parent)); } return false; } public static boolean isValidSourceModuleName(IModelElement parent, String name) { IDLTKLanguageToolkit toolkit = DLTKLanguageManager .getLanguageToolkit(parent); if (toolkit != null) { return DLTKContentTypeManager.isValidFileNameForContentType(toolkit, name); } else { return false; } } /** * This is deprecated since the main part of the implementation * DLTKLanguageManager.findToolkit() is deprecated too. * * @deprecated */ @Deprecated public static boolean isValidSourceModule(IResource res) { IDLTKLanguageToolkit toolkit = DLTKLanguageManager .findToolkitForResource(res); if (toolkit != null) { return DLTKContentTypeManager.isValidResourceForContentType(toolkit, res); } return false; } /* * Converts the given URI to a local file. Use the existing file if the uri * is on the local file system. Otherwise fetch it. Returns null if unable * to fetch it. */ public static File toLocalFile(URI uri, IProgressMonitor monitor) throws CoreException { IFileStore fileStore = EFS.getStore(uri); File localFile = fileStore.toLocalFile(EFS.NONE, monitor); if (localFile == null) // non local file system localFile = fileStore.toLocalFile(EFS.CACHE, monitor); return localFile; } 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 elements based on their toStringWithAncestors(), * returning a new array with the sorted items. The original array is left * untouched. */ public static IModelElement[] sortCopy(IModelElement[] elements) { int len = elements.length; IModelElement[] copy = new IModelElement[len]; System.arraycopy(elements, 0, copy, 0, len); sort(copy, (a, b) -> ((org.eclipse.dltk.internal.core.ModelElement) a) .toStringWithAncestors() .compareTo(((ModelElement) b).toStringWithAncestors())); 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; } /** * @param container * @param folderName * @return */ public static boolean isValidFolderNameForPackage(IContainer container, String folderName) { if (container.getType() == IResource.PROJECT && IScriptProjectFilenames.SETTINGS_FOLDER_NAME .equals(folderName)) { return false; } return isValidFolderNameForPackage(folderName); } /** * 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; } /** * Returns whether the local file system supports accessing and modifying * the given attribute. */ protected static boolean isAttributeSupported(int attribute) { return (EFS.getLocalFileSystem().attributes() & attribute) != 0; } /** * Returns whether the local file system supports accessing and modifying * the read only flag. */ public static boolean isReadOnlySupported() { return isAttributeSupported(EFS.ATTRIBUTE_READ_ONLY); } public static boolean isReadOnly(IResource resource) { if (isReadOnlySupported()) { ResourceAttributes resourceAttributes = resource .getResourceAttributes(); if (resourceAttributes == null) return false; // not supported on this platform for this // resource return resourceAttributes.isReadOnly(); } return false; } public static void setReadOnly(IResource resource, boolean readOnly) { if (isReadOnlySupported()) { 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 } } } public static boolean equalsIgnoreExtension(String elementName, String cuName) { // TODO: Add more complex check here. if (DLTKCore.DEBUG) { System.out.println("//TODO: Add more complex check here."); //$NON-NLS-1$ } if (elementName.startsWith(cuName)) { return true; } return false; } /* * 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; } } } public static boolean isExcluded(IPath path, IProjectFragment root, boolean isFolderPath) { char[][] inclusion = null; char[][] exclusion = null; if (root instanceof ProjectFragment) { ProjectFragment projectFragment = (ProjectFragment) root; inclusion = projectFragment.fullInclusionPatternChars(); exclusion = projectFragment.fullExclusionPatternChars(); } return isExcluded(path, inclusion, exclusion, isFolderPath); } }