| /******************************************************************************* |
| * Copyright (c) 2007, 2013 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.eclipse.pde.api.tools.internal.util; |
| |
| import java.io.BufferedInputStream; |
| import java.io.BufferedOutputStream; |
| import java.io.BufferedWriter; |
| import java.io.ByteArrayInputStream; |
| import java.io.ByteArrayOutputStream; |
| import java.io.File; |
| import java.io.FileFilter; |
| import java.io.FileInputStream; |
| import java.io.FileNotFoundException; |
| import java.io.FileOutputStream; |
| import java.io.FileWriter; |
| import java.io.FilenameFilter; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.LineNumberReader; |
| import java.io.PrintWriter; |
| import java.io.StringReader; |
| import java.io.UnsupportedEncodingException; |
| import java.lang.reflect.Field; |
| import java.nio.ByteBuffer; |
| import java.nio.CharBuffer; |
| import java.nio.charset.Charset; |
| import java.nio.charset.CharsetDecoder; |
| import java.nio.charset.CodingErrorAction; |
| import java.nio.charset.IllegalCharsetNameException; |
| import java.nio.charset.UnsupportedCharsetException; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Comparator; |
| import java.util.Enumeration; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Properties; |
| import java.util.Set; |
| import java.util.StringTokenizer; |
| import java.util.jar.JarFile; |
| import java.util.regex.Pattern; |
| import java.util.regex.PatternSyntaxException; |
| import java.util.stream.Stream; |
| import java.util.zip.ZipEntry; |
| import java.util.zip.ZipException; |
| import java.util.zip.ZipFile; |
| import java.util.zip.ZipInputStream; |
| |
| import javax.xml.parsers.DocumentBuilder; |
| import javax.xml.parsers.DocumentBuilderFactory; |
| import javax.xml.parsers.FactoryConfigurationError; |
| import javax.xml.parsers.ParserConfigurationException; |
| import javax.xml.transform.OutputKeys; |
| import javax.xml.transform.Transformer; |
| import javax.xml.transform.TransformerException; |
| import javax.xml.transform.TransformerFactory; |
| import javax.xml.transform.dom.DOMSource; |
| import javax.xml.transform.stream.StreamResult; |
| |
| import org.eclipse.core.filebuffers.FileBuffers; |
| import org.eclipse.core.filebuffers.ITextFileBufferManager; |
| import org.eclipse.core.filebuffers.LocationKind; |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IMarker; |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.resources.IncrementalProjectBuilder; |
| import org.eclipse.core.resources.ResourcesPlugin; |
| import org.eclipse.core.runtime.Assert; |
| 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.NullProgressMonitor; |
| import org.eclipse.core.runtime.OperationCanceledException; |
| import org.eclipse.core.runtime.Path; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.runtime.SubMonitor; |
| import org.eclipse.core.runtime.jobs.Job; |
| import org.eclipse.jdt.core.Flags; |
| import org.eclipse.jdt.core.ICompilationUnit; |
| import org.eclipse.jdt.core.IField; |
| import org.eclipse.jdt.core.IJavaElement; |
| import org.eclipse.jdt.core.IJavaProject; |
| import org.eclipse.jdt.core.IMember; |
| import org.eclipse.jdt.core.IMethod; |
| import org.eclipse.jdt.core.IType; |
| import org.eclipse.jdt.core.ITypeParameter; |
| import org.eclipse.jdt.core.JavaCore; |
| import org.eclipse.jdt.core.JavaModelException; |
| import org.eclipse.jdt.core.Signature; |
| import org.eclipse.jdt.internal.compiler.codegen.ConstantPool; |
| import org.eclipse.jdt.launching.IVMInstall; |
| import org.eclipse.jdt.launching.JavaRuntime; |
| import org.eclipse.jdt.launching.LibraryLocation; |
| import org.eclipse.jdt.launching.environments.ExecutionEnvironmentDescription; |
| import org.eclipse.jface.text.IDocument; |
| import org.eclipse.osgi.util.NLS; |
| import org.eclipse.pde.api.tools.internal.FilterStore; |
| import org.eclipse.pde.api.tools.internal.IApiCoreConstants; |
| import org.eclipse.pde.api.tools.internal.builder.BuildState; |
| import org.eclipse.pde.api.tools.internal.provisional.ApiPlugin; |
| import org.eclipse.pde.api.tools.internal.provisional.Factory; |
| import org.eclipse.pde.api.tools.internal.provisional.IApiMarkerConstants; |
| import org.eclipse.pde.api.tools.internal.provisional.IRequiredComponentDescription; |
| import org.eclipse.pde.api.tools.internal.provisional.VisibilityModifiers; |
| import org.eclipse.pde.api.tools.internal.provisional.comparator.DeltaVisitor; |
| import org.eclipse.pde.api.tools.internal.provisional.comparator.IDelta; |
| import org.eclipse.pde.api.tools.internal.provisional.descriptors.IReferenceTypeDescriptor; |
| import org.eclipse.pde.api.tools.internal.provisional.model.IApiBaseline; |
| import org.eclipse.pde.api.tools.internal.provisional.model.IApiComponent; |
| import org.eclipse.pde.api.tools.internal.provisional.model.IApiElement; |
| import org.eclipse.pde.api.tools.internal.provisional.model.IApiType; |
| import org.eclipse.pde.api.tools.internal.provisional.model.IApiTypeRoot; |
| import org.eclipse.pde.api.tools.internal.search.SkippedComponent; |
| import org.objectweb.asm.Opcodes; |
| import org.osgi.framework.Version; |
| import org.w3c.dom.Document; |
| import org.w3c.dom.Element; |
| import org.xml.sax.SAXException; |
| import org.xml.sax.helpers.DefaultHandler; |
| |
| /** |
| * A Utility class to use for API tools |
| * |
| * @since 1.0.0 |
| */ |
| public final class Util { |
| |
| public static final String DOT_TGZ = ".tgz"; //$NON-NLS-1$ |
| public static final String DOT_TAR_GZ = ".tar.gz"; //$NON-NLS-1$ |
| public static final String DOT_JAR = ".jar"; //$NON-NLS-1$ |
| public static final String DOT_ZIP = ".zip"; //$NON-NLS-1$ |
| |
| public static final char VERSION_SEPARATOR = '('; |
| |
| /** |
| * Class that runs a build in the workspace or the given project |
| */ |
| private static final class BuildJob extends Job { |
| private final IProject[] fProjects; |
| private int fBuildType; |
| |
| /** |
| * Constructor |
| * |
| * @param name |
| * @param project |
| */ |
| BuildJob(String name, IProject[] projects) { |
| this(name, projects, IncrementalProjectBuilder.FULL_BUILD); |
| } |
| |
| BuildJob(String name, IProject[] projects, int buildType) { |
| super(name); |
| fProjects = projects; |
| this.fBuildType = buildType; |
| } |
| |
| @Override |
| public boolean belongsTo(Object family) { |
| return ResourcesPlugin.FAMILY_MANUAL_BUILD == family; |
| } |
| |
| /** |
| * Returns if this build job is covered by another build job |
| * |
| * @param other |
| * @return true if covered by another build job, false otherwise |
| */ |
| public boolean isCoveredBy(BuildJob other) { |
| if (other.fProjects == null) { |
| return true; |
| } |
| if (this.fProjects != null) { |
| for (int i = 0, max = this.fProjects.length; i < max; i++) { |
| if (!other.contains(this.fProjects[i])) { |
| return false; |
| } |
| } |
| return true; |
| } |
| return false; |
| } |
| |
| public boolean contains(IProject project) { |
| if (project == null) { |
| return false; |
| } |
| for (IProject fProject : this.fProjects) { |
| if (project.equals(fProject)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| @Override |
| protected IStatus run(IProgressMonitor monitor) { |
| synchronized (getClass()) { |
| if (monitor.isCanceled()) { |
| return Status.CANCEL_STATUS; |
| } |
| // cancelBuild(ResourcesPlugin.FAMILY_AUTO_BUILD); |
| cancelBuild(ResourcesPlugin.FAMILY_MANUAL_BUILD); |
| } |
| try { |
| if (fProjects != null) { |
| SubMonitor localmonitor = SubMonitor.convert(monitor, UtilMessages.Util_0, fProjects.length); |
| for (IProject currentProject : fProjects) { |
| if (this.fBuildType == IncrementalProjectBuilder.FULL_BUILD) { |
| BuildState.setLastBuiltState(currentProject, null); |
| } |
| localmonitor.subTask(NLS.bind(UtilMessages.Util_5, currentProject.getName())); |
| if (ResourcesPlugin.getWorkspace().isAutoBuilding()) { |
| currentProject.touch(null); |
| } else { |
| currentProject.build(IncrementalProjectBuilder.INCREMENTAL_BUILD, localmonitor.split(1)); |
| } |
| } |
| } |
| } catch (CoreException e) { |
| return new Status(e.getStatus().getSeverity(), ApiPlugin.PLUGIN_ID, ApiPlugin.INTERNAL_ERROR, UtilMessages.Util_builder_errorMessage, e); |
| } catch (OperationCanceledException e) { |
| return Status.CANCEL_STATUS; |
| } finally { |
| monitor.done(); |
| } |
| return Status.OK_STATUS; |
| } |
| |
| private void cancelBuild(Object jobfamily) { |
| Job[] buildJobs = Job.getJobManager().find(jobfamily); |
| for (Job curr : buildJobs) { |
| if (curr != this && curr instanceof BuildJob) { |
| BuildJob job = (BuildJob) curr; |
| if (job.isCoveredBy(this)) { |
| curr.cancel(); // cancel all other build jobs of our |
| // kind |
| } |
| } |
| } |
| } |
| } |
| |
| public static final String EMPTY_STRING = "";//$NON-NLS-1$ |
| public static final String DEFAULT_PACKAGE_NAME = EMPTY_STRING; |
| public static final String MANIFEST_NAME = "MANIFEST.MF"; //$NON-NLS-1$ |
| |
| public static final String DOT_CLASS_SUFFIX = ".class"; //$NON-NLS-1$ |
| public static final String DOT_JAVA_SUFFIX = ".java"; //$NON-NLS-1$ |
| |
| /** |
| * Constant representing the default size to read from an input stream |
| */ |
| private static final int DEFAULT_READING_SIZE = 8192; |
| |
| private static final String JAVA_LANG_OBJECT = "java.lang.Object"; //$NON-NLS-1$ |
| private static final String JAVA_LANG_RUNTIMEEXCEPTION = "java.lang.RuntimeException"; //$NON-NLS-1$ |
| public static final String LINE_DELIMITER = System.getProperty("line.separator"); //$NON-NLS-1$ |
| |
| public static final String UNKNOWN_ELEMENT_KIND = "UNKNOWN_ELEMENT_KIND"; //$NON-NLS-1$ |
| |
| public static final String UNKNOWN_FLAGS = "UNKNOWN_FLAGS"; //$NON-NLS-1$ |
| public static final String UNKNOWN_KIND = "UNKNOWN_KIND"; //$NON-NLS-1$ |
| public static final String UNKNOWN_VISIBILITY = "UNKNOWN_VISIBILITY"; //$NON-NLS-1$ |
| public static final String ISO_8859_1 = "ISO-8859-1"; //$NON-NLS-1$ |
| public static final String REGULAR_EXPRESSION_START = "R:"; //$NON-NLS-1$ |
| |
| // Trace for delete operation |
| /* |
| * Maximum time wasted repeating delete operations while running JDT/Core |
| * tests. |
| */ |
| private static int DELETE_MAX_TIME = 0; |
| /** |
| * Trace deletion operations while running JDT/Core tests. |
| */ |
| private static boolean DELETE_DEBUG = false; |
| /** |
| * Maximum of time in milliseconds to wait in deletion operation while |
| * running JDT/Core tests. Default is 10 seconds. This number cannot exceed |
| * 1 minute (i.e. 60000). <br> |
| * To avoid too many loops while waiting, the ten first ones are done |
| * waiting 10ms before repeating, the ten loops after are done waiting 100ms |
| * and the other loops are done waiting 1s... |
| */ |
| private static int DELETE_MAX_WAIT = 10000; |
| |
| public static final IPath MANIFEST_PROJECT_RELATIVE_PATH = new Path(JarFile.MANIFEST_NAME); |
| |
| public static final String ORG_ECLIPSE_SWT = "org.eclipse.swt"; //$NON-NLS-1$ |
| |
| /** |
| * Throws an exception with the given message and underlying exception. |
| * |
| * @param message error message |
| * @param exception underlying exception, or <code>null</code> |
| * @throws CoreException |
| */ |
| private static void abort(String message, Throwable exception) throws CoreException { |
| IStatus status = new Status(IStatus.ERROR, ApiPlugin.PLUGIN_ID, message, exception); |
| throw new CoreException(status); |
| } |
| |
| /** |
| * Appends a property to the given string buffer with the given key and |
| * value in the format "key=value\n". |
| * |
| * @param buffer buffer to append to |
| * @param key key |
| * @param value value |
| */ |
| private static void appendProperty(StringBuffer buffer, String key, String value) { |
| buffer.append(key); |
| buffer.append('='); |
| buffer.append(value); |
| buffer.append('\n'); |
| } |
| |
| /** |
| * Collects all of the deltas from the given parent delta |
| * |
| * @param delta |
| * @return |
| */ |
| public static List<IDelta> collectAllDeltas(IDelta delta) { |
| final List<IDelta> list = new ArrayList<>(); |
| delta.accept(new DeltaVisitor() { |
| @Override |
| public void endVisit(IDelta localDelta) { |
| if (localDelta.getChildren().length == 0) { |
| list.add(localDelta); |
| } |
| super.endVisit(localDelta); |
| } |
| }); |
| return list; |
| } |
| |
| /** |
| * Collects files into the collector array list |
| * |
| * @param root the root to collect the files from |
| * @param collector the collector to place matches into |
| * @param fileFilter the filter for files or <code>null</code> to accept all |
| * files |
| */ |
| private static void collectAllFiles(File root, ArrayList<File> collector, FileFilter fileFilter) { |
| File[] files = root.listFiles(fileFilter); |
| for (final File currentFile : files) { |
| if (currentFile.isDirectory()) { |
| collectAllFiles(currentFile, collector, fileFilter); |
| } else { |
| collector.add(currentFile); |
| } |
| } |
| } |
| |
| /** |
| * Returns all of the API projects in the workspace |
| * |
| * @return all of the API projects in the workspace or <code>null</code> if |
| * there are none. |
| */ |
| public static IProject[] getApiProjects() { |
| IProject[] allProjects = ResourcesPlugin.getWorkspace().getRoot().getProjects(); |
| ArrayList<IProject> temp = new ArrayList<>(); |
| IProject project = null; |
| for (IProject allProject : allProjects) { |
| project = allProject; |
| if (project.isAccessible()) { |
| try { |
| if (project.hasNature(org.eclipse.pde.api.tools.internal.provisional.ApiPlugin.NATURE_ID)) { |
| temp.add(project); |
| } |
| } catch (CoreException e) { |
| } |
| } |
| } |
| IProject[] projects = null; |
| if (temp.size() != 0) { |
| projects = new IProject[temp.size()]; |
| temp.toArray(projects); |
| } |
| return projects; |
| } |
| |
| /** |
| * Returns all of the API projects in the workspace |
| * |
| * @param sourcelevel |
| * @return all of the API projects in the workspace or <code>null</code> if |
| * there are none. |
| */ |
| public static IProject[] getApiProjectsMinSourceLevel(String sourcelevel) { |
| IProject[] allProjects = ResourcesPlugin.getWorkspace().getRoot().getProjects(); |
| ArrayList<IProject> temp = new ArrayList<>(); |
| IProject project = null; |
| for (IProject allProject : allProjects) { |
| project = allProject; |
| if (project.isAccessible()) { |
| try { |
| if (project.hasNature(org.eclipse.pde.api.tools.internal.provisional.ApiPlugin.NATURE_ID)) { |
| IJavaProject jp = JavaCore.create(project); |
| String src = jp.getOption(JavaCore.COMPILER_SOURCE, true); |
| if (src != null && src.compareTo(sourcelevel) >= 0) { |
| temp.add(project); |
| } |
| } |
| } catch (CoreException e) { |
| } |
| } |
| } |
| IProject[] projects = null; |
| if (temp.size() != 0) { |
| projects = new IProject[temp.size()]; |
| temp.toArray(projects); |
| } |
| return projects; |
| } |
| |
| /** |
| * Copies the given file to the new file |
| * |
| * @param file |
| * @param newFile |
| * @return if the copy succeeded |
| */ |
| public static boolean copy(File file, File newFile) { |
| byte[] bytes = null; |
| BufferedInputStream inputStream = null; |
| try { |
| inputStream = new BufferedInputStream(new FileInputStream(file)); |
| bytes = Util.getInputStreamAsByteArray(inputStream, -1); |
| } catch (FileNotFoundException e) { |
| ApiPlugin.log(e); |
| } catch (IOException e) { |
| ApiPlugin.log(e); |
| } finally { |
| if (inputStream != null) { |
| try { |
| inputStream.close(); |
| } catch (IOException e) { |
| ApiPlugin.log(e); |
| } |
| } |
| } |
| if (bytes != null) { |
| BufferedOutputStream outputStream = null; |
| try { |
| outputStream = new BufferedOutputStream(new FileOutputStream(newFile)); |
| outputStream.write(bytes); |
| outputStream.flush(); |
| } catch (FileNotFoundException e) { |
| ApiPlugin.log(e); |
| } catch (IOException e) { |
| ApiPlugin.log(e); |
| } finally { |
| if (outputStream != null) { |
| try { |
| outputStream.close(); |
| } catch (IOException e) { |
| // ignore |
| } |
| } |
| } |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Creates an EE file for the given JRE and specified EE id |
| * |
| * @param jre |
| * @param eeid |
| * @return |
| * @throws IOException |
| */ |
| public static File createEEFile(IVMInstall jre, String eeid) throws IOException { |
| String string = Util.generateEEContents(jre, eeid); |
| File eeFile = createTempFile("eed", ".ee"); //$NON-NLS-1$ //$NON-NLS-2$ |
| FileOutputStream outputStream = null; |
| try { |
| outputStream = new FileOutputStream(eeFile); |
| outputStream.write(string.getBytes(IApiCoreConstants.UTF_8)); |
| } finally { |
| if (outputStream != null) { |
| outputStream.close(); |
| } |
| } |
| return eeFile; |
| } |
| |
| /** |
| * Returns whether the objects are equal, accounting for either one being |
| * <code>null</code>. |
| * |
| * @param o1 |
| * @param o2 |
| * @return whether the objects are equal, or both are <code>null</code> |
| */ |
| public static boolean equalsOrNull(Object o1, Object o2) { |
| if (o1 == null) { |
| return o2 == null; |
| } |
| return o1.equals(o2); |
| } |
| |
| /** |
| * Returns an execution environment description for the given VM. |
| * |
| * @param vm JRE to create an definition for |
| * @return an execution environment description for the given VM |
| * @throws IOException if unable to generate description |
| */ |
| public static String generateEEContents(IVMInstall vm, String eeId) throws IOException { |
| StringBuffer buffer = new StringBuffer(); |
| appendProperty(buffer, ExecutionEnvironmentDescription.JAVA_HOME, vm.getInstallLocation().getCanonicalPath()); |
| StringBuffer paths = new StringBuffer(); |
| LibraryLocation[] libraryLocations = JavaRuntime.getLibraryLocations(vm); |
| for (int i = 0; i < libraryLocations.length; i++) { |
| LibraryLocation lib = libraryLocations[i]; |
| paths.append(lib.getSystemLibraryPath().toOSString()); |
| if (i < (libraryLocations.length - 1)) { |
| paths.append(File.pathSeparatorChar); |
| } |
| } |
| appendProperty(buffer, ExecutionEnvironmentDescription.BOOT_CLASS_PATH, paths.toString()); |
| appendProperty(buffer, ExecutionEnvironmentDescription.CLASS_LIB_LEVEL, eeId); |
| return buffer.toString(); |
| } |
| |
| /** |
| * Returns an array of all of the files from the given root that are |
| * accepted by the given file filter. If the file filter is null all files |
| * within the given root are returned. |
| * |
| * @param root |
| * @param fileFilter |
| * @return the list of files from within the given root |
| */ |
| public static File[] getAllFiles(File root, FileFilter fileFilter) { |
| ArrayList<File> files = new ArrayList<>(); |
| if (root.isDirectory()) { |
| collectAllFiles(root, files, fileFilter); |
| File[] result = new File[files.size()]; |
| files.toArray(result); |
| return result; |
| } |
| return null; |
| } |
| |
| /** |
| * Returns a build job that will perform a full build on the given projects. |
| * |
| * If <code>projects</code> are null, then an AssertionFailedException is |
| * thrown |
| * |
| * @param projects the projects to build |
| * @return the build job |
| * @throws AssertionFailedException if the given projects are null |
| */ |
| public static Job getBuildJob(final IProject[] projects) { |
| Assert.isNotNull(projects); |
| Job buildJob = new BuildJob(UtilMessages.Util_4, projects); |
| buildJob.setRule(ResourcesPlugin.getWorkspace().getRuleFactory().buildRule()); |
| buildJob.setUser(true); |
| return buildJob; |
| } |
| |
| /** |
| * Returns a build job that will return the build that corresponds to the |
| * given build kind on the given projects. |
| * |
| * If <code>projects</code> are null, then an AssertionFailedException is |
| * thrown |
| * |
| * @param projects the projects to build |
| * @param buildKind the given build kind |
| * @return the build job |
| * @throws AssertionFailedException if the given projects are null |
| */ |
| public static Job getBuildJob(final IProject[] projects, int buildKind) { |
| Assert.isNotNull(projects); |
| Job buildJob = new BuildJob(UtilMessages.Util_4, projects, buildKind); |
| buildJob.setRule(ResourcesPlugin.getWorkspace().getRuleFactory().buildRule()); |
| buildJob.setUser(true); |
| return buildJob; |
| } |
| |
| /** |
| * Returns a result of searching the given components for class file with |
| * the given type name. |
| * |
| * @param components API components to search or <code>null</code> if none |
| * @param typeName type to search for |
| * @return class file or <code>null</code> if none found |
| */ |
| public static IApiTypeRoot getClassFile(IApiComponent[] components, String typeName) { |
| if (components == null) { |
| return null; |
| } |
| for (IApiComponent apiComponent : components) { |
| if (apiComponent != null) { |
| try { |
| IApiTypeRoot classFile = apiComponent.findTypeRoot(typeName); |
| if (classFile != null) { |
| return classFile; |
| } |
| } catch (CoreException e) { |
| // ignore |
| } |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Return a string that represents the element type of the given delta. |
| * Returns {@link #UNKNOWN_ELEMENT_KIND} if the element type cannot be |
| * determined. |
| * |
| * @param delta the given delta |
| * @return a string that represents the element type of the given delta. |
| */ |
| public static String getDeltaElementType(IDelta delta) { |
| return getDeltaElementType(delta.getElementType()); |
| } |
| |
| /** |
| * Returns a text representation of a marker severity level |
| * |
| * @param severity |
| * @return text of a marker severity level |
| */ |
| public static String getSeverity(int severity) { |
| switch (severity) { |
| case IMarker.SEVERITY_ERROR: { |
| return "ERROR"; //$NON-NLS-1$ |
| } |
| case IMarker.SEVERITY_INFO: { |
| return "INFO"; //$NON-NLS-1$ |
| } |
| case IMarker.SEVERITY_WARNING: { |
| return "WARNING"; //$NON-NLS-1$ |
| } |
| default: { |
| return "UNKNOWN_SEVERITY"; //$NON-NLS-1$ |
| } |
| } |
| } |
| |
| /** |
| * Return an int value that represents the given element type Returns -1 if |
| * the element type cannot be determined. |
| * |
| * @param elementType the given element type |
| * @return an int that represents the given element type constant. |
| */ |
| public static int getDeltaElementTypeValue(String elementType) { |
| Class<IDelta> IDeltaClass = IDelta.class; |
| try { |
| Field field = IDeltaClass.getField(elementType); |
| return field.getInt(null); |
| } catch (SecurityException e) { |
| // ignore |
| } catch (IllegalArgumentException e) { |
| // ignore |
| } catch (NoSuchFieldException e) { |
| // ignore |
| } catch (IllegalAccessException e) { |
| // ignore |
| } |
| return -1; |
| } |
| |
| /** |
| * Return a string that represents the given element type Returns |
| * {@link #UNKNOWN_ELEMENT_KIND} if the element type cannot be determined. |
| * |
| * @param elementType the given element type |
| * @return a string that represents the given element type. |
| */ |
| public static String getDeltaElementType(int elementType) { |
| switch (elementType) { |
| case IDelta.ANNOTATION_ELEMENT_TYPE: |
| return "ANNOTATION_ELEMENT_TYPE"; //$NON-NLS-1$ |
| case IDelta.INTERFACE_ELEMENT_TYPE: |
| return "INTERFACE_ELEMENT_TYPE"; //$NON-NLS-1$ |
| case IDelta.ENUM_ELEMENT_TYPE: |
| return "ENUM_ELEMENT_TYPE"; //$NON-NLS-1$ |
| case IDelta.API_COMPONENT_ELEMENT_TYPE: |
| return "API_COMPONENT_ELEMENT_TYPE"; //$NON-NLS-1$ |
| case IDelta.API_BASELINE_ELEMENT_TYPE: |
| return "API_BASELINE_ELEMENT_TYPE"; //$NON-NLS-1$ |
| case IDelta.CONSTRUCTOR_ELEMENT_TYPE: |
| return "CONSTRUCTOR_ELEMENT_TYPE"; //$NON-NLS-1$ |
| case IDelta.METHOD_ELEMENT_TYPE: |
| return "METHOD_ELEMENT_TYPE"; //$NON-NLS-1$ |
| case IDelta.FIELD_ELEMENT_TYPE: |
| return "FIELD_ELEMENT_TYPE"; //$NON-NLS-1$ |
| case IDelta.CLASS_ELEMENT_TYPE: |
| return "CLASS_ELEMENT_TYPE"; //$NON-NLS-1$ |
| case IDelta.TYPE_PARAMETER_ELEMENT_TYPE: |
| return "TYPE_PARAMETER_ELEMENT_TYPE"; //$NON-NLS-1$ |
| default: |
| break; |
| } |
| return UNKNOWN_ELEMENT_KIND; |
| } |
| |
| /** |
| * Return a string that represents the given flags Returns |
| * {@link #UNKNOWN_FLAGS} if the flags cannot be determined. |
| * |
| * @param flags the given delta's flags |
| * @return a string that represents the given flags. |
| */ |
| public static String getDeltaFlagsName(int flags) { |
| switch (flags) { |
| case IDelta.ABSTRACT_TO_NON_ABSTRACT: |
| return "ABSTRACT_TO_NON_ABSTRACT"; //$NON-NLS-1$ |
| case IDelta.ANNOTATION_DEFAULT_VALUE: |
| return "ANNOTATION_DEFAULT_VALUE"; //$NON-NLS-1$ |
| case IDelta.API_COMPONENT: |
| return "API_COMPONENT"; //$NON-NLS-1$ |
| case IDelta.ARRAY_TO_VARARGS: |
| return "ARRAY_TO_VARARGS"; //$NON-NLS-1$ |
| case IDelta.CHECKED_EXCEPTION: |
| return "CHECKED_EXCEPTION"; //$NON-NLS-1$ |
| case IDelta.CLASS_BOUND: |
| return "CLASS_BOUND"; //$NON-NLS-1$ |
| case IDelta.CLINIT: |
| return "CLINIT"; //$NON-NLS-1$ |
| case IDelta.CONSTRUCTOR: |
| return "CONSTRUCTOR"; //$NON-NLS-1$ |
| case IDelta.CONTRACTED_SUPERINTERFACES_SET: |
| return "CONTRACTED_SUPERINTERFACES_SET"; //$NON-NLS-1$ |
| case IDelta.DECREASE_ACCESS: |
| return "DECREASE_ACCESS"; //$NON-NLS-1$ |
| case IDelta.ENUM_CONSTANT: |
| return "ENUM_CONSTANT"; //$NON-NLS-1$ |
| case IDelta.EXECUTION_ENVIRONMENT: |
| return "EXECUTION_ENVIRONMENT"; //$NON-NLS-1$ |
| case IDelta.EXPANDED_SUPERINTERFACES_SET: |
| return "EXPANDED_SUPERINTERFACES_SET"; //$NON-NLS-1$ |
| case IDelta.FIELD: |
| return "FIELD"; //$NON-NLS-1$ |
| case IDelta.FIELD_MOVED_UP: |
| return "FIELD_MOVED_UP"; //$NON-NLS-1$ |
| case IDelta.FINAL_TO_NON_FINAL: |
| return "FINAL_TO_NON_FINAL"; //$NON-NLS-1$ |
| case IDelta.FINAL_TO_NON_FINAL_NON_STATIC: |
| return "FINAL_TO_NON_FINAL_NON_STATIC"; //$NON-NLS-1$ |
| case IDelta.FINAL_TO_NON_FINAL_STATIC_CONSTANT: |
| return "FINAL_TO_NON_FINAL_STATIC_CONSTANT"; //$NON-NLS-1$ |
| case IDelta.FINAL_TO_NON_FINAL_STATIC_NON_CONSTANT: |
| return "FINAL_TO_NON_FINAL_STATIC_NON_CONSTANT"; //$NON-NLS-1$ |
| case IDelta.INCREASE_ACCESS: |
| return "INCREASE_ACCESS"; //$NON-NLS-1$ |
| case IDelta.INTERFACE_BOUND: |
| return "INTERFACE_BOUND"; //$NON-NLS-1$ |
| case IDelta.METHOD: |
| return "METHOD"; //$NON-NLS-1$ |
| case IDelta.DEFAULT_METHOD: |
| return "DEFAULT_METHOD"; //$NON-NLS-1$ |
| case IDelta.METHOD_MOVED_UP: |
| return "METHOD_MOVED_UP"; //$NON-NLS-1$ |
| case IDelta.METHOD_WITH_DEFAULT_VALUE: |
| return "METHOD_WITH_DEFAULT_VALUE"; //$NON-NLS-1$ |
| case IDelta.METHOD_WITHOUT_DEFAULT_VALUE: |
| return "METHOD_WITHOUT_DEFAULT_VALUE"; //$NON-NLS-1$ |
| case IDelta.NATIVE_TO_NON_NATIVE: |
| return "NATIVE_TO_NON_NATIVE"; //$NON-NLS-1$ |
| case IDelta.NON_ABSTRACT_TO_ABSTRACT: |
| return "NON_ABSTRACT_TO_ABSTRACT"; //$NON-NLS-1$ |
| case IDelta.NON_FINAL_TO_FINAL: |
| return "NON_FINAL_TO_FINAL"; //$NON-NLS-1$ |
| case IDelta.NON_NATIVE_TO_NATIVE: |
| return "NON_NATIVE_TO_NATIVE"; //$NON-NLS-1$ |
| case IDelta.NON_STATIC_TO_STATIC: |
| return "NON_STATIC_TO_STATIC"; //$NON-NLS-1$ |
| case IDelta.NON_SYNCHRONIZED_TO_SYNCHRONIZED: |
| return "NON_SYNCHRONIZED_TO_SYNCHRONIZED"; //$NON-NLS-1$ |
| case IDelta.NON_TRANSIENT_TO_TRANSIENT: |
| return "NON_TRANSIENT_TO_TRANSIENT"; //$NON-NLS-1$ |
| case IDelta.OVERRIDEN_METHOD: |
| return "OVERRIDEN_METHOD"; //$NON-NLS-1$ |
| case IDelta.STATIC_TO_NON_STATIC: |
| return "STATIC_TO_NON_STATIC"; //$NON-NLS-1$ |
| case IDelta.SUPERCLASS: |
| return "SUPERCLASS"; //$NON-NLS-1$ |
| case IDelta.SYNCHRONIZED_TO_NON_SYNCHRONIZED: |
| return "SYNCHRONIZED_TO_NON_SYNCHRONIZED"; //$NON-NLS-1$ |
| case IDelta.TYPE_CONVERSION: |
| return "TYPE_CONVERSION"; //$NON-NLS-1$ |
| case IDelta.TRANSIENT_TO_NON_TRANSIENT: |
| return "TRANSIENT_TO_NON_TRANSIENT"; //$NON-NLS-1$ |
| case IDelta.TYPE: |
| return "TYPE"; //$NON-NLS-1$ |
| case IDelta.TYPE_ARGUMENTS: |
| return "TYPE_ARGUMENTS"; //$NON-NLS-1$ |
| case IDelta.TYPE_MEMBER: |
| return "TYPE_MEMBER"; //$NON-NLS-1$ |
| case IDelta.TYPE_PARAMETER: |
| return "TYPE_PARAMETER"; //$NON-NLS-1$ |
| case IDelta.TYPE_PARAMETER_NAME: |
| return "TYPE_PARAMETER_NAME"; //$NON-NLS-1$ |
| case IDelta.TYPE_PARAMETERS: |
| return "TYPE_PARAMETERS"; //$NON-NLS-1$ |
| case IDelta.TYPE_VISIBILITY: |
| return "TYPE_VISIBILITY"; //$NON-NLS-1$ |
| case IDelta.UNCHECKED_EXCEPTION: |
| return "UNCHECKED_EXCEPTION"; //$NON-NLS-1$ |
| case IDelta.VALUE: |
| return "VALUE"; //$NON-NLS-1$ |
| case IDelta.VARARGS_TO_ARRAY: |
| return "VARARGS_TO_ARRAY"; //$NON-NLS-1$ |
| case IDelta.RESTRICTIONS: |
| return "RESTRICTIONS"; //$NON-NLS-1$ |
| case IDelta.API_TYPE: |
| return "API_TYPE"; //$NON-NLS-1$ |
| case IDelta.NON_VOLATILE_TO_VOLATILE: |
| return "NON_VOLATILE_TO_VOLATILE"; //$NON-NLS-1$ |
| case IDelta.VOLATILE_TO_NON_VOLATILE: |
| return "VOLATILE_TO_NON_VOLATILE"; //$NON-NLS-1$ |
| case IDelta.MINOR_VERSION: |
| return "MINOR_VERSION"; //$NON-NLS-1$ |
| case IDelta.MAJOR_VERSION: |
| return "MAJOR_VERSION"; //$NON-NLS-1$ |
| case IDelta.API_FIELD: |
| return "API_FIELD"; //$NON-NLS-1$ |
| case IDelta.API_METHOD: |
| return "API_METHOD"; //$NON-NLS-1$ |
| case IDelta.API_CONSTRUCTOR: |
| return "API_CONSTRUCTOR"; //$NON-NLS-1$ |
| case IDelta.API_ENUM_CONSTANT: |
| return "API_ENUM_CONSTANT"; //$NON-NLS-1$ |
| case IDelta.API_METHOD_WITH_DEFAULT_VALUE: |
| return "API_METHOD_WITH_DEFAULT_VALUE"; //$NON-NLS-1$ |
| case IDelta.API_METHOD_WITHOUT_DEFAULT_VALUE: |
| return "API_METHOD_WITHOUT_DEFAULT_VALUE"; //$NON-NLS-1$ |
| case IDelta.TYPE_ARGUMENT: |
| return "TYPE_ARGUMENT"; //$NON-NLS-1$ |
| case IDelta.SUPER_INTERFACE_WITH_METHODS: |
| return "SUPER_INTERFACE_WITH_METHODS"; //$NON-NLS-1$ |
| case IDelta.REEXPORTED_API_TYPE: |
| return "REEXPORTED_API_TYPE"; //$NON-NLS-1$ |
| case IDelta.REEXPORTED_TYPE: |
| return "REEXPORTED_TYPE"; //$NON-NLS-1$ |
| case IDelta.METHOD_MOVED_DOWN: |
| return "METHOD_MOVED_DOWN"; //$NON-NLS-1$ |
| case IDelta.DEPRECATION: |
| return "DEPRECATION"; //$NON-NLS-1$ |
| default: |
| break; |
| } |
| return UNKNOWN_FLAGS; |
| } |
| |
| /** |
| * Return a string that represents the kind of the given delta. Returns |
| * {@link #UNKNOWN_KIND} if the kind cannot be determined. |
| * |
| * @param delta the given delta |
| * @return a string that represents the kind of the given delta. |
| */ |
| public static String getDeltaKindName(IDelta delta) { |
| return getDeltaKindName(delta.getKind()); |
| } |
| |
| /** |
| * Return a string that represents the given kind. Returns |
| * {@link #UNKNOWN_KIND} if the kind cannot be determined. |
| * |
| * @param delta the given kind |
| * @return a string that represents the given kind. |
| */ |
| public static String getDeltaKindName(int kind) { |
| switch (kind) { |
| case IDelta.ADDED: |
| return "ADDED"; //$NON-NLS-1$ |
| case IDelta.CHANGED: |
| return "CHANGED"; //$NON-NLS-1$ |
| case IDelta.REMOVED: |
| return "REMOVED"; //$NON-NLS-1$ |
| default: |
| break; |
| } |
| return UNKNOWN_KIND; |
| } |
| |
| /** |
| * Returns the preference key for the given element type, the given kind and |
| * the given flags. |
| * |
| * @param elementType the given element type (retrieved using |
| * {@link IDelta#getElementType()} |
| * @param kind the given kind (retrieved using {@link IDelta#getKind()} |
| * @param flags the given flags (retrieved using {@link IDelta#getFlags()} |
| * @return the preference key for the given element type, the given kind and |
| * the given flags. |
| */ |
| public static String getDeltaPrefererenceKey(int elementType, int kind, int flags) { |
| StringBuffer buffer = new StringBuffer(Util.getDeltaElementType(elementType)); |
| buffer.append('_').append(Util.getDeltaKindName(kind)); |
| if (flags != -1) { |
| buffer.append('_'); |
| switch (flags) { |
| case IDelta.API_FIELD: |
| buffer.append(Util.getDeltaFlagsName(IDelta.FIELD)); |
| break; |
| case IDelta.API_ENUM_CONSTANT: |
| buffer.append(Util.getDeltaFlagsName(IDelta.ENUM_CONSTANT)); |
| break; |
| case IDelta.API_CONSTRUCTOR: |
| buffer.append(Util.getDeltaFlagsName(IDelta.CONSTRUCTOR)); |
| break; |
| case IDelta.API_METHOD: |
| buffer.append(Util.getDeltaFlagsName(IDelta.METHOD)); |
| break; |
| case IDelta.API_METHOD_WITH_DEFAULT_VALUE: |
| if (kind == IDelta.REMOVED) { |
| buffer.append(Util.getDeltaFlagsName(IDelta.METHOD)); |
| } else { |
| buffer.append(Util.getDeltaFlagsName(IDelta.METHOD_WITH_DEFAULT_VALUE)); |
| } |
| break; |
| case IDelta.API_METHOD_WITHOUT_DEFAULT_VALUE: |
| if (kind == IDelta.REMOVED) { |
| buffer.append(Util.getDeltaFlagsName(IDelta.METHOD)); |
| } else { |
| buffer.append(Util.getDeltaFlagsName(IDelta.METHOD_WITHOUT_DEFAULT_VALUE)); |
| } |
| break; |
| case IDelta.METHOD_WITH_DEFAULT_VALUE: |
| if (kind == IDelta.REMOVED) { |
| buffer.append(Util.getDeltaFlagsName(IDelta.METHOD)); |
| } else { |
| buffer.append(Util.getDeltaFlagsName(IDelta.METHOD_WITH_DEFAULT_VALUE)); |
| } |
| break; |
| case IDelta.METHOD_WITHOUT_DEFAULT_VALUE: |
| if (kind == IDelta.REMOVED) { |
| buffer.append(Util.getDeltaFlagsName(IDelta.METHOD)); |
| } else { |
| buffer.append(Util.getDeltaFlagsName(IDelta.METHOD_WITHOUT_DEFAULT_VALUE)); |
| } |
| break; |
| default: |
| buffer.append(Util.getDeltaFlagsName(flags)); |
| } |
| } |
| return String.valueOf(buffer); |
| } |
| |
| /** |
| * Returns the details of the API delta as a string |
| * |
| * @param delta |
| * @return the details of the delta as a string |
| */ |
| public static String getDetail(IDelta delta) { |
| StringBuffer buffer = new StringBuffer(); |
| switch (delta.getElementType()) { |
| case IDelta.CLASS_ELEMENT_TYPE: |
| buffer.append("class"); //$NON-NLS-1$ |
| break; |
| case IDelta.ANNOTATION_ELEMENT_TYPE: |
| buffer.append("annotation"); //$NON-NLS-1$ |
| break; |
| case IDelta.INTERFACE_ELEMENT_TYPE: |
| buffer.append("interface"); //$NON-NLS-1$ |
| break; |
| case IDelta.API_COMPONENT_ELEMENT_TYPE: |
| buffer.append("api component"); //$NON-NLS-1$ |
| break; |
| case IDelta.API_BASELINE_ELEMENT_TYPE: |
| buffer.append("api baseline"); //$NON-NLS-1$ |
| break; |
| case IDelta.METHOD_ELEMENT_TYPE: |
| buffer.append("method"); //$NON-NLS-1$ |
| break; |
| case IDelta.CONSTRUCTOR_ELEMENT_TYPE: |
| buffer.append("constructor"); //$NON-NLS-1$ |
| break; |
| case IDelta.ENUM_ELEMENT_TYPE: |
| buffer.append("enum"); //$NON-NLS-1$ |
| break; |
| case IDelta.FIELD_ELEMENT_TYPE: |
| buffer.append("field"); //$NON-NLS-1$ |
| break; |
| default: |
| break; |
| } |
| buffer.append(' '); |
| switch (delta.getKind()) { |
| case IDelta.ADDED: |
| buffer.append("added"); //$NON-NLS-1$ |
| break; |
| case IDelta.REMOVED: |
| buffer.append("removed"); //$NON-NLS-1$ |
| break; |
| case IDelta.CHANGED: |
| buffer.append("changed"); //$NON-NLS-1$ |
| break; |
| default: |
| buffer.append("unknown kind"); //$NON-NLS-1$ |
| break; |
| } |
| buffer.append(' ').append(getDeltaFlagsName(delta.getFlags())).append(' ').append(delta.getTypeName()).append("#").append(delta.getKey()); //$NON-NLS-1$ |
| return String.valueOf(buffer); |
| } |
| |
| /** |
| * Returns the {@link IDocument} for the specified {@link ICompilationUnit} |
| * |
| * @param cu |
| * @return the {@link IDocument} for the specified {@link ICompilationUnit} |
| * @throws CoreException |
| */ |
| public static IDocument getDocument(ICompilationUnit cu) throws CoreException { |
| if (cu.getOwner() == null) { |
| IFile file = (IFile) cu.getResource(); |
| if (file.exists()) { |
| ITextFileBufferManager bufferManager = FileBuffers.getTextFileBufferManager(); |
| IPath path = cu.getPath(); |
| bufferManager.connect(path, LocationKind.IFILE, new NullProgressMonitor()); |
| try { |
| return bufferManager.getTextFileBuffer(path, LocationKind.IFILE).getDocument(); |
| } finally { |
| bufferManager.disconnect(path, LocationKind.IFILE, null); |
| } |
| } |
| } |
| return new org.eclipse.jface.text.Document(cu.getSource()); |
| } |
| |
| /** |
| * Returns the OSGi profile properties corresponding to the given execution |
| * environment id, or <code>null</code> if none. |
| * |
| * @param eeId OSGi profile identifier |
| * |
| * @return the corresponding properties or <code>null</code> if none |
| */ |
| public static Properties getEEProfile(String eeId) { |
| String profileName = eeId + ".profile"; //$NON-NLS-1$ |
| InputStream stream = Util.class.getResourceAsStream("profiles/" + profileName); //$NON-NLS-1$ |
| if (stream != null) { |
| try { |
| Properties profile = new Properties(); |
| profile.load(stream); |
| return profile; |
| } catch (IOException e) { |
| ApiPlugin.log(e); |
| } finally { |
| try { |
| stream.close(); |
| } catch (IOException e) { |
| ApiPlugin.log(e); |
| } |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Returns the number of fragments for the given version value, -1 if the |
| * format is unknown. The version is formed like: [optional plug-in name] |
| * major.minor.micro.qualifier. |
| * |
| * @param version the given version value |
| * @return the number of fragments for the given version value or -1 if the |
| * format is unknown |
| * @throws IllegalArgumentException if version is null |
| */ |
| public static final int getFragmentNumber(String version) { |
| if (version == null) { |
| throw new IllegalArgumentException("The given version should not be null"); //$NON-NLS-1$ |
| } |
| int index = version.indexOf(' '); |
| char[] charArray = version.toCharArray(); |
| int length = charArray.length; |
| if (index + 1 >= length) { |
| return -1; |
| } |
| int counter = 1; |
| for (int i = index + 1; i < length; i++) { |
| switch (charArray[i]) { |
| case '0': |
| case '1': |
| case '2': |
| case '3': |
| case '4': |
| case '5': |
| case '6': |
| case '7': |
| case '8': |
| case '9': |
| continue; |
| case '.': |
| counter++; |
| break; |
| default: |
| return -1; |
| } |
| } |
| return counter; |
| } |
| |
| public static IMember getIMember(IDelta delta, IJavaProject javaProject) { |
| String typeName = delta.getTypeName(); |
| if (typeName == null) { |
| return null; |
| } |
| IType type = null; |
| try { |
| type = javaProject.findType(typeName.replace('$', '.')); |
| } catch (JavaModelException e) { |
| // ignore |
| } |
| if (type == null) { |
| return null; |
| } |
| String key = delta.getKey(); |
| switch (delta.getElementType()) { |
| case IDelta.FIELD_ELEMENT_TYPE: { |
| IField field = type.getField(key); |
| if (field.exists()) { |
| return field; |
| } |
| } |
| break; |
| case IDelta.CLASS_ELEMENT_TYPE: |
| case IDelta.ANNOTATION_ELEMENT_TYPE: |
| case IDelta.INTERFACE_ELEMENT_TYPE: |
| case IDelta.ENUM_ELEMENT_TYPE: |
| // we report the marker on the type |
| switch (delta.getKind()) { |
| case IDelta.ADDED: |
| switch (delta.getFlags()) { |
| case IDelta.FIELD: |
| case IDelta.ENUM_CONSTANT: |
| IField field = type.getField(key); |
| if (field.exists()) { |
| return field; |
| } |
| break; |
| case IDelta.METHOD_WITH_DEFAULT_VALUE: |
| case IDelta.METHOD_WITHOUT_DEFAULT_VALUE: |
| case IDelta.METHOD: |
| case IDelta.CONSTRUCTOR: |
| return getMethod(type, key); |
| case IDelta.TYPE_MEMBER: |
| IType type2 = type.getType(key); |
| if (type2.exists()) { |
| return type2; |
| } |
| break; |
| default: |
| break; |
| } |
| break; |
| case IDelta.REMOVED: |
| switch (delta.getFlags()) { |
| case IDelta.API_FIELD: |
| case IDelta.API_ENUM_CONSTANT: |
| IField field = type.getField(key); |
| if (field.exists()) { |
| return field; |
| } |
| break; |
| case IDelta.API_METHOD_WITH_DEFAULT_VALUE: |
| case IDelta.API_METHOD_WITHOUT_DEFAULT_VALUE: |
| case IDelta.API_METHOD: |
| case IDelta.API_CONSTRUCTOR: |
| return getMethod(type, key); |
| default: |
| break; |
| } |
| break; |
| default: |
| break; |
| } |
| return type; |
| case IDelta.METHOD_ELEMENT_TYPE: |
| case IDelta.CONSTRUCTOR_ELEMENT_TYPE: { |
| return getMethod(type, key); |
| } |
| case IDelta.API_COMPONENT_ELEMENT_TYPE: |
| return type; |
| default: |
| break; |
| } |
| return null; |
| } |
| |
| private static IMember getMethod(IType type, String key) { |
| boolean isGeneric = false; |
| int indexOfTypeVariable = key.indexOf('<'); |
| int index = 0; |
| if (indexOfTypeVariable == -1) { |
| int indexOfParen = key.indexOf('('); |
| if (indexOfParen == -1) { |
| return null; |
| } |
| index = indexOfParen; |
| } else { |
| int indexOfParen = key.indexOf('('); |
| if (indexOfParen == -1) { |
| return null; |
| } |
| if (indexOfParen < indexOfTypeVariable) { |
| index = indexOfParen; |
| } else { |
| index = indexOfTypeVariable; |
| isGeneric = true; |
| } |
| } |
| String selector = key.substring(0, index); |
| String descriptor = key.substring(index, key.length()); |
| IMethod method = null; |
| String signature = descriptor.replace('/', '.'); |
| String[] parameterTypes = null; |
| if (isGeneric) { |
| // remove all type variables first |
| signature = signature.substring(signature.indexOf('(')); |
| parameterTypes = Signature.getParameterTypes(signature); |
| } else { |
| parameterTypes = Signature.getParameterTypes(signature); |
| } |
| |
| try { |
| method = type.getMethod(selector, parameterTypes); |
| } catch (IllegalArgumentException e) { |
| ApiPlugin.log(e); |
| } |
| if (method == null) { |
| return null; |
| } |
| if (method.exists()) { |
| return method; |
| } |
| // if the method is not null and it doesn't exist, it might be the |
| // default constructor or a constructor in inner type |
| if (selector.equals(type.getElementName())) { |
| if (parameterTypes.length == 0) { |
| return null; |
| } |
| // Perhaps a constructor on an inner type? |
| IJavaElement parent = type.getParent(); |
| if (parent instanceof IType) { |
| String parentTypeSig = Signature.createTypeSignature(((IType) parent).getFullyQualifiedName(), true); |
| if (Signatures.matches(parentTypeSig, parameterTypes[0])) { |
| IMethod constructor = type.getMethod(selector, Arrays.copyOfRange(parameterTypes, 1, parameterTypes.length)); |
| try { |
| if (constructor.exists() && constructor.isConstructor()) { |
| return constructor; |
| } |
| String contructorSig = Signature.createMethodSignature(Arrays.copyOfRange(parameterTypes, 1, parameterTypes.length), Signature.getReturnType(signature)); |
| IMethod[] methods = type.findMethods(constructor); |
| if (methods != null) { |
| if (methods.length == 1 && methods[0].isConstructor()) { |
| return methods[0]; |
| } |
| // findMethods() checks simple type names, so |
| // it's possible to have multiple matches with |
| // different package names |
| for (IMethod m : methods) { |
| try { |
| if (m.isConstructor() && m.getNumberOfParameters() == parameterTypes.length - 1 && Signatures.matchesSignatures(generateBinarySignature(m), contructorSig)) { |
| return m; |
| } |
| } catch (JavaModelException e) { |
| // ignore |
| } |
| } |
| } |
| } catch (JavaModelException e) { |
| // ignore |
| } |
| } |
| } |
| } |
| // Let JDT have a go |
| IMethod[] methods = type.findMethods(method); |
| if (methods != null && methods.length == 1) { |
| /* exact match found */ |
| return methods[0]; |
| } |
| if (methods == null || methods.length == 0) { |
| /* no methods found: may be due to type erasure */ |
| try { |
| methods = type.getMethods(); |
| } catch (JavaModelException e) { |
| ApiPlugin.log(new Status(IStatus.ERROR, ApiPlugin.PLUGIN_ID, NLS.bind("Unable to retrieve methods for {0}", type.getFullyQualifiedName()), e)); //$NON-NLS-1$ |
| return null; |
| } |
| } |
| |
| /* |
| * findMethods() checks simple type names, so it's possible to have |
| * multiple matches with different package names. Or we may need to |
| * check with type erasure. |
| */ |
| for (IMethod m : methods) { |
| try { |
| if (!m.getElementName().equals(selector) || m.getNumberOfParameters() != parameterTypes.length) { |
| continue; |
| } |
| if (Signatures.matchesSignatures(generateBinarySignature(m), signature)) { |
| return m; |
| } |
| } catch (JavaModelException e) { |
| // ignore |
| } |
| } |
| |
| /* |
| * Unclear what circumstances that this could happen, so provide more |
| * information to help understand why |
| */ |
| StringBuilder sb = new StringBuilder(); |
| for (IMethod m : methods) { |
| sb.append('\n').append(m.getHandleIdentifier()); |
| } |
| ApiPlugin.log(new Status(IStatus.ERROR, ApiPlugin.PLUGIN_ID, NLS.bind(UtilMessages.Util_6, new String[] { |
| selector, descriptor }) + sb.toString())); |
| // do not default to the enclosing type - see bug 224713 |
| return null; |
| } |
| |
| /** |
| * Generate the binary signature for the provided method. This is the |
| * type-erased signature written out to the .class file. |
| * |
| * @param method the method |
| * @return the method signature as would be encoded in a .class file |
| * @throws JavaModelException |
| */ |
| private static String generateBinarySignature(IMethod method) throws JavaModelException { |
| ITypeParameter[] typeTPs = method.getDeclaringType().getTypeParameters(); |
| ITypeParameter[] methodTPs = method.getTypeParameters(); |
| if (typeTPs.length == 0 && methodTPs.length == 0) { |
| return method.getSignature(); |
| } |
| Map<String, String> lookup = new HashMap<>(); |
| Stream.concat(Stream.of(typeTPs), Stream.of(methodTPs)).forEach(tp -> { |
| try { |
| String sigs[] = tp.getBoundsSignatures(); |
| lookup.put(tp.getElementName(), sigs.length == 1 ? sigs[0] : "Ljava.lang.Object;"); //$NON-NLS-1$ |
| } catch (JavaModelException e) { |
| /* ignore */ |
| } |
| }); |
| String[] parameterTypes = Stream.of(method.getParameterTypes()).map(p -> expandParameterType(p, lookup)).toArray(String[]::new); |
| return Signature.createMethodSignature(parameterTypes, expandParameterType(method.getReturnType(), lookup)); |
| } |
| |
| /** |
| * Rewrite a parameter type signature with type erasure and using the |
| * parameterized type bounds lookup table. For example: |
| * |
| * <pre> |
| * expand("QList<QE;>;", {"E" → "Ljava.lang.Object;"}) = "QList;" |
| * expand("QE;", {"E" → "Ljava.lang.Object;"}) = "Ljava.lang.Object;" |
| * </pre> |
| * |
| * @param parameterTypeSig the type signature for a parameter |
| * @param bounds the type bounds as expressed on the method and class |
| * @return a rewritten parameter type signature as would be found in the .class file |
| */ |
| private static String expandParameterType(String parameterTypeSig, Map<String, String> bounds) { |
| String erased = Signature.getTypeErasure(parameterTypeSig); |
| if (erased.charAt(0) == Signature.C_UNRESOLVED || erased.charAt(0) == Signature.C_TYPE_VARIABLE) { |
| String repl = bounds.get(Signature.getSignatureSimpleName(erased)); |
| if (repl != null) { |
| return repl; |
| } |
| } |
| return erased; |
| } |
| |
| /** |
| * Returns the given input stream as a byte array |
| * |
| * @param stream the stream to get as a byte array |
| * @param length the length to read from the stream or -1 for unknown |
| * @return the given input stream as a byte array |
| * @throws IOException |
| */ |
| public static byte[] getInputStreamAsByteArray(InputStream stream, int length) throws IOException { |
| byte[] contents; |
| if (length == -1) { |
| contents = new byte[0]; |
| int contentsLength = 0; |
| int amountRead = -1; |
| do { |
| // read at least 8K |
| int amountRequested = Math.max(stream.available(), DEFAULT_READING_SIZE); |
| // 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 length is the |
| // actual |
| // read size. |
| len += readSize; |
| readSize = stream.read(contents, len, length - len); |
| } |
| } |
| return contents; |
| } |
| |
| /** |
| * Returns the given input stream's contents as a character array. If a |
| * length is specified (i.e. if length != -1), this represents the number of |
| * bytes in the stream. Note the specified stream is not closed in this |
| * method |
| * |
| * @param stream the stream to get convert to the char array |
| * @param length the length of the input stream, or -1 if unknown |
| * @param encoding the encoding to use when reading the stream |
| * @return the given input stream's contents as a character array. |
| * @throws IOException if a problem occurred reading the stream. |
| */ |
| public static char[] getInputStreamAsCharArray(InputStream stream, int length, String encoding) throws IOException { |
| Charset charset = null; |
| try { |
| charset = Charset.forName(encoding); |
| } catch (IllegalCharsetNameException e) { |
| System.err.println("Illegal charset name : " + encoding); //$NON-NLS-1$ |
| return null; |
| } catch (UnsupportedCharsetException e) { |
| System.err.println("Unsupported charset : " + encoding); //$NON-NLS-1$ |
| return null; |
| } |
| CharsetDecoder charsetDecoder = charset.newDecoder(); |
| |
| charsetDecoder.onMalformedInput(CodingErrorAction.REPLACE).onUnmappableCharacter(CodingErrorAction.REPLACE); |
| byte[] contents = getInputStreamAsByteArray(stream, length); |
| ByteBuffer byteBuffer = ByteBuffer.allocate(contents.length); |
| byteBuffer.put(contents); |
| byteBuffer.flip(); |
| CharBuffer charBuffer = charsetDecoder.decode(byteBuffer); |
| charBuffer.compact(); // ensure pay-load starting at 0 |
| char[] array = charBuffer.array(); |
| int lengthToBe = charBuffer.position(); |
| if (array.length > lengthToBe) { |
| System.arraycopy(array, 0, (array = new char[lengthToBe]), 0, lengthToBe); |
| } |
| return array; |
| } |
| |
| /** |
| * Tries to find the 'MANIFEST.MF' file with in the given project in the |
| * 'META-INF folder'. |
| * |
| * @param currentProject |
| * @return a handle to the manifest file or <code>null</code> if not found |
| */ |
| public static IResource getManifestFile(IProject currentProject) { |
| return currentProject.findMember("META-INF/MANIFEST.MF"); //$NON-NLS-1$ |
| } |
| |
| /** |
| * Returns if the given {@link IMarker} is representing an |
| * {@link org.eclipse.pde.api.tools.internal.provisional.problems.IApiProblem} |
| * or not |
| * |
| * @param marker the marker to check |
| * @return true if the marker is for an |
| * {@link org.eclipse.pde.api.tools.internal.provisional.problems.IApiProblem} |
| * false otherwise |
| * @throws CoreException |
| */ |
| public static boolean isApiProblemMarker(IMarker marker) { |
| return marker.getAttribute(IApiMarkerConstants.API_MARKER_ATTR_ID, -1) > 0; |
| } |
| |
| /** |
| * Returns a reference type for the given fully qualified type name. |
| * |
| * @param fullyQualifiedName type name |
| * @return reference type |
| */ |
| public static IReferenceTypeDescriptor getType(String fullyQualifiedName) { |
| int index = fullyQualifiedName.lastIndexOf('.'); |
| String pkg = index == -1 ? DEFAULT_PACKAGE_NAME : fullyQualifiedName.substring(0, index); |
| String type = index == -1 ? fullyQualifiedName : fullyQualifiedName.substring(index + 1); |
| return Factory.packageDescriptor(pkg).getType(type); |
| } |
| |
| /** |
| * Returns if the given project is API enabled |
| * |
| * @param project the given project |
| * @return true if the project is API enabled, false otherwise |
| */ |
| public static boolean isApiProject(IProject project) { |
| try { |
| return project.hasNature(ApiPlugin.NATURE_ID); |
| } catch (CoreException e) { |
| return false; |
| } |
| } |
| |
| /** |
| * Returns if the given project is a java project |
| * |
| * @param project the given project |
| * @return <code>true</code> if the project is a java project, |
| * <code>false</code> otherwise |
| */ |
| public static boolean isJavaProject(IProject project) { |
| try { |
| return project.hasNature(JavaCore.NATURE_ID); |
| } catch (CoreException e) { |
| return false; |
| } |
| } |
| |
| /** |
| * Returns if the given project is API enabled |
| * |
| * @param project the given project |
| * @return <code>true</code> if the project is API enabled, |
| * <code>false</code> otherwise |
| */ |
| public static boolean isApiProject(IJavaProject project) { |
| if (project != null) { |
| return isApiProject(project.getProject()); |
| } |
| return false; |
| } |
| |
| /** |
| * Returns if the given {@link IApiComponent} is a valid |
| * {@link IApiComponent} |
| * |
| * @param apiComponent the given component |
| * @return true if the given {@link IApiComponent} is valid, false otherwise |
| */ |
| public static boolean isApiToolsComponent(IApiComponent apiComponent) { |
| File file = new File(apiComponent.getLocation()); |
| if (file.exists()) { |
| if (file.isDirectory()) { |
| // directory binary bundle |
| File apiDescription = new File(file, IApiCoreConstants.API_DESCRIPTION_XML_NAME); |
| return apiDescription.exists(); |
| } |
| ZipFile zipFile = null; |
| try { |
| zipFile = new ZipFile(file); |
| return zipFile.getEntry(IApiCoreConstants.API_DESCRIPTION_XML_NAME) != null; |
| } catch (ZipException e) { |
| // ignore |
| } catch (IOException e) { |
| // ignore |
| } finally { |
| try { |
| if (zipFile != null) { |
| zipFile.close(); |
| } |
| } catch (IOException e) { |
| // ignore |
| } |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Returns if the specified file name is an archive name. A name is |
| * considered to be an archive name if it ends with either '.zip' or '.jar' |
| * |
| * @param fileName |
| * @return true if the file name is an archive name false otherwise |
| */ |
| public static boolean isArchive(String fileName) { |
| return isZipJarFile(fileName) || isTGZFile(fileName); |
| } |
| |
| /** |
| * Returns if the given file name represents a 'standard' archive, where the |
| * name has an extension of *.zip or *.jar |
| * |
| * @param fileName |
| * @return true if the given file name is that of a 'standard' archive, |
| * false otherwise |
| */ |
| public static boolean isZipJarFile(String fileName) { |
| String normalizedFileName = fileName.toLowerCase(); |
| return normalizedFileName.endsWith(DOT_ZIP) || normalizedFileName.endsWith(DOT_JAR); |
| } |
| |
| /** |
| * Returns if the given file name represents a G-zip file name, where the |
| * name has an extension of *.tar.gz or *.tgz |
| * |
| * @param fileName |
| * @return true if the given file name is that of a G-zip archive, false |
| * otherwise |
| */ |
| public static boolean isTGZFile(String fileName) { |
| String normalizedFileName = fileName.toLowerCase(); |
| return normalizedFileName.endsWith(DOT_TAR_GZ) || normalizedFileName.endsWith(DOT_TGZ); |
| } |
| |
| /** |
| * Returns if the flags are for a class |
| * |
| * @param accessFlags the given access flags |
| * @return |
| */ |
| public static boolean isClass(int accessFlags) { |
| return (accessFlags & (Opcodes.ACC_ENUM | Opcodes.ACC_ANNOTATION | Opcodes.ACC_INTERFACE)) == 0; |
| } |
| |
| /** |
| * Returns if the specified file name is for a class file. A name is |
| * considered to be a class file if it ends in '.class' |
| * |
| * @param fileName |
| * @return true if the name is for a class file false otherwise |
| */ |
| public static boolean isClassFile(String fileName) { |
| return fileName.toLowerCase().endsWith(DOT_CLASS_SUFFIX); |
| } |
| |
| public static boolean isDefault(int accessFlags) { |
| // none of the private, protected or public bit is set |
| return (accessFlags & (Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED | Opcodes.ACC_PUBLIC)) == 0; |
| } |
| |
| public static final boolean isDifferentVersion(String versionToBeChecked, String referenceVersion) { |
| SinceTagVersion sinceTagVersion1 = null; |
| SinceTagVersion sinceTagVersion2 = null; |
| try { |
| sinceTagVersion1 = new SinceTagVersion(versionToBeChecked); |
| sinceTagVersion2 = new SinceTagVersion(referenceVersion); |
| } catch (IllegalArgumentException e) { |
| // We cannot compare the two versions as their format is unknown |
| // TODO (olivier) should we report these as malformed tags? |
| return false; |
| } |
| Version version1 = sinceTagVersion1.getVersion(); |
| Version version2 = sinceTagVersion2.getVersion(); |
| if (version1.getMajor() != version2.getMajor()) { |
| return true; |
| } |
| if (version1.getMinor() != version2.getMinor()) { |
| return true; |
| } |
| if (version1.getMicro() != version2.getMicro()) { |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Returns if the specified file name is for a java source file. A name is |
| * considered to be a java source file if it ends in '.java' |
| * |
| * @param fileName |
| * @return true if the name is for a java source file, false otherwise |
| */ |
| public static boolean isJavaFileName(String fileName) { |
| return fileName.toLowerCase().endsWith(DOT_JAVA_SUFFIX); |
| } |
| |
| /** |
| * Returns if the given name is {@link java.lang.Object} |
| * |
| * @param name |
| * @return true if the name is java.lang.Object, false otherwise |
| */ |
| public static boolean isJavaLangObject(String name) { |
| return name != null && name.equals(JAVA_LANG_OBJECT); |
| } |
| |
| /** |
| * Return if the name is {@link java.lang.RuntimeException} |
| * |
| * @param name |
| * @return true if the name is java.lang.RuntimeException, false otherwise |
| */ |
| public static boolean isJavaLangRuntimeException(String name) { |
| return name != null && name.equals(JAVA_LANG_RUNTIMEEXCEPTION); |
| } |
| |
| public static boolean isVisible(int modifiers) { |
| return Flags.isProtected(modifiers) || Flags.isPublic(modifiers); |
| } |
| |
| public static boolean isBinaryProject(IProject project) { |
| return org.eclipse.pde.internal.core.WorkspaceModelManager.isBinaryProject(project); |
| } |
| |
| /** |
| * Returns a new XML document. |
| * |
| * @return document |
| * @throws CoreException if unable to create a new document |
| */ |
| public static Document newDocument() throws CoreException { |
| DocumentBuilderFactory dfactory = DocumentBuilderFactory.newInstance(); |
| DocumentBuilder docBuilder = null; |
| try { |
| docBuilder = dfactory.newDocumentBuilder(); |
| } catch (ParserConfigurationException e) { |
| abort("Unable to create new XML document.", e); //$NON-NLS-1$ |
| } |
| Document doc = docBuilder.newDocument(); |
| return doc; |
| } |
| |
| /** |
| * Parses the given string representing an XML document, returning its root |
| * element. |
| * |
| * @param document XML document as a string |
| * @return the document's root element |
| * @throws CoreException if unable to parse the document |
| */ |
| public static Element parseDocument(String document) throws CoreException { |
| Element root = null; |
| InputStream stream = null; |
| try { |
| DocumentBuilder parser = DocumentBuilderFactory.newInstance().newDocumentBuilder(); |
| parser.setErrorHandler(new DefaultHandler()); |
| stream = new ByteArrayInputStream(document.getBytes(IApiCoreConstants.UTF_8)); |
| root = parser.parse(stream).getDocumentElement(); |
| } catch (ParserConfigurationException e) { |
| abort("Unable to parse XML document.", e); //$NON-NLS-1$ |
| } catch (FactoryConfigurationError e) { |
| abort("Unable to parse XML document.", e); //$NON-NLS-1$ |
| } catch (SAXException e) { |
| abort("Unable to parse XML document.", e); //$NON-NLS-1$ |
| } catch (IOException e) { |
| abort("Unable to parse XML document.", e); //$NON-NLS-1$ |
| } finally { |
| try { |
| if (stream != null) { |
| stream.close(); |
| } |
| } catch (IOException e) { |
| abort("Unable to parse XML document.", e); //$NON-NLS-1$ |
| } |
| } |
| return root; |
| } |
| |
| /** |
| * Save the given contents into the given file. The file parent folder must |
| * exist. |
| * |
| * @param file the given file target |
| * @param contents the given contents |
| * @throws IOException if an IOException occurs while saving the file |
| */ |
| public static void saveFile(File file, String contents) throws IOException { |
| BufferedWriter writer = null; |
| try { |
| writer = new BufferedWriter(new FileWriter(file)); |
| writer.write(contents); |
| writer.flush(); |
| } finally { |
| if (writer != null) { |
| try { |
| writer.close(); |
| } catch (IOException e) { |
| // ignore |
| } |
| } |
| } |
| } |
| |
| /** |
| * Returns the contents of the given file as a string, or <code>null</code> |
| * |
| * @param file the file to get the contents for |
| * @return the contents of the file as a {@link String} or <code>null</code> |
| */ |
| public static String getFileContentAsString(File file) { |
| String contents = null; |
| FileInputStream stream = null; |
| try { |
| stream = new FileInputStream(file); |
| char[] array = getInputStreamAsCharArray(stream, -1, IApiCoreConstants.UTF_8); |
| contents = new String(array); |
| } catch (IOException ioe) { |
| ApiPlugin.log(ioe); |
| } finally { |
| if (stream != null) { |
| try { |
| stream.close(); |
| } catch (IOException e) { |
| // ignore |
| } |
| } |
| } |
| return contents; |
| } |
| |
| /** |
| * Returns the given string as an {@link InputStream}. It is up to the |
| * caller to close the new stream. |
| * |
| * @param string the string to convert |
| * @return the {@link InputStream} for the given string |
| */ |
| public static InputStream getInputStreamFromString(String string) { |
| try { |
| return new ByteArrayInputStream(string.getBytes(IApiCoreConstants.UTF_8)); |
| } catch (UnsupportedEncodingException uee) { |
| ApiPlugin.log(uee); |
| } |
| return null; |
| } |
| |
| /** |
| * Serializes the given XML document into a UTF-8 string. |
| * |
| * @param document XML document to serialize |
| * @return a string representing the given document |
| * @throws CoreException if unable to serialize the document |
| */ |
| public static String serializeDocument(Document document) throws CoreException { |
| try { |
| ByteArrayOutputStream s = new ByteArrayOutputStream(); |
| TransformerFactory factory = TransformerFactory.newInstance(); |
| Transformer transformer = factory.newTransformer(); |
| transformer.setOutputProperty(OutputKeys.METHOD, "xml"); //$NON-NLS-1$ |
| transformer.setOutputProperty(OutputKeys.INDENT, "yes"); //$NON-NLS-1$ |
| transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4"); //$NON-NLS-1$ //$NON-NLS-2$ |
| DOMSource source = new DOMSource(document); |
| StreamResult outputTarget = new StreamResult(s); |
| transformer.transform(source, outputTarget); |
| return s.toString(IApiCoreConstants.UTF_8); |
| } catch (TransformerException e) { |
| abort("Unable to serialize XML document.", e); //$NON-NLS-1$ |
| } catch (IOException e) { |
| abort("Unable to serialize XML document.", e); //$NON-NLS-1$ |
| } |
| return null; |
| } |
| |
| /** |
| * Unzip the contents of the given zip in the given directory (create it if |
| * it doesn't exist) |
| */ |
| public static void unzip(String zipPath, String destDirPath) throws IOException { |
| InputStream zipIn = new FileInputStream(zipPath); |
| byte[] buf = new byte[8192]; |
| File destDir = new File(destDirPath); |
| ZipInputStream zis = new ZipInputStream(new BufferedInputStream(zipIn)); |
| BufferedOutputStream outputStream = null; |
| try { |
| ZipEntry zEntry; |
| while ((zEntry = zis.getNextEntry()) != null) { |
| // if it is empty directory, create it |
| if (zEntry.isDirectory()) { |
| new File(destDir, zEntry.getName()).mkdirs(); |
| continue; |
| } |
| // if it is a file, extract it |
| String filePath = zEntry.getName(); |
| int lastSeparator = filePath.lastIndexOf("/"); //$NON-NLS-1$ |
| String fileDir = ""; //$NON-NLS-1$ |
| if (lastSeparator >= 0) { |
| fileDir = filePath.substring(0, lastSeparator); |
| } |
| // create directory for a file |
| new File(destDir, fileDir).mkdirs(); |
| // write file |
| File outFile = new File(destDir, filePath); |
| outputStream = new BufferedOutputStream(new FileOutputStream(outFile)); |
| int n = 0; |
| while ((n = zis.read(buf)) >= 0) { |
| outputStream.write(buf, 0, n); |
| } |
| outputStream.close(); |
| } |
| } catch (IOException ioe) { |
| if (outputStream != null) { |
| try { |
| outputStream.close(); |
| } catch (IOException ioe2) { |
| } |
| } |
| } finally { |
| try { |
| zipIn.close(); |
| zis.close(); |
| } catch (IOException ioe) { |
| } |
| } |
| } |
| |
| /** |
| * Unzip the contents of the given zip in the given directory (create it if |
| * it doesn't exist) |
| */ |
| public static void guntar(String zipPath, String destDirPath) throws TarException, IOException { |
| TarFile tarFile = new TarFile(zipPath); |
| Enumeration<?> entries = tarFile.entries(); |
| byte[] buf = new byte[8192]; |
| for (; entries.hasMoreElements();) { |
| TarEntry zEntry; |
| while ((zEntry = (TarEntry) entries.nextElement()) != null) { |
| // if it is empty directory, create it |
| if (zEntry.getFileType() == TarEntry.DIRECTORY) { |
| new File(destDirPath, zEntry.getName()).mkdirs(); |
| continue; |
| } |
| // if it is a file, extract it |
| String filePath = zEntry.getName(); |
| int lastSeparator = filePath.lastIndexOf("/"); //$NON-NLS-1$ |
| String fileDir = ""; //$NON-NLS-1$ |
| if (lastSeparator >= 0) { |
| fileDir = filePath.substring(0, lastSeparator); |
| } |
| // create directory for a file |
| new File(destDirPath, fileDir).mkdirs(); |
| // write file |
| File outFile = new File(destDirPath, filePath); |
| BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(outFile)); |
| int n = 0; |
| InputStream inputStream = tarFile.getInputStream(zEntry); |
| BufferedInputStream stream = new BufferedInputStream(inputStream); |
| while ((n = stream.read(buf)) >= 0) { |
| outputStream.write(buf, 0, n); |
| } |
| outputStream.close(); |
| stream.close(); |
| } |
| } |
| } |
| |
| /** |
| * Gets the .ee file supplied to run tests based on system property. |
| * |
| * @return |
| */ |
| public static File getEEDescriptionFile() { |
| // generate a fake 1.6 ee file |
| File fakeEEFile = null; |
| PrintWriter writer = null; |
| try { |
| fakeEEFile = createTempFile("eefile", ".ee"); //$NON-NLS-1$ //$NON-NLS-2$ |
| writer = new PrintWriter(new BufferedWriter(new FileWriter(fakeEEFile))); |
| writer.print("-Djava.home="); //$NON-NLS-1$ |
| writer.println(System.getProperty("java.home")); //$NON-NLS-1$ |
| writer.print("-Dee.bootclasspath="); //$NON-NLS-1$ |
| writer.println(getJavaClassLibsAsString()); |
| writer.println("-Dee.language.level=1.6"); //$NON-NLS-1$ |
| writer.println("-Dee.class.library.level=JavaSE-1.6"); //$NON-NLS-1$ |
| writer.flush(); |
| } catch (IOException e) { |
| // ignore |
| } finally { |
| if (writer != null) { |
| writer.close(); |
| } |
| } |
| return fakeEEFile; |
| } |
| |
| /** |
| * Creates a new file in the users' <code>temp</code> directory |
| * |
| * @param prefix |
| * @param suffix |
| * @return a new temp file |
| * @throws IOException |
| * @since 1.1 |
| */ |
| public static File createTempFile(String prefix, String suffix) throws IOException { |
| File file = File.createTempFile(prefix, suffix); |
| file.deleteOnExit(); |
| FileManager.getManager().recordTempFileRoot(file.getCanonicalPath()); |
| return file; |
| } |
| |
| /** |
| * @return a string representation of all of the libraries from the bootpath |
| * of the current default system VM. |
| */ |
| public static String getJavaClassLibsAsString() { |
| String[] libs = Util.getJavaClassLibs(); |
| StringBuffer buffer = new StringBuffer(); |
| for (int i = 0, max = libs.length; i < max; i++) { |
| if (i > 0) { |
| buffer.append(File.pathSeparatorChar); |
| } |
| buffer.append(libs[i]); |
| } |
| return String.valueOf(buffer); |
| } |
| |
| /** |
| * @return an array of the library names from the bootpath of the current |
| * default system VM |
| */ |
| public static String[] getJavaClassLibs() { |
| // check bootclasspath properties for Sun, JRockit and Harmony VMs |
| String bootclasspathProperty = System.getProperty("sun.boot.class.path"); //$NON-NLS-1$ |
| if ((bootclasspathProperty == null) || (bootclasspathProperty.length() == 0)) { |
| // IBM J9 VMs |
| bootclasspathProperty = System.getProperty("vm.boot.class.path"); //$NON-NLS-1$ |
| if ((bootclasspathProperty == null) || (bootclasspathProperty.length() == 0)) { |
| // Harmony using IBM VME |
| bootclasspathProperty = System.getProperty("org.apache.harmony.boot.class.path"); //$NON-NLS-1$ |
| } |
| } |
| String[] jars = null; |
| if ((bootclasspathProperty != null) && (bootclasspathProperty.length() != 0)) { |
| StringTokenizer tokenizer = new StringTokenizer(bootclasspathProperty, File.pathSeparator); |
| final int size = tokenizer.countTokens(); |
| jars = new String[size]; |
| int i = 0; |
| while (tokenizer.hasMoreTokens()) { |
| final String fileName = toNativePath(tokenizer.nextToken()); |
| if (new File(fileName).exists()) { |
| jars[i] = fileName; |
| i++; |
| } |
| } |
| if (size != i) { |
| // resize |
| System.arraycopy(jars, 0, (jars = new String[i]), 0, i); |
| } |
| } else { |
| String jreDir = System.getProperty("java.home"); //$NON-NLS-1$ |
| final String osName = System.getProperty("os.name"); //$NON-NLS-1$ |
| if (jreDir == null) { |
| return new String[] {}; |
| } |
| if (osName.startsWith("Mac")) { //$NON-NLS-1$ |
| return new String[] { toNativePath(jreDir + "/../Classes/classes.jar") //$NON-NLS-1$ |
| }; |
| } |
| final String vmName = System.getProperty("java.vm.name"); //$NON-NLS-1$ |
| if ("J9".equals(vmName)) { //$NON-NLS-1$ |
| return new String[] { toNativePath(jreDir + "/lib/jclMax/classes.zip") //$NON-NLS-1$ |
| }; |
| } |
| String[] jarsNames = null; |
| ArrayList<String> paths = new ArrayList<>(); |
| if ("DRLVM".equals(vmName)) { //$NON-NLS-1$ |
| FilenameFilter jarFilter = (dir, name) -> name.endsWith(DOT_JAR) & !name.endsWith("-src.jar"); //$NON-NLS-1$ |
| jarsNames = new File(jreDir + "/lib/boot/").list(jarFilter); //$NON-NLS-1$ |
| addJarEntries(jreDir + "/lib/boot/", jarsNames, paths); //$NON-NLS-1$ |
| } else { |
| jarsNames = new String[] { "/lib/vm.jar", //$NON-NLS-1$ |
| "/lib/rt.jar", //$NON-NLS-1$ |
| "/lib/core.jar", //$NON-NLS-1$ |
| "/lib/security.jar", //$NON-NLS-1$ |
| "/lib/xml.jar", //$NON-NLS-1$ |
| "/lib/graphics.jar" //$NON-NLS-1$ |
| }; |
| addJarEntries(jreDir, jarsNames, paths); |
| } |
| jars = new String[paths.size()]; |
| paths.toArray(jars); |
| } |
| return jars; |
| } |
| |
| /** |
| * Makes the given path a path using native path separators as returned by |
| * File.getPath() and trimming any extra slash. |
| */ |
| public static String toNativePath(String path) { |
| String nativePath = path.replace('\\', File.separatorChar).replace('/', File.separatorChar); |
| return nativePath.endsWith("/") || nativePath.endsWith("\\") ? //$NON-NLS-1$ //$NON-NLS-2$ |
| nativePath.substring(0, nativePath.length() - 1) : nativePath; |
| } |
| |
| private static void addJarEntries(String jreDir, String[] jarNames, ArrayList<String> paths) { |
| for (String jarName : jarNames) { |
| final String currentName = jreDir + jarName; |
| File f = new File(currentName); |
| if (f.exists()) { |
| paths.add(toNativePath(currentName)); |
| } |
| } |
| } |
| |
| /** |
| * Delete a file or directory and insure that the file is no longer present |
| * on file system. In case of directory, delete all the hierarchy |
| * underneath. |
| * |
| * @param file The file or directory to delete |
| * @return true iff the file was really delete, false otherwise |
| */ |
| public static boolean delete(File file) { |
| if (!file.exists()) { |
| return true; |
| } |
| // flush all directory content |
| if (file.isDirectory()) { |
| flushDirectoryContent(file); |
| } |
| // remove file |
| file.delete(); |
| if (isFileDeleted(file)) { |
| return true; |
| } |
| return waitUntilFileDeleted(file); |
| } |
| |
| public static void flushDirectoryContent(File dir) { |
| File[] files = dir.listFiles(); |
| if (files == null) { |
| return; |
| } |
| for (File file : files) { |
| delete(file); |
| } |
| } |
| |
| /** |
| * Wait until the file is _really_ deleted on file system. |
| * |
| * @param file Deleted file |
| * @return true if the file was finally deleted, false otherwise |
| */ |
| private static boolean waitUntilFileDeleted(File file) { |
| int count = 0; |
| int delay = 10; // ms |
| int maxRetry = DELETE_MAX_WAIT / delay; |
| int time = 0; |
| while (count < maxRetry) { |
| try { |
| count++; |
| Thread.sleep(delay); |
| time += delay; |
| if (time > DELETE_MAX_TIME) { |
| DELETE_MAX_TIME = time; |
| } |
| if (DELETE_DEBUG) { |
| System.out.print('.'); |
| } |
| if (file.exists()) { |
| if (file.delete()) { |
| // SUCCESS |
| return true; |
| } |
| } |
| if (isFileDeleted(file)) { |
| // SUCCESS |
| return true; |
| } |
| // Increment waiting delay exponentially |
| if (count >= 10 && delay <= 100) { |
| count = 1; |
| delay *= 10; |
| maxRetry = DELETE_MAX_WAIT / delay; |
| if ((DELETE_MAX_WAIT % delay) != 0) { |
| maxRetry++; |
| } |
| } |
| } catch (InterruptedException ie) { |
| break; // end loop |
| } |
| } |
| System.err.println(); |
| System.err.println(" !!! ERROR: " + file + " was never deleted even after having waited " + DELETE_MAX_TIME + "ms!!!"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| System.err.println(); |
| return false; |
| } |
| |
| /** |
| * Returns whether a file is really deleted or not. Does not only rely on |
| * {@link File#exists()} method but also look if it's not in its parent |
| * children {@link #getParentChildFile(File)}. |
| * |
| * @param file The file to test if deleted |
| * @return true if the file does not exist and was not found in its parent |
| * children. |
| */ |
| public static boolean isFileDeleted(File file) { |
| return !file.exists() && getParentChildFile(file) == null; |
| } |
| |
| /** |
| * Returns the parent's child file matching the given file or null if not |
| * found. |
| * |
| * @param file The searched file in parent |
| * @return The parent's child matching the given file or null if not found. |
| */ |
| private static File getParentChildFile(File file) { |
| File parent = file.getParentFile(); |
| if (parent == null || !parent.exists()) { |
| return null; |
| } |
| File[] files = parent.listFiles(); |
| if (files == null) { |
| return null; |
| } |
| int length = files == null ? 0 : files.length; |
| if (length > 0) { |
| for (int i = 0; i < length; i++) { |
| if (files[i] == file) { |
| return files[i]; |
| } else if (files[i].equals(file)) { |
| return files[i]; |
| } else if (files[i].getPath().equals(file.getPath())) { |
| return files[i]; |
| } |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Turns the given array of strings into a {@link HashSet} |
| * |
| * @param values |
| * @return a new {@link HashSet} of the string array |
| */ |
| public static Set<String> convertAsSet(String[] values) { |
| Set<String> set = new HashSet<>(); |
| if (values != null && values.length != 0) { |
| for (String value : values) { |
| set.add(value); |
| } |
| } |
| return set; |
| } |
| |
| /** |
| * Returns an identifier for the given API component including its version |
| * identifier (component id + '(' + major + . + minor + . + micro + ')' ) |
| * |
| * @param component API component |
| * @return API component + version identifier |
| */ |
| public static String getDeltaComponentVersionsId(IApiComponent component) { |
| StringBuffer buffer = new StringBuffer(component.getSymbolicName()); |
| String version = component.getVersion(); |
| // remove the qualifier part |
| if (version != null) { |
| buffer.append(Util.VERSION_SEPARATOR); |
| try { |
| Version version2 = new Version(version); |
| buffer.append(version2.getMajor()).append('.').append(version2.getMinor()).append('.').append(version2.getMicro()); |
| } catch (IllegalArgumentException e) { |
| // the version string doesn't follow the Eclipse pattern |
| // we keep the version as is |
| buffer.append(version); |
| } |
| buffer.append(')'); |
| } |
| return String.valueOf(buffer); |
| } |
| |
| /** |
| * Returns an identifier for the given API component including its version |
| * identifier (component id + _ + major + _ + minor + _ + micro) |
| * |
| * @param component API component |
| * @return API component + version identifier |
| */ |
| public static String getComponentVersionsId(IApiComponent component) { |
| StringBuffer buffer = new StringBuffer(component.getSymbolicName()); |
| String version = component.getVersion(); |
| // remove the qualifier part |
| if (version != null) { |
| buffer.append('_'); |
| try { |
| Version version2 = new Version(version); |
| buffer.append(version2.getMajor()).append('.').append(version2.getMinor()).append('.').append(version2.getMicro()); |
| } catch (IllegalArgumentException e) { |
| // the version string doesn't follow the Eclipse pattern |
| // we keep the version as is |
| buffer.append(version); |
| } |
| } |
| return String.valueOf(buffer); |
| } |
| |
| public static String getDescriptorName(IApiType descriptor) { |
| String typeName = descriptor.getName(); |
| int index = typeName.lastIndexOf('$'); |
| if (index != -1) { |
| return typeName.replace('$', '.'); |
| } |
| return typeName; |
| } |
| |
| public static String getDeltaArgumentString(IDelta delta) { |
| String[] arguments = delta.getArguments(); |
| switch (delta.getFlags()) { |
| case IDelta.TYPE_MEMBER: |
| case IDelta.TYPE: |
| return arguments[0]; |
| case IDelta.METHOD: |
| case IDelta.CONSTRUCTOR: |
| case IDelta.ENUM_CONSTANT: |
| case IDelta.METHOD_WITH_DEFAULT_VALUE: |
| case IDelta.METHOD_WITHOUT_DEFAULT_VALUE: |
| case IDelta.FIELD: |
| return arguments[1]; |
| case IDelta.INCREASE_ACCESS: |
| switch (delta.getElementType()) { |
| case IDelta.FIELD_ELEMENT_TYPE: |
| case IDelta.METHOD_ELEMENT_TYPE: |
| case IDelta.CONSTRUCTOR_ELEMENT_TYPE: |
| return arguments[1]; |
| default: |
| return arguments[0]; |
| } |
| default: |
| break; |
| } |
| return EMPTY_STRING; |
| } |
| |
| /** |
| * Returns the string representation of the {@link IApiElement} type |
| * |
| * @param type |
| * @return the string of the {@link IApiElement} type |
| */ |
| public static String getApiElementType(int type) { |
| switch (type) { |
| case IApiElement.API_TYPE_CONTAINER: |
| return "API_TYPE_CONTAINER"; //$NON-NLS-1$ |
| case IApiElement.API_TYPE_ROOT: |
| return "API_TYPE_ROOT"; //$NON-NLS-1$ |
| case IApiElement.BASELINE: |
| return "BASELINE"; //$NON-NLS-1$ |
| case IApiElement.COMPONENT: |
| return "COMPONENT"; //$NON-NLS-1$ |
| case IApiElement.FIELD: |
| return "FIELD"; //$NON-NLS-1$ |
| case IApiElement.METHOD: |
| return "METHOD"; //$NON-NLS-1$ |
| case IApiElement.TYPE: |
| return "TYPE"; //$NON-NLS-1$ |
| default: |
| return "UNKNOWN"; //$NON-NLS-1$ |
| } |
| } |
| |
| public static boolean isConstructor(String referenceMemberName) { |
| return Arrays.equals(ConstantPool.Init, referenceMemberName.toCharArray()); |
| } |
| |
| public static boolean isManifest(IPath path) { |
| return MANIFEST_PROJECT_RELATIVE_PATH.equals(path); |
| } |
| |
| public static void touchCorrespondingResource(IProject project, IResource resource, String typeName) { |
| if (typeName != null && typeName != FilterStore.GLOBAL) { |
| if (Util.isManifest(resource.getProjectRelativePath())) { |
| try { |
| IJavaProject javaProject = JavaCore.create(project); |
| IType findType = javaProject.findType(typeName); |
| if (findType != null) { |
| ICompilationUnit compilationUnit = findType.getCompilationUnit(); |
| if (compilationUnit != null) { |
| IResource cuResource = compilationUnit.getResource(); |
| if (cuResource != null) { |
| cuResource.touch(null); |
| } |
| } |
| } |
| } catch (JavaModelException e) { |
| ApiPlugin.log(e); |
| } catch (CoreException e) { |
| ApiPlugin.log(e); |
| } |
| } else { |
| try { |
| resource.touch(null); |
| } catch (CoreException e) { |
| ApiPlugin.log(e); |
| } |
| } |
| } |
| } |
| |
| public static String getTypeNameFromMarker(IMarker marker) { |
| return marker.getAttribute(IApiMarkerConstants.MARKER_ATTR_PROBLEM_TYPE_NAME, null); |
| } |
| |
| public static IApiComponent[] getReexportedComponents(IApiComponent component) { |
| try { |
| IRequiredComponentDescription[] requiredComponents = component.getRequiredComponents(); |
| int length = requiredComponents.length; |
| if (length != 0) { |
| List<IApiComponent> reexportedComponents = null; |
| IApiBaseline baseline = component.getBaseline(); |
| for (int i = 0; i < length; i++) { |
| IRequiredComponentDescription description = requiredComponents[i]; |
| if (description.isExported()) { |
| String id = description.getId(); |
| IApiComponent reexportedComponent = baseline.getApiComponent(id); |
| if (reexportedComponent != null) { |
| if (reexportedComponents == null) { |
| reexportedComponents = new ArrayList<>(); |
| } |
| reexportedComponents.add(reexportedComponent); |
| } |
| } |
| } |
| if (reexportedComponents == null || reexportedComponents.size() == 0) { |
| return null; |
| } |
| return reexportedComponents.toArray(new IApiComponent[reexportedComponents.size()]); |
| } |
| } catch (CoreException e) { |
| ApiPlugin.log(e); |
| } |
| return null; |
| } |
| |
| /** |
| * Returns the {@link IResource} to create markers on when building. If the |
| * {@link IType} is <code>null</code> or the type cannot be located (does |
| * not exist) than the MANIFEST.MF will be returned. <code>null</code> can |
| * be returned in the case that the project does not have a manifest file. |
| * |
| * @param project the project to look in for the {@link IResource} |
| * @param type the type we are looking for the resource for, or |
| * <code>null</code> |
| * @return the {@link IResource} associated with the given {@link IType} or |
| * the MANIFEST.MF file, or <code>null</code> if the project does |
| * not have a manifest |
| */ |
| public static IResource getResource(IProject project, IType type) { |
| try { |
| if (type != null) { |
| ICompilationUnit unit = type.getCompilationUnit(); |
| if (unit != null) { |
| IResource resource = unit.getCorrespondingResource(); |
| if (resource != null && resource.exists()) { |
| return resource; |
| } |
| } |
| } |
| } catch (JavaModelException e) { |
| ApiPlugin.log(e); |
| } |
| return getManifestFile(project); |
| } |
| |
| /** |
| * Default comparator that orders {@link IApiComponent} by their ID |
| */ |
| public static final Comparator<Object> componentsorter = (o1, o2) -> { |
| if (o1 instanceof IApiComponent && o2 instanceof IApiComponent) { |
| return ((IApiComponent) o1).getSymbolicName().compareTo(((IApiComponent) o2).getSymbolicName()); |
| } |
| if (o1 instanceof SkippedComponent && o2 instanceof SkippedComponent) { |
| return ((SkippedComponent) o1).getComponentId().compareTo(((SkippedComponent) o2).getComponentId()); |
| } |
| if (o1 instanceof String && o2 instanceof String) { |
| return ((String) o1).compareTo((String) o2); |
| } |
| return -1; |
| }; |
| |
| /** |
| * Initializes the exclude set with regex support. The API baseline is used |
| * to determine which bundles should be added to the list when processing |
| * regex expressions. |
| * |
| * @param location |
| * @param baseline |
| * @return the list of bundles to be excluded |
| * @throws CoreException if the location does not describe a includes file |
| * or an IOException occurs |
| */ |
| public static FilteredElements initializeRegexFilterList(String location, IApiBaseline baseline, boolean debug) throws CoreException { |
| FilteredElements excludedElements = new FilteredElements(); |
| if (location != null) { |
| File file = new File(location); |
| InputStream stream = null; |
| char[] contents = null; |
| try { |
| stream = new BufferedInputStream(new FileInputStream(file)); |
| contents = getInputStreamAsCharArray(stream, -1, ISO_8859_1); |
| } catch (FileNotFoundException e) { |
| abort(NLS.bind(UtilMessages.Util_couldNotFindFilterFile, location), e); |
| } catch (IOException e) { |
| abort(NLS.bind(UtilMessages.Util_problemWithFilterFile, location), e); |
| } finally { |
| if (stream != null) { |
| try { |
| stream.close(); |
| } catch (IOException e) { |
| } |
| } |
| } |
| if (contents != null) { |
| LineNumberReader reader = new LineNumberReader(new StringReader(new String(contents))); |
| String line = null; |
| try { |
| while ((line = reader.readLine()) != null) { |
| line = line.trim(); |
| if (line.startsWith("#") || line.length() == 0) { //$NON-NLS-1$ |
| continue; |
| } |
| if (line.startsWith(REGULAR_EXPRESSION_START)) { |
| if (baseline != null) { |
| Util.collectRegexIds(line, excludedElements, baseline.getApiComponents(), debug); |
| } |
| } else { |
| excludedElements.addExactMatch(line); |
| } |
| } |
| } catch (IOException e) { |
| abort(NLS.bind(UtilMessages.Util_problemWithFilterFile, location), e); |
| } finally { |
| try { |
| reader.close(); |
| } catch (IOException e) { |
| } |
| } |
| } |
| } |
| return excludedElements; |
| } |
| |
| /** |
| * Collects the set of component ids that match a given regex in the exclude |
| * file |
| * |
| * @param line |
| * @param list |
| * @param components |
| */ |
| public static void collectRegexIds(String line, FilteredElements excludedElements, IApiComponent[] components, boolean debug) throws CoreException { |
| if (line.startsWith(REGULAR_EXPRESSION_START)) { |
| String componentname = line; |
| // regular expression |
| componentname = componentname.substring(2); |
| Pattern pattern = null; |
| try { |
| if (debug) { |
| System.out.println("Pattern to match : " + componentname); //$NON-NLS-1$ |
| } |
| pattern = Pattern.compile(componentname); |
| String componentid = null; |
| for (IApiComponent component : components) { |
| componentid = component.getSymbolicName(); |
| if (pattern.matcher(componentid).matches()) { |
| if (debug) { |
| System.out.println(componentid + " matched the pattern " + componentname); //$NON-NLS-1$ |
| } |
| excludedElements.addPartialMatch(componentid); |
| } else if (debug) { |
| System.out.println(componentid + " didn't match the pattern " + componentname); //$NON-NLS-1$ |
| } |
| } |
| } catch (PatternSyntaxException e) { |
| abort(NLS.bind(UtilMessages.comparison_invalidRegularExpression, componentname), e); |
| } |
| } |
| } |
| |
| /** |
| * Default comparator that orders {@link File}s by their name |
| */ |
| public static final Comparator<Object> filesorter = (o1, o2) -> { |
| if (o1 instanceof File && o2 instanceof File) { |
| return ((File) o1).getName().compareTo(((File) o2).getName()); |
| } |
| return 0; |
| }; |
| |
| /** |
| * Returns true if the given {@link IApiType} is API or not, where API is |
| * defined as having API visibility in an API description and having either |
| * the public of protected Java flag set |
| * |
| * @param visibility |
| * @param typeDescriptor |
| * @return true if the given type is API, false otherwise |
| */ |
| public static boolean isAPI(int visibility, IApiType typeDescriptor) { |
| int access = typeDescriptor.getModifiers(); |
| return VisibilityModifiers.isAPI(visibility) && (Flags.isPublic(access) || Flags.isProtected(access)); |
| } |
| |
| /** |
| * Simple method to walk an array and call <code>toString()</code> on each |
| * of the entries. Does not descend into sub-collections. |
| * |
| * @param array the array |
| * @return the comma-separated string representation of the the array |
| * @since 1.0.3 |
| */ |
| public static String deepToString(Object[] array) { |
| StringBuffer buffer = new StringBuffer(); |
| for (int i = 0; i < array.length; i++) { |
| buffer.append(array[i].toString()); |
| if (i < array.length - 1) { |
| buffer.append(','); |
| } |
| } |
| return buffer.toString(); |
| } |
| } |