1 /*******************************************************************************
2  * Copyright (c) 2007, 2020 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *******************************************************************************/
14 package org.eclipse.pde.api.tools.internal.util;
15 
16 import java.io.BufferedInputStream;
17 import java.io.BufferedOutputStream;
18 import java.io.BufferedWriter;
19 import java.io.ByteArrayInputStream;
20 import java.io.ByteArrayOutputStream;
21 import java.io.File;
22 import java.io.FileFilter;
23 import java.io.FileInputStream;
24 import java.io.FileNotFoundException;
25 import java.io.FileOutputStream;
26 import java.io.FileWriter;
27 import java.io.FilenameFilter;
28 import java.io.IOException;
29 import java.io.InputStream;
30 import java.io.LineNumberReader;
31 import java.io.PrintWriter;
32 import java.io.StringReader;
33 import java.lang.reflect.Field;
34 import java.nio.ByteBuffer;
35 import java.nio.CharBuffer;
36 import java.nio.charset.Charset;
37 import java.nio.charset.CharsetDecoder;
38 import java.nio.charset.CodingErrorAction;
39 import java.nio.charset.IllegalCharsetNameException;
40 import java.nio.charset.StandardCharsets;
41 import java.nio.charset.UnsupportedCharsetException;
42 import java.text.MessageFormat;
43 import java.util.ArrayList;
44 import java.util.Arrays;
45 import java.util.Collections;
46 import java.util.Comparator;
47 import java.util.Enumeration;
48 import java.util.HashMap;
49 import java.util.HashSet;
50 import java.util.List;
51 import java.util.Map;
52 import java.util.Properties;
53 import java.util.Set;
54 import java.util.StringTokenizer;
55 import java.util.jar.JarFile;
56 import java.util.regex.Pattern;
57 import java.util.regex.PatternSyntaxException;
58 import java.util.stream.Stream;
59 import java.util.zip.ZipEntry;
60 import java.util.zip.ZipFile;
61 import java.util.zip.ZipInputStream;
62 
63 import javax.xml.parsers.DocumentBuilder;
64 import javax.xml.parsers.DocumentBuilderFactory;
65 import javax.xml.parsers.FactoryConfigurationError;
66 import javax.xml.parsers.ParserConfigurationException;
67 import javax.xml.transform.OutputKeys;
68 import javax.xml.transform.Transformer;
69 import javax.xml.transform.TransformerException;
70 import javax.xml.transform.TransformerFactory;
71 import javax.xml.transform.dom.DOMSource;
72 import javax.xml.transform.stream.StreamResult;
73 
74 import org.eclipse.core.filebuffers.FileBuffers;
75 import org.eclipse.core.filebuffers.ITextFileBufferManager;
76 import org.eclipse.core.filebuffers.LocationKind;
77 import org.eclipse.core.resources.IFile;
78 import org.eclipse.core.resources.IMarker;
79 import org.eclipse.core.resources.IProject;
80 import org.eclipse.core.resources.IResource;
81 import org.eclipse.core.resources.IncrementalProjectBuilder;
82 import org.eclipse.core.resources.ResourcesPlugin;
83 import org.eclipse.core.runtime.Assert;
84 import org.eclipse.core.runtime.AssertionFailedException;
85 import org.eclipse.core.runtime.CoreException;
86 import org.eclipse.core.runtime.IPath;
87 import org.eclipse.core.runtime.IProgressMonitor;
88 import org.eclipse.core.runtime.IStatus;
89 import org.eclipse.core.runtime.NullProgressMonitor;
90 import org.eclipse.core.runtime.OperationCanceledException;
91 import org.eclipse.core.runtime.Path;
92 import org.eclipse.core.runtime.Status;
93 import org.eclipse.core.runtime.SubMonitor;
94 import org.eclipse.core.runtime.jobs.Job;
95 import org.eclipse.jdt.core.Flags;
96 import org.eclipse.jdt.core.ICompilationUnit;
97 import org.eclipse.jdt.core.IField;
98 import org.eclipse.jdt.core.IJavaElement;
99 import org.eclipse.jdt.core.IJavaProject;
100 import org.eclipse.jdt.core.IMember;
101 import org.eclipse.jdt.core.IMethod;
102 import org.eclipse.jdt.core.IPackageFragment;
103 import org.eclipse.jdt.core.IType;
104 import org.eclipse.jdt.core.ITypeParameter;
105 import org.eclipse.jdt.core.ITypeRoot;
106 import org.eclipse.jdt.core.JavaCore;
107 import org.eclipse.jdt.core.JavaModelException;
108 import org.eclipse.jdt.core.Signature;
109 import org.eclipse.jdt.internal.compiler.codegen.ConstantPool;
110 import org.eclipse.jdt.internal.core.BinaryType;
111 import org.eclipse.jdt.internal.core.ClassFile;
112 import org.eclipse.jdt.internal.core.CompilationUnit;
113 import org.eclipse.jdt.internal.core.DefaultWorkingCopyOwner;
114 import org.eclipse.jdt.internal.core.JavaProject;
115 import org.eclipse.jdt.internal.core.NameLookup;
116 import org.eclipse.jdt.internal.core.PackageFragment;
117 import org.eclipse.jdt.internal.core.SourceType;
118 import org.eclipse.jdt.launching.IVMInstall;
119 import org.eclipse.jdt.launching.JavaRuntime;
120 import org.eclipse.jdt.launching.LibraryLocation;
121 import org.eclipse.jdt.launching.environments.ExecutionEnvironmentDescription;
122 import org.eclipse.jface.text.IDocument;
123 import org.eclipse.osgi.util.NLS;
124 import org.eclipse.pde.api.tools.internal.FilterStore;
125 import org.eclipse.pde.api.tools.internal.IApiCoreConstants;
126 import org.eclipse.pde.api.tools.internal.builder.BuildState;
127 import org.eclipse.pde.api.tools.internal.problems.ApiProblemFactory;
128 import org.eclipse.pde.api.tools.internal.provisional.ApiPlugin;
129 import org.eclipse.pde.api.tools.internal.provisional.Factory;
130 import org.eclipse.pde.api.tools.internal.provisional.IApiMarkerConstants;
131 import org.eclipse.pde.api.tools.internal.provisional.IRequiredComponentDescription;
132 import org.eclipse.pde.api.tools.internal.provisional.VisibilityModifiers;
133 import org.eclipse.pde.api.tools.internal.provisional.comparator.DeltaVisitor;
134 import org.eclipse.pde.api.tools.internal.provisional.comparator.IDelta;
135 import org.eclipse.pde.api.tools.internal.provisional.descriptors.IReferenceTypeDescriptor;
136 import org.eclipse.pde.api.tools.internal.provisional.model.IApiBaseline;
137 import org.eclipse.pde.api.tools.internal.provisional.model.IApiComponent;
138 import org.eclipse.pde.api.tools.internal.provisional.model.IApiElement;
139 import org.eclipse.pde.api.tools.internal.provisional.model.IApiType;
140 import org.eclipse.pde.api.tools.internal.provisional.model.IApiTypeRoot;
141 import org.eclipse.pde.api.tools.internal.provisional.problems.IApiProblem;
142 import org.eclipse.pde.api.tools.internal.provisional.problems.IApiProblemTypes;
143 import org.eclipse.pde.api.tools.internal.search.SkippedComponent;
144 import org.objectweb.asm.Opcodes;
145 import org.osgi.framework.Version;
146 import org.w3c.dom.Document;
147 import org.w3c.dom.Element;
148 import org.xml.sax.SAXException;
149 import org.xml.sax.helpers.DefaultHandler;
150 
151 /**
152  * A Utility class to use for API tools
153  *
154  * @since 1.0.0
155  */
156 public final class Util {
157 
158 	public static final String DOT_TGZ = ".tgz"; //$NON-NLS-1$
159 	public static final String DOT_TAR_GZ = ".tar.gz"; //$NON-NLS-1$
160 	public static final String DOT_JAR = ".jar"; //$NON-NLS-1$
161 	public static final String DOT_ZIP = ".zip"; //$NON-NLS-1$
162 
163 	public static final char VERSION_SEPARATOR = '(';
164 
165 	/**
166 	 * Class that runs a build in the workspace or the given project
167 	 */
168 	private static final class BuildJob extends Job {
169 		private final IProject[] fProjects;
170 		private int fBuildType;
171 
172 		/**
173 		 * Constructor
174 		 *
175 		 * @param name
176 		 * @param project
177 		 */
BuildJob(String name, IProject[] projects)178 		BuildJob(String name, IProject[] projects) {
179 			this(name, projects, IncrementalProjectBuilder.FULL_BUILD);
180 		}
181 
BuildJob(String name, IProject[] projects, int buildType)182 		BuildJob(String name, IProject[] projects, int buildType) {
183 			super(name);
184 			fProjects = projects;
185 			this.fBuildType = buildType;
186 		}
187 
188 		@Override
belongsTo(Object family)189 		public boolean belongsTo(Object family) {
190 			return ResourcesPlugin.FAMILY_MANUAL_BUILD == family;
191 		}
192 
193 		/**
194 		 * Returns if this build job is covered by another build job
195 		 *
196 		 * @param other
197 		 * @return true if covered by another build job, false otherwise
198 		 */
isCoveredBy(BuildJob other)199 		public boolean isCoveredBy(BuildJob other) {
200 			if (other.fProjects == null) {
201 				return true;
202 			}
203 			if (this.fProjects != null) {
204 				for (int i = 0, max = this.fProjects.length; i < max; i++) {
205 					if (!other.contains(this.fProjects[i])) {
206 						return false;
207 					}
208 				}
209 				return true;
210 			}
211 			return false;
212 		}
213 
contains(IProject project)214 		public boolean contains(IProject project) {
215 			if (project == null) {
216 				return false;
217 			}
218 			for (IProject fProject : this.fProjects) {
219 				if (project.equals(fProject)) {
220 					return true;
221 				}
222 			}
223 			return false;
224 		}
225 
226 		@Override
run(IProgressMonitor monitor)227 		protected IStatus run(IProgressMonitor monitor) {
228 			synchronized (getClass()) {
229 				if (monitor.isCanceled()) {
230 					return Status.CANCEL_STATUS;
231 				}
232 				// cancelBuild(ResourcesPlugin.FAMILY_AUTO_BUILD);
233 				cancelBuild(ResourcesPlugin.FAMILY_MANUAL_BUILD);
234 			}
235 			try {
236 				if (fProjects != null) {
237 					SubMonitor localmonitor = SubMonitor.convert(monitor, UtilMessages.Util_0, fProjects.length);
238 					for (IProject currentProject : fProjects) {
239 						if (this.fBuildType == IncrementalProjectBuilder.FULL_BUILD) {
240 							BuildState.setLastBuiltState(currentProject, null);
241 						}
242 						localmonitor.subTask(NLS.bind(UtilMessages.Util_5, currentProject.getName()));
243 						if (ResourcesPlugin.getWorkspace().isAutoBuilding()) {
244 							currentProject.touch(null);
245 						} else {
246 							currentProject.build(IncrementalProjectBuilder.INCREMENTAL_BUILD, localmonitor.split(1));
247 						}
248 					}
249 				}
250 			} catch (CoreException e) {
251 				return new Status(e.getStatus().getSeverity(), ApiPlugin.PLUGIN_ID, ApiPlugin.INTERNAL_ERROR, UtilMessages.Util_builder_errorMessage, e);
252 			} catch (OperationCanceledException e) {
253 				return Status.CANCEL_STATUS;
254 			} finally {
255 				monitor.done();
256 			}
257 			return Status.OK_STATUS;
258 		}
259 
cancelBuild(Object jobfamily)260 		private void cancelBuild(Object jobfamily) {
261 			Job[] buildJobs = Job.getJobManager().find(jobfamily);
262 			for (Job curr : buildJobs) {
263 				if (curr != this && curr instanceof BuildJob) {
264 					BuildJob job = (BuildJob) curr;
265 					if (job.isCoveredBy(this)) {
266 						curr.cancel(); // cancel all other build jobs of our
267 										// kind
268 					}
269 				}
270 			}
271 		}
272 	}
273 
274 	public static final String EMPTY_STRING = "";//$NON-NLS-1$
275 	public static final String DEFAULT_PACKAGE_NAME = EMPTY_STRING;
276 	public static final String MANIFEST_NAME = "MANIFEST.MF"; //$NON-NLS-1$
277 
278 	public static final String DOT_CLASS_SUFFIX = ".class"; //$NON-NLS-1$
279 	public static final String DOT_JAVA_SUFFIX = ".java"; //$NON-NLS-1$
280 
281 	/**
282 	 * Constant representing the default size to read from an input stream
283 	 */
284 	private static final int DEFAULT_READING_SIZE = 8192;
285 
286 	private static final String JAVA_LANG_OBJECT = "java.lang.Object"; //$NON-NLS-1$
287 	private static final String JAVA_LANG_RUNTIMEEXCEPTION = "java.lang.RuntimeException"; //$NON-NLS-1$
288 	public static final String LINE_DELIMITER = System.lineSeparator();
289 
290 	public static final String UNKNOWN_ELEMENT_KIND = "UNKNOWN_ELEMENT_KIND"; //$NON-NLS-1$
291 
292 	public static final String UNKNOWN_FLAGS = "UNKNOWN_FLAGS"; //$NON-NLS-1$
293 	public static final String UNKNOWN_KIND = "UNKNOWN_KIND"; //$NON-NLS-1$
294 	public static final String UNKNOWN_VISIBILITY = "UNKNOWN_VISIBILITY"; //$NON-NLS-1$
295 	public static final String ISO_8859_1 = "ISO-8859-1"; //$NON-NLS-1$
296 	public static final String REGULAR_EXPRESSION_START = "R:"; //$NON-NLS-1$
297 
298 	// Trace for delete operation
299 	/*
300 	 * Maximum time wasted repeating delete operations while running JDT/Core
301 	 * tests.
302 	 */
303 	private static int DELETE_MAX_TIME = 0;
304 	/**
305 	 * Trace deletion operations while running JDT/Core tests.
306 	 */
307 	private static boolean DELETE_DEBUG = false;
308 	/**
309 	 * Maximum of time in milliseconds to wait in deletion operation while
310 	 * running JDT/Core tests. Default is 10 seconds. This number cannot exceed
311 	 * 1 minute (i.e. 60000). <br>
312 	 * To avoid too many loops while waiting, the ten first ones are done
313 	 * waiting 10ms before repeating, the ten loops after are done waiting 100ms
314 	 * and the other loops are done waiting 1s...
315 	 */
316 	private static int DELETE_MAX_WAIT = 10000;
317 
318 	public static final IPath MANIFEST_PROJECT_RELATIVE_PATH = new Path(JarFile.MANIFEST_NAME);
319 
320 	public static final String ORG_ECLIPSE_SWT = "org.eclipse.swt"; //$NON-NLS-1$
321 
322 	public static final int LATEST_OPCODES_ASM = Opcodes.ASM8;
323 
324 	/**
325 	 * Throws an exception with the given message and underlying exception.
326 	 *
327 	 * @param message error message
328 	 * @param exception underlying exception, or <code>null</code>
329 	 * @throws CoreException
330 	 */
abort(String message, Throwable exception)331 	private static void abort(String message, Throwable exception) throws CoreException {
332 		IStatus status = new Status(IStatus.ERROR, ApiPlugin.PLUGIN_ID, message, exception);
333 		throw new CoreException(status);
334 	}
335 
336 	/**
337 	 * Appends a property to the given string buffer with the given key and
338 	 * value in the format "key=value\n".
339 	 *
340 	 * @param buffer buffer to append to
341 	 * @param key key
342 	 * @param value value
343 	 */
appendProperty(StringBuilder buffer, String key, String value)344 	private static void appendProperty(StringBuilder buffer, String key, String value) {
345 		buffer.append(key);
346 		buffer.append('=');
347 		buffer.append(value);
348 		buffer.append('\n');
349 	}
350 
351 	/**
352 	 * Collects all of the deltas from the given parent delta
353 	 *
354 	 * @param delta
355 	 * @return
356 	 */
collectAllDeltas(IDelta delta)357 	public static List<IDelta> collectAllDeltas(IDelta delta) {
358 		final List<IDelta> list = new ArrayList<>();
359 		delta.accept(new DeltaVisitor() {
360 			@Override
361 			public void endVisit(IDelta localDelta) {
362 				if (localDelta.getChildren().length == 0) {
363 					list.add(localDelta);
364 				}
365 				super.endVisit(localDelta);
366 			}
367 		});
368 		return list;
369 	}
370 
371 	/**
372 	 * Collects files into the collector array list
373 	 *
374 	 * @param root the root to collect the files from
375 	 * @param collector the collector to place matches into
376 	 * @param fileFilter the filter for files or <code>null</code> to accept all
377 	 *            files
378 	 */
collectAllFiles(File root, ArrayList<File> collector, FileFilter fileFilter)379 	private static void collectAllFiles(File root, ArrayList<File> collector, FileFilter fileFilter) {
380 		File[] files = root.listFiles(fileFilter);
381 		for (final File currentFile : files) {
382 			if (currentFile.isDirectory()) {
383 				collectAllFiles(currentFile, collector, fileFilter);
384 			} else {
385 				collector.add(currentFile);
386 			}
387 		}
388 	}
389 
390 	/**
391 	 * Returns all of the API projects in the workspace
392 	 *
393 	 * @return all of the API projects in the workspace or <code>null</code> if
394 	 *         there are none.
395 	 */
getApiProjects()396 	public static IProject[] getApiProjects() {
397 		IProject[] allProjects = ResourcesPlugin.getWorkspace().getRoot().getProjects();
398 		ArrayList<IProject> temp = new ArrayList<>();
399 		IProject project = null;
400 		for (IProject allProject : allProjects) {
401 			project = allProject;
402 			if (project.isAccessible()) {
403 				try {
404 					if (project.hasNature(org.eclipse.pde.api.tools.internal.provisional.ApiPlugin.NATURE_ID)) {
405 						temp.add(project);
406 					}
407 				} catch (CoreException e) {
408 					// should not happen
409 				}
410 			}
411 		}
412 		IProject[] projects = null;
413 		if (temp.size() != 0) {
414 			projects = new IProject[temp.size()];
415 			temp.toArray(projects);
416 		}
417 		return projects;
418 	}
419 
420 	/**
421 	 * Returns all of the API projects in the workspace
422 	 *
423 	 * @param sourcelevel
424 	 * @return all of the API projects in the workspace or <code>null</code> if
425 	 *         there are none.
426 	 */
getApiProjectsMinSourceLevel(String sourcelevel)427 	public static IProject[] getApiProjectsMinSourceLevel(String sourcelevel) {
428 		IProject[] allProjects = ResourcesPlugin.getWorkspace().getRoot().getProjects();
429 		ArrayList<IProject> temp = new ArrayList<>();
430 		IProject project = null;
431 		for (IProject allProject : allProjects) {
432 			project = allProject;
433 			if (project.isAccessible()) {
434 				try {
435 					if (project.hasNature(org.eclipse.pde.api.tools.internal.provisional.ApiPlugin.NATURE_ID)) {
436 						IJavaProject jp = JavaCore.create(project);
437 						String src = jp.getOption(JavaCore.COMPILER_SOURCE, true);
438 						if (src != null && src.compareTo(sourcelevel) >= 0) {
439 							temp.add(project);
440 						}
441 					}
442 				} catch (CoreException e) {
443 					// should not happen
444 				}
445 			}
446 		}
447 		IProject[] projects = null;
448 		if (temp.size() != 0) {
449 			projects = new IProject[temp.size()];
450 			temp.toArray(projects);
451 		}
452 		return projects;
453 	}
454 
455 	/**
456 	 * Copies the given file to the new file
457 	 *
458 	 * @param file
459 	 * @param newFile
460 	 * @return if the copy succeeded
461 	 */
copy(File file, File newFile)462 	public static boolean copy(File file, File newFile) {
463 		byte[] bytes = null;
464 		BufferedInputStream inputStream = null;
465 		try {
466 			inputStream = new BufferedInputStream(new FileInputStream(file));
467 			bytes = Util.getInputStreamAsByteArray(inputStream, -1);
468 		} catch (IOException e) {
469 			ApiPlugin.log(e);
470 		} finally {
471 			if (inputStream != null) {
472 				try {
473 					inputStream.close();
474 				} catch (IOException e) {
475 					ApiPlugin.log(e);
476 				}
477 			}
478 		}
479 		if (bytes != null) {
480 			BufferedOutputStream outputStream = null;
481 			try {
482 				outputStream = new BufferedOutputStream(new FileOutputStream(newFile));
483 				outputStream.write(bytes);
484 				outputStream.flush();
485 			} catch (IOException e) {
486 				ApiPlugin.log(e);
487 			} finally {
488 				if (outputStream != null) {
489 					try {
490 						outputStream.close();
491 					} catch (IOException e) {
492 						// ignore
493 					}
494 				}
495 			}
496 			return true;
497 		}
498 		return false;
499 	}
500 
501 	/**
502 	 * Creates an EE file for the given JRE and specified EE id
503 	 *
504 	 * @param jre
505 	 * @param eeid
506 	 * @return
507 	 * @throws IOException
508 	 */
createEEFile(IVMInstall jre, String eeid)509 	public static File createEEFile(IVMInstall jre, String eeid) throws IOException {
510 		String string = Util.generateEEContents(jre, eeid);
511 		File eeFile = createTempFile("eed", ".ee"); //$NON-NLS-1$ //$NON-NLS-2$
512 		FileOutputStream outputStream = null;
513 		try {
514 			outputStream = new FileOutputStream(eeFile);
515 			outputStream.write(string.getBytes(StandardCharsets.UTF_8));
516 		} finally {
517 			if (outputStream != null) {
518 				outputStream.close();
519 			}
520 		}
521 		return eeFile;
522 	}
523 
524 	/**
525 	 * Returns whether the objects are equal, accounting for either one being
526 	 * <code>null</code>.
527 	 *
528 	 * @param o1
529 	 * @param o2
530 	 * @return whether the objects are equal, or both are <code>null</code>
531 	 */
equalsOrNull(Object o1, Object o2)532 	public static boolean equalsOrNull(Object o1, Object o2) {
533 		if (o1 == null) {
534 			return o2 == null;
535 		}
536 		return o1.equals(o2);
537 	}
538 
539 	/**
540 	 * Returns an execution environment description for the given VM.
541 	 *
542 	 * @param vm JRE to create an definition for
543 	 * @return an execution environment description for the given VM
544 	 * @throws IOException if unable to generate description
545 	 */
generateEEContents(IVMInstall vm, String eeId)546 	public static String generateEEContents(IVMInstall vm, String eeId) throws IOException {
547 		StringBuilder buffer = new StringBuilder();
548 		appendProperty(buffer, ExecutionEnvironmentDescription.JAVA_HOME, vm.getInstallLocation().getCanonicalPath());
549 		StringBuilder paths = new StringBuilder();
550 		LibraryLocation[] libraryLocations = JavaRuntime.getLibraryLocations(vm);
551 		for (int i = 0; i < libraryLocations.length; i++) {
552 			LibraryLocation lib = libraryLocations[i];
553 			paths.append(lib.getSystemLibraryPath().toOSString());
554 			if (i < (libraryLocations.length - 1)) {
555 				paths.append(File.pathSeparatorChar);
556 			}
557 		}
558 		appendProperty(buffer, ExecutionEnvironmentDescription.BOOT_CLASS_PATH, paths.toString());
559 		appendProperty(buffer, ExecutionEnvironmentDescription.CLASS_LIB_LEVEL, eeId);
560 		return buffer.toString();
561 	}
562 
563 	/**
564 	 * Returns an array of all of the files from the given root that are
565 	 * accepted by the given file filter. If the file filter is null all files
566 	 * within the given root are returned.
567 	 *
568 	 * @param root
569 	 * @param fileFilter
570 	 * @return the list of files from within the given root
571 	 */
getAllFiles(File root, FileFilter fileFilter)572 	public static File[] getAllFiles(File root, FileFilter fileFilter) {
573 		ArrayList<File> files = new ArrayList<>();
574 		if (root.isDirectory()) {
575 			collectAllFiles(root, files, fileFilter);
576 			File[] result = new File[files.size()];
577 			files.toArray(result);
578 			return result;
579 		}
580 		return null;
581 	}
582 
583 	/**
584 	 * Returns a build job that will perform a full build on the given projects.
585 	 *
586 	 * If <code>projects</code> are null, then an AssertionFailedException is
587 	 * thrown
588 	 *
589 	 * @param projects the projects to build
590 	 * @return the build job
591 	 * @throws AssertionFailedException if the given projects are null
592 	 */
getBuildJob(final IProject[] projects)593 	public static Job getBuildJob(final IProject[] projects) {
594 		Assert.isNotNull(projects);
595 		Job buildJob = new BuildJob(UtilMessages.Util_4, projects);
596 		buildJob.setRule(ResourcesPlugin.getWorkspace().getRuleFactory().buildRule());
597 		buildJob.setUser(true);
598 		return buildJob;
599 	}
600 
601 	/**
602 	 * Returns a build job that will return the build that corresponds to the
603 	 * given build kind on the given projects.
604 	 *
605 	 * If <code>projects</code> are null, then an AssertionFailedException is
606 	 * thrown
607 	 *
608 	 * @param projects the projects to build
609 	 * @param buildKind the given build kind
610 	 * @return the build job
611 	 * @throws AssertionFailedException if the given projects are null
612 	 */
getBuildJob(final IProject[] projects, int buildKind)613 	public static Job getBuildJob(final IProject[] projects, int buildKind) {
614 		Assert.isNotNull(projects);
615 		Job buildJob = new BuildJob(UtilMessages.Util_4, projects, buildKind);
616 		buildJob.setRule(ResourcesPlugin.getWorkspace().getRuleFactory().buildRule());
617 		buildJob.setUser(true);
618 		return buildJob;
619 	}
620 
621 	/**
622 	 * Returns a result of searching the given components for class file with
623 	 * the given type name.
624 	 *
625 	 * @param components API components to search or <code>null</code> if none
626 	 * @param typeName type to search for
627 	 * @return class file or <code>null</code> if none found
628 	 */
getClassFile(IApiComponent[] components, String typeName)629 	public static IApiTypeRoot getClassFile(IApiComponent[] components, String typeName) {
630 		if (components == null) {
631 			return null;
632 		}
633 		CoreException ex = null;
634 		IApiComponent component = null;
635 		for (IApiComponent apiComponent : components) {
636 			if (apiComponent != null) {
637 				try {
638 					IApiTypeRoot classFile = apiComponent.findTypeRoot(typeName);
639 					if (classFile != null) {
640 						return classFile;
641 					}
642 				} catch (CoreException e) {
643 					if (ex == null) {
644 						ex = e;
645 						component = apiComponent;
646 					}
647 				}
648 			}
649 		}
650 		if (ex != null) {
651 			ApiPlugin.log("Error while resolving class file for: " + typeName + " via " + component.getName(), ex); //$NON-NLS-1$ //$NON-NLS-2$
652 		}
653 		return null;
654 	}
655 
656 	/**
657 	 * Return a string that represents the element type of the given delta.
658 	 * Returns {@link #UNKNOWN_ELEMENT_KIND} if the element type cannot be
659 	 * determined.
660 	 *
661 	 * @param delta the given delta
662 	 * @return a string that represents the element type of the given delta.
663 	 */
getDeltaElementType(IDelta delta)664 	public static String getDeltaElementType(IDelta delta) {
665 		return getDeltaElementType(delta.getElementType());
666 	}
667 
668 	/**
669 	 * Returns a text representation of a marker severity level
670 	 *
671 	 * @param severity
672 	 * @return text of a marker severity level
673 	 */
getSeverity(int severity)674 	public static String getSeverity(int severity) {
675 		switch (severity) {
676 			case IMarker.SEVERITY_ERROR: {
677 				return "ERROR"; //$NON-NLS-1$
678 			}
679 			case IMarker.SEVERITY_INFO: {
680 				return "INFO"; //$NON-NLS-1$
681 			}
682 			case IMarker.SEVERITY_WARNING: {
683 				return "WARNING"; //$NON-NLS-1$
684 			}
685 			default: {
686 				return "UNKNOWN_SEVERITY"; //$NON-NLS-1$
687 			}
688 		}
689 	}
690 
691 	/**
692 	 * Return an int value that represents the given element type Returns -1 if
693 	 * the element type cannot be determined.
694 	 *
695 	 * @param elementType the given element type
696 	 * @return an int that represents the given element type constant.
697 	 */
getDeltaElementTypeValue(String elementType)698 	public static int getDeltaElementTypeValue(String elementType) {
699 		Class<IDelta> IDeltaClass = IDelta.class;
700 		try {
701 			Field field = IDeltaClass.getField(elementType);
702 			return field.getInt(null);
703 		} catch (SecurityException | IllegalArgumentException | NoSuchFieldException | IllegalAccessException e) {
704 			// ignore
705 		}
706 		return -1;
707 	}
708 
709 	/**
710 	 * Return a string that represents the given element type Returns
711 	 * {@link #UNKNOWN_ELEMENT_KIND} if the element type cannot be determined.
712 	 *
713 	 * @param elementType the given element type
714 	 * @return a string that represents the given element type.
715 	 */
getDeltaElementType(int elementType)716 	public static String getDeltaElementType(int elementType) {
717 		switch (elementType) {
718 			case IDelta.ANNOTATION_ELEMENT_TYPE:
719 				return "ANNOTATION_ELEMENT_TYPE"; //$NON-NLS-1$
720 			case IDelta.INTERFACE_ELEMENT_TYPE:
721 				return "INTERFACE_ELEMENT_TYPE"; //$NON-NLS-1$
722 			case IDelta.ENUM_ELEMENT_TYPE:
723 				return "ENUM_ELEMENT_TYPE"; //$NON-NLS-1$
724 			case IDelta.API_COMPONENT_ELEMENT_TYPE:
725 				return "API_COMPONENT_ELEMENT_TYPE"; //$NON-NLS-1$
726 			case IDelta.API_BASELINE_ELEMENT_TYPE:
727 				return "API_BASELINE_ELEMENT_TYPE"; //$NON-NLS-1$
728 			case IDelta.CONSTRUCTOR_ELEMENT_TYPE:
729 				return "CONSTRUCTOR_ELEMENT_TYPE"; //$NON-NLS-1$
730 			case IDelta.METHOD_ELEMENT_TYPE:
731 				return "METHOD_ELEMENT_TYPE"; //$NON-NLS-1$
732 			case IDelta.FIELD_ELEMENT_TYPE:
733 				return "FIELD_ELEMENT_TYPE"; //$NON-NLS-1$
734 			case IDelta.CLASS_ELEMENT_TYPE:
735 				return "CLASS_ELEMENT_TYPE"; //$NON-NLS-1$
736 			case IDelta.TYPE_PARAMETER_ELEMENT_TYPE:
737 				return "TYPE_PARAMETER_ELEMENT_TYPE"; //$NON-NLS-1$
738 			default:
739 				break;
740 		}
741 		return UNKNOWN_ELEMENT_KIND;
742 	}
743 
744 	/**
745 	 * Return a string that represents the given flags Returns
746 	 * {@link #UNKNOWN_FLAGS} if the flags cannot be determined.
747 	 *
748 	 * @param flags the given delta's flags
749 	 * @return a string that represents the given flags.
750 	 */
getDeltaFlagsName(int flags)751 	public static String getDeltaFlagsName(int flags) {
752 		switch (flags) {
753 			case IDelta.ABSTRACT_TO_NON_ABSTRACT:
754 				return "ABSTRACT_TO_NON_ABSTRACT"; //$NON-NLS-1$
755 			case IDelta.ANNOTATION_DEFAULT_VALUE:
756 				return "ANNOTATION_DEFAULT_VALUE"; //$NON-NLS-1$
757 			case IDelta.API_COMPONENT:
758 				return "API_COMPONENT"; //$NON-NLS-1$
759 			case IDelta.ARRAY_TO_VARARGS:
760 				return "ARRAY_TO_VARARGS"; //$NON-NLS-1$
761 			case IDelta.CHECKED_EXCEPTION:
762 				return "CHECKED_EXCEPTION"; //$NON-NLS-1$
763 			case IDelta.CLASS_BOUND:
764 				return "CLASS_BOUND"; //$NON-NLS-1$
765 			case IDelta.CLINIT:
766 				return "CLINIT"; //$NON-NLS-1$
767 			case IDelta.CONSTRUCTOR:
768 				return "CONSTRUCTOR"; //$NON-NLS-1$
769 			case IDelta.CONTRACTED_SUPERINTERFACES_SET:
770 				return "CONTRACTED_SUPERINTERFACES_SET"; //$NON-NLS-1$
771 			case IDelta.DECREASE_ACCESS:
772 				return "DECREASE_ACCESS"; //$NON-NLS-1$
773 			case IDelta.ENUM_CONSTANT:
774 				return "ENUM_CONSTANT"; //$NON-NLS-1$
775 			case IDelta.EXECUTION_ENVIRONMENT:
776 				return "EXECUTION_ENVIRONMENT"; //$NON-NLS-1$
777 			case IDelta.EXPANDED_SUPERINTERFACES_SET:
778 				return "EXPANDED_SUPERINTERFACES_SET"; //$NON-NLS-1$
779 			case IDelta.EXPANDED_SUPERINTERFACES_SET_BREAKING:
780 				return "EXPANDED_SUPERINTERFACES_SET_BREAKING"; //$NON-NLS-1$
781 			case IDelta.FIELD:
782 				return "FIELD"; //$NON-NLS-1$
783 			case IDelta.FIELD_MOVED_UP:
784 				return "FIELD_MOVED_UP"; //$NON-NLS-1$
785 			case IDelta.FINAL_TO_NON_FINAL:
786 				return "FINAL_TO_NON_FINAL"; //$NON-NLS-1$
787 			case IDelta.FINAL_TO_NON_FINAL_NON_STATIC:
788 				return "FINAL_TO_NON_FINAL_NON_STATIC"; //$NON-NLS-1$
789 			case IDelta.FINAL_TO_NON_FINAL_STATIC_CONSTANT:
790 				return "FINAL_TO_NON_FINAL_STATIC_CONSTANT"; //$NON-NLS-1$
791 			case IDelta.FINAL_TO_NON_FINAL_STATIC_NON_CONSTANT:
792 				return "FINAL_TO_NON_FINAL_STATIC_NON_CONSTANT"; //$NON-NLS-1$
793 			case IDelta.INCREASE_ACCESS:
794 				return "INCREASE_ACCESS"; //$NON-NLS-1$
795 			case IDelta.INTERFACE_BOUND:
796 				return "INTERFACE_BOUND"; //$NON-NLS-1$
797 			case IDelta.METHOD:
798 				return "METHOD"; //$NON-NLS-1$
799 			case IDelta.DEFAULT_METHOD:
800 			case IDelta.SUPER_INTERFACE_DEFAULT_METHOD:
801 				return "DEFAULT_METHOD"; //$NON-NLS-1$
802 			case IDelta.METHOD_MOVED_UP:
803 				return "METHOD_MOVED_UP"; //$NON-NLS-1$
804 			case IDelta.METHOD_WITH_DEFAULT_VALUE:
805 				return "METHOD_WITH_DEFAULT_VALUE"; //$NON-NLS-1$
806 			case IDelta.METHOD_WITHOUT_DEFAULT_VALUE:
807 				return "METHOD_WITHOUT_DEFAULT_VALUE"; //$NON-NLS-1$
808 			case IDelta.NATIVE_TO_NON_NATIVE:
809 				return "NATIVE_TO_NON_NATIVE"; //$NON-NLS-1$
810 			case IDelta.NON_ABSTRACT_TO_ABSTRACT:
811 				return "NON_ABSTRACT_TO_ABSTRACT"; //$NON-NLS-1$
812 			case IDelta.NON_FINAL_TO_FINAL:
813 				return "NON_FINAL_TO_FINAL"; //$NON-NLS-1$
814 			case IDelta.NON_NATIVE_TO_NATIVE:
815 				return "NON_NATIVE_TO_NATIVE"; //$NON-NLS-1$
816 			case IDelta.NON_STATIC_TO_STATIC:
817 				return "NON_STATIC_TO_STATIC"; //$NON-NLS-1$
818 			case IDelta.NON_SYNCHRONIZED_TO_SYNCHRONIZED:
819 				return "NON_SYNCHRONIZED_TO_SYNCHRONIZED"; //$NON-NLS-1$
820 			case IDelta.NON_TRANSIENT_TO_TRANSIENT:
821 				return "NON_TRANSIENT_TO_TRANSIENT"; //$NON-NLS-1$
822 			case IDelta.OVERRIDEN_METHOD:
823 				return "OVERRIDEN_METHOD"; //$NON-NLS-1$
824 			case IDelta.STATIC_TO_NON_STATIC:
825 				return "STATIC_TO_NON_STATIC"; //$NON-NLS-1$
826 			case IDelta.SUPERCLASS:
827 				return "SUPERCLASS"; //$NON-NLS-1$
828 			case IDelta.SYNCHRONIZED_TO_NON_SYNCHRONIZED:
829 				return "SYNCHRONIZED_TO_NON_SYNCHRONIZED"; //$NON-NLS-1$
830 			case IDelta.TYPE_CONVERSION:
831 				return "TYPE_CONVERSION"; //$NON-NLS-1$
832 			case IDelta.TRANSIENT_TO_NON_TRANSIENT:
833 				return "TRANSIENT_TO_NON_TRANSIENT"; //$NON-NLS-1$
834 			case IDelta.TYPE:
835 				return "TYPE"; //$NON-NLS-1$
836 			case IDelta.TYPE_ARGUMENTS:
837 				return "TYPE_ARGUMENTS"; //$NON-NLS-1$
838 			case IDelta.TYPE_MEMBER:
839 				return "TYPE_MEMBER"; //$NON-NLS-1$
840 			case IDelta.TYPE_PARAMETER:
841 				return "TYPE_PARAMETER"; //$NON-NLS-1$
842 			case IDelta.TYPE_PARAMETER_NAME:
843 				return "TYPE_PARAMETER_NAME"; //$NON-NLS-1$
844 			case IDelta.TYPE_PARAMETERS:
845 				return "TYPE_PARAMETERS"; //$NON-NLS-1$
846 			case IDelta.TYPE_VISIBILITY:
847 				return "TYPE_VISIBILITY"; //$NON-NLS-1$
848 			case IDelta.UNCHECKED_EXCEPTION:
849 				return "UNCHECKED_EXCEPTION"; //$NON-NLS-1$
850 			case IDelta.VALUE:
851 				return "VALUE"; //$NON-NLS-1$
852 			case IDelta.VARARGS_TO_ARRAY:
853 				return "VARARGS_TO_ARRAY"; //$NON-NLS-1$
854 			case IDelta.RESTRICTIONS:
855 				return "RESTRICTIONS"; //$NON-NLS-1$
856 			case IDelta.API_TYPE:
857 				return "API_TYPE"; //$NON-NLS-1$
858 			case IDelta.NON_VOLATILE_TO_VOLATILE:
859 				return "NON_VOLATILE_TO_VOLATILE"; //$NON-NLS-1$
860 			case IDelta.VOLATILE_TO_NON_VOLATILE:
861 				return "VOLATILE_TO_NON_VOLATILE"; //$NON-NLS-1$
862 			case IDelta.MINOR_VERSION:
863 				return "MINOR_VERSION"; //$NON-NLS-1$
864 			case IDelta.MAJOR_VERSION:
865 				return "MAJOR_VERSION"; //$NON-NLS-1$
866 			case IDelta.API_FIELD:
867 				return "API_FIELD"; //$NON-NLS-1$
868 			case IDelta.API_METHOD:
869 				return "API_METHOD"; //$NON-NLS-1$
870 			case IDelta.API_CONSTRUCTOR:
871 				return "API_CONSTRUCTOR"; //$NON-NLS-1$
872 			case IDelta.API_ENUM_CONSTANT:
873 				return "API_ENUM_CONSTANT"; //$NON-NLS-1$
874 			case IDelta.API_METHOD_WITH_DEFAULT_VALUE:
875 				return "API_METHOD_WITH_DEFAULT_VALUE"; //$NON-NLS-1$
876 			case IDelta.API_METHOD_WITHOUT_DEFAULT_VALUE:
877 				return "API_METHOD_WITHOUT_DEFAULT_VALUE"; //$NON-NLS-1$
878 			case IDelta.TYPE_ARGUMENT:
879 				return "TYPE_ARGUMENT"; //$NON-NLS-1$
880 			case IDelta.SUPER_INTERFACE_WITH_METHODS:
881 				return "SUPER_INTERFACE_WITH_METHODS"; //$NON-NLS-1$
882 			case IDelta.REEXPORTED_API_TYPE:
883 				return "REEXPORTED_API_TYPE"; //$NON-NLS-1$
884 			case IDelta.REEXPORTED_TYPE:
885 				return "REEXPORTED_TYPE"; //$NON-NLS-1$
886 			case IDelta.METHOD_MOVED_DOWN:
887 				return "METHOD_MOVED_DOWN"; //$NON-NLS-1$
888 			case IDelta.DEPRECATION:
889 				return "DEPRECATION"; //$NON-NLS-1$
890 			default:
891 				break;
892 		}
893 		return UNKNOWN_FLAGS;
894 	}
895 
896 	/**
897 	 * Return a string that represents the kind of the given delta. Returns
898 	 * {@link #UNKNOWN_KIND} if the kind cannot be determined.
899 	 *
900 	 * @param delta the given delta
901 	 * @return a string that represents the kind of the given delta.
902 	 */
getDeltaKindName(IDelta delta)903 	public static String getDeltaKindName(IDelta delta) {
904 		return getDeltaKindName(delta.getKind());
905 	}
906 
907 	/**
908 	 * Return a string that represents the given kind. Returns
909 	 * {@link #UNKNOWN_KIND} if the kind cannot be determined.
910 	 *
911 	 * @param delta the given kind
912 	 * @return a string that represents the given kind.
913 	 */
getDeltaKindName(int kind)914 	public static String getDeltaKindName(int kind) {
915 		switch (kind) {
916 			case IDelta.ADDED:
917 				return "ADDED"; //$NON-NLS-1$
918 			case IDelta.CHANGED:
919 				return "CHANGED"; //$NON-NLS-1$
920 			case IDelta.REMOVED:
921 				return "REMOVED"; //$NON-NLS-1$
922 			default:
923 				break;
924 		}
925 		return UNKNOWN_KIND;
926 	}
927 
928 	/**
929 	 * Returns the preference key for the given element type, the given kind and
930 	 * the given flags.
931 	 *
932 	 * @param elementType the given element type (retrieved using
933 	 *            {@link IDelta#getElementType()}
934 	 * @param kind the given kind (retrieved using {@link IDelta#getKind()}
935 	 * @param flags the given flags (retrieved using {@link IDelta#getFlags()}
936 	 * @return the preference key for the given element type, the given kind and
937 	 *         the given flags.
938 	 */
getDeltaPrefererenceKey(int elementType, int kind, int flags)939 	public static String getDeltaPrefererenceKey(int elementType, int kind, int flags) {
940 		StringBuilder buffer = new StringBuilder(Util.getDeltaElementType(elementType));
941 		buffer.append('_').append(Util.getDeltaKindName(kind));
942 		if (flags != -1) {
943 			buffer.append('_');
944 			switch (flags) {
945 				case IDelta.API_FIELD:
946 					buffer.append(Util.getDeltaFlagsName(IDelta.FIELD));
947 					break;
948 				case IDelta.API_ENUM_CONSTANT:
949 					buffer.append(Util.getDeltaFlagsName(IDelta.ENUM_CONSTANT));
950 					break;
951 				case IDelta.API_CONSTRUCTOR:
952 					buffer.append(Util.getDeltaFlagsName(IDelta.CONSTRUCTOR));
953 					break;
954 				case IDelta.API_METHOD:
955 					buffer.append(Util.getDeltaFlagsName(IDelta.METHOD));
956 					break;
957 				case IDelta.API_METHOD_WITH_DEFAULT_VALUE:
958 					if (kind == IDelta.REMOVED) {
959 						buffer.append(Util.getDeltaFlagsName(IDelta.METHOD));
960 					} else {
961 						buffer.append(Util.getDeltaFlagsName(IDelta.METHOD_WITH_DEFAULT_VALUE));
962 					}
963 					break;
964 				case IDelta.API_METHOD_WITHOUT_DEFAULT_VALUE:
965 					if (kind == IDelta.REMOVED) {
966 						buffer.append(Util.getDeltaFlagsName(IDelta.METHOD));
967 					} else {
968 						buffer.append(Util.getDeltaFlagsName(IDelta.METHOD_WITHOUT_DEFAULT_VALUE));
969 					}
970 					break;
971 				case IDelta.METHOD_WITH_DEFAULT_VALUE:
972 					if (kind == IDelta.REMOVED) {
973 						buffer.append(Util.getDeltaFlagsName(IDelta.METHOD));
974 					} else {
975 						buffer.append(Util.getDeltaFlagsName(IDelta.METHOD_WITH_DEFAULT_VALUE));
976 					}
977 					break;
978 				case IDelta.METHOD_WITHOUT_DEFAULT_VALUE:
979 					if (kind == IDelta.REMOVED) {
980 						buffer.append(Util.getDeltaFlagsName(IDelta.METHOD));
981 					} else {
982 						buffer.append(Util.getDeltaFlagsName(IDelta.METHOD_WITHOUT_DEFAULT_VALUE));
983 					}
984 					break;
985 				default:
986 					buffer.append(Util.getDeltaFlagsName(flags));
987 			}
988 		}
989 		return String.valueOf(buffer);
990 	}
991 
992 	/**
993 	 * Returns the details of the API delta as a string
994 	 *
995 	 * @param delta
996 	 * @return the details of the delta as a string
997 	 */
getDetail(IDelta delta)998 	public static String getDetail(IDelta delta) {
999 		StringBuilder buffer = new StringBuilder();
1000 		switch (delta.getElementType()) {
1001 			case IDelta.CLASS_ELEMENT_TYPE:
1002 				buffer.append("class"); //$NON-NLS-1$
1003 				break;
1004 			case IDelta.ANNOTATION_ELEMENT_TYPE:
1005 				buffer.append("annotation"); //$NON-NLS-1$
1006 				break;
1007 			case IDelta.INTERFACE_ELEMENT_TYPE:
1008 				buffer.append("interface"); //$NON-NLS-1$
1009 				break;
1010 			case IDelta.API_COMPONENT_ELEMENT_TYPE:
1011 				buffer.append("api component"); //$NON-NLS-1$
1012 				break;
1013 			case IDelta.API_BASELINE_ELEMENT_TYPE:
1014 				buffer.append("api baseline"); //$NON-NLS-1$
1015 				break;
1016 			case IDelta.METHOD_ELEMENT_TYPE:
1017 				buffer.append("method"); //$NON-NLS-1$
1018 				break;
1019 			case IDelta.CONSTRUCTOR_ELEMENT_TYPE:
1020 				buffer.append("constructor"); //$NON-NLS-1$
1021 				break;
1022 			case IDelta.ENUM_ELEMENT_TYPE:
1023 				buffer.append("enum"); //$NON-NLS-1$
1024 				break;
1025 			case IDelta.FIELD_ELEMENT_TYPE:
1026 				buffer.append("field"); //$NON-NLS-1$
1027 				break;
1028 			default:
1029 				break;
1030 		}
1031 		buffer.append(' ');
1032 		switch (delta.getKind()) {
1033 			case IDelta.ADDED:
1034 				buffer.append("added"); //$NON-NLS-1$
1035 				break;
1036 			case IDelta.REMOVED:
1037 				buffer.append("removed"); //$NON-NLS-1$
1038 				break;
1039 			case IDelta.CHANGED:
1040 				buffer.append("changed"); //$NON-NLS-1$
1041 				break;
1042 			default:
1043 				buffer.append("unknown kind"); //$NON-NLS-1$
1044 				break;
1045 		}
1046 		buffer.append(' ').append(getDeltaFlagsName(delta.getFlags())).append(' ').append(delta.getTypeName()).append("#").append(delta.getKey()); //$NON-NLS-1$
1047 		return String.valueOf(buffer);
1048 	}
1049 
1050 	/**
1051 	 * Returns the {@link IDocument} for the specified {@link ICompilationUnit}
1052 	 *
1053 	 * @param cu
1054 	 * @return the {@link IDocument} for the specified {@link ICompilationUnit}
1055 	 * @throws CoreException
1056 	 */
getDocument(ICompilationUnit cu)1057 	public static IDocument getDocument(ICompilationUnit cu) throws CoreException {
1058 		if (cu.getOwner() == null) {
1059 			IFile file = (IFile) cu.getResource();
1060 			if (file.exists()) {
1061 				ITextFileBufferManager bufferManager = FileBuffers.getTextFileBufferManager();
1062 				IPath path = cu.getPath();
1063 				bufferManager.connect(path, LocationKind.IFILE, new NullProgressMonitor());
1064 				try {
1065 					return bufferManager.getTextFileBuffer(path, LocationKind.IFILE).getDocument();
1066 				} finally {
1067 					bufferManager.disconnect(path, LocationKind.IFILE, null);
1068 				}
1069 			}
1070 		}
1071 		return new org.eclipse.jface.text.Document(cu.getSource());
1072 	}
1073 
1074 	/**
1075 	 * Returns the OSGi profile properties corresponding to the given execution
1076 	 * environment id, or <code>null</code> if none.
1077 	 *
1078 	 * @param eeId OSGi profile identifier
1079 	 *
1080 	 * @return the corresponding properties or <code>null</code> if none
1081 	 */
getEEProfile(String eeId)1082 	public static Properties getEEProfile(String eeId) {
1083 		String profileName = eeId + ".profile"; //$NON-NLS-1$
1084 		InputStream stream = Util.class.getResourceAsStream("profiles/" + profileName); //$NON-NLS-1$
1085 		if (stream != null) {
1086 			try {
1087 				Properties profile = new Properties();
1088 				profile.load(stream);
1089 				return profile;
1090 			} catch (IOException e) {
1091 				ApiPlugin.log(e);
1092 			} finally {
1093 				try {
1094 					stream.close();
1095 				} catch (IOException e) {
1096 					ApiPlugin.log(e);
1097 				}
1098 			}
1099 		}
1100 		return null;
1101 	}
1102 
1103 	/**
1104 	 * Returns the number of fragments for the given version value, -1 if the
1105 	 * format is unknown. The version is formed like: [optional plug-in name]
1106 	 * major.minor.micro.qualifier.
1107 	 *
1108 	 * @param version the given version value
1109 	 * @return the number of fragments for the given version value or -1 if the
1110 	 *         format is unknown
1111 	 * @throws IllegalArgumentException if version is null
1112 	 */
getFragmentNumber(String version)1113 	public static final int getFragmentNumber(String version) {
1114 		if (version == null) {
1115 			throw new IllegalArgumentException("The given version should not be null"); //$NON-NLS-1$
1116 		}
1117 		int index = version.indexOf(' ');
1118 		char[] charArray = version.toCharArray();
1119 		int length = charArray.length;
1120 		if (index + 1 >= length) {
1121 			return -1;
1122 		}
1123 		int counter = 1;
1124 		for (int i = index + 1; i < length; i++) {
1125 			switch (charArray[i]) {
1126 				case '0':
1127 				case '1':
1128 				case '2':
1129 				case '3':
1130 				case '4':
1131 				case '5':
1132 				case '6':
1133 				case '7':
1134 				case '8':
1135 				case '9':
1136 					continue;
1137 				case '.':
1138 					counter++;
1139 					break;
1140 				default:
1141 					return -1;
1142 			}
1143 		}
1144 		return counter;
1145 	}
1146 
getIMember(IDelta delta, IJavaProject javaProject)1147 	public static IMember getIMember(IDelta delta, IJavaProject javaProject) {
1148 		String typeName = delta.getTypeName();
1149 		if (typeName == null) {
1150 			return null;
1151 		}
1152 		IType type = null;
1153 		try {
1154 			type = javaProject.findType(typeName.replace('$', '.'));
1155 		} catch (JavaModelException e) {
1156 			// ignore
1157 		}
1158 		IType typeInProject = getTypeInSameJavaProject(type, typeName, javaProject);
1159 		if (typeInProject != null) {
1160 			type = typeInProject;
1161 		}
1162 		if (type instanceof BinaryType) {
1163 			IType sourceType = Util.findSourceTypeinJavaProject(javaProject, delta.getTypeName().replace('$', '.'));
1164 			if (sourceType != null) {
1165 				type = sourceType;
1166 			}
1167 		}
1168 		if (type == null) {
1169 			return null;
1170 		}
1171 		String key = delta.getKey();
1172 		switch (delta.getElementType()) {
1173 			case IDelta.FIELD_ELEMENT_TYPE: {
1174 				IField field = type.getField(key);
1175 				if (field.exists()) {
1176 					return field;
1177 				}
1178 			}
1179 				break;
1180 			case IDelta.CLASS_ELEMENT_TYPE:
1181 			case IDelta.ANNOTATION_ELEMENT_TYPE:
1182 			case IDelta.INTERFACE_ELEMENT_TYPE:
1183 			case IDelta.ENUM_ELEMENT_TYPE:
1184 				// we report the marker on the type
1185 				switch (delta.getKind()) {
1186 					case IDelta.ADDED:
1187 						switch (delta.getFlags()) {
1188 							case IDelta.FIELD:
1189 							case IDelta.ENUM_CONSTANT:
1190 								IField field = type.getField(key);
1191 								if (field.exists()) {
1192 									return field;
1193 								}
1194 								break;
1195 							case IDelta.METHOD_WITH_DEFAULT_VALUE:
1196 							case IDelta.METHOD_WITHOUT_DEFAULT_VALUE:
1197 							case IDelta.METHOD:
1198 							case IDelta.DEFAULT_METHOD:
1199 							case IDelta.SUPER_INTERFACE_DEFAULT_METHOD:
1200 							case IDelta.CONSTRUCTOR:
1201 								return getMethod(type, key);
1202 							case IDelta.TYPE_MEMBER:
1203 								IType type2 = type.getType(key);
1204 								if (type2.exists()) {
1205 									return type2;
1206 								}
1207 								break;
1208 							default:
1209 								break;
1210 						}
1211 						break;
1212 					case IDelta.REMOVED:
1213 						switch (delta.getFlags()) {
1214 							case IDelta.API_FIELD:
1215 							case IDelta.API_ENUM_CONSTANT:
1216 								IField field = type.getField(key);
1217 								if (field.exists()) {
1218 									return field;
1219 								}
1220 								break;
1221 							case IDelta.API_METHOD_WITH_DEFAULT_VALUE:
1222 							case IDelta.API_METHOD_WITHOUT_DEFAULT_VALUE:
1223 							case IDelta.API_METHOD:
1224 							case IDelta.API_CONSTRUCTOR:
1225 								return getMethod(type, key);
1226 							default:
1227 								break;
1228 						}
1229 						break;
1230 					default:
1231 						break;
1232 				}
1233 				return type;
1234 			case IDelta.METHOD_ELEMENT_TYPE:
1235 			case IDelta.CONSTRUCTOR_ELEMENT_TYPE: {
1236 				return getMethod(type, key);
1237 			}
1238 			case IDelta.API_COMPONENT_ELEMENT_TYPE:
1239 				return type;
1240 			default:
1241 				break;
1242 		}
1243 		return null;
1244 	}
1245 
updateType(String typeName, IJavaProject javaProject)1246 	public static IType updateType(String typeName, IJavaProject javaProject) throws JavaModelException {
1247 		String typeNameWithDot = typeName.replace('$', '.');
1248 		String typeNameWithSeparator = typeNameWithDot.replace(".", "/"); //$NON-NLS-1$ //$NON-NLS-2$
1249 		IPath path = new Path(typeNameWithSeparator);
1250 		IPath pathExceptLastSegment = path.removeLastSegments(1);
1251 		IJavaElement packFrag = javaProject.findElement(pathExceptLastSegment, DefaultWorkingCopyOwner.PRIMARY);
1252 		if (packFrag instanceof PackageFragment) {
1253 			PackageFragment pf = (PackageFragment) packFrag;
1254 			ArrayList<?> children = pf.getChildrenOfType(IJavaElement.COMPILATION_UNIT);
1255 			for (Object object : children) {
1256 				if (object instanceof CompilationUnit) {
1257 					CompilationUnit compilationUn = (CompilationUnit) object;
1258 					ITypeRoot typeRoot = compilationUn.getTypeRoot();
1259 					if (typeRoot.findPrimaryType() == null) {
1260 						continue;
1261 					}
1262 					if (typeRoot.findPrimaryType().getFullyQualifiedName().equals(typeName.replace('$', '.'))) {
1263 						return typeRoot.findPrimaryType();
1264 					}
1265 				}
1266 
1267 			}
1268 			ArrayList<?> children2 = pf.getChildrenOfType(IJavaElement.CLASS_FILE);
1269 			for (Object object : children2) {
1270 				if (object instanceof ClassFile) {
1271 					ClassFile compilationUn = (ClassFile) object;
1272 					ITypeRoot typeRoot = compilationUn.getTypeRoot();
1273 					if (typeRoot.findPrimaryType() == null) {
1274 						continue;
1275 					}
1276 					if (typeRoot.findPrimaryType().getFullyQualifiedName().equals(typeName.replace('$', '.'))) {
1277 						return typeRoot.findPrimaryType();
1278 					}
1279 				}
1280 			}
1281 		}
1282 		return null;
1283 
1284 	}
1285 
1286 	/**
1287 	 * Checks if type is not in the same project, then it tries to get the type
1288 	 * in the same project and return type if it could find one. It return null
1289 	 * if type is in the same project or if type cannot be found in the same
1290 	 * project.
1291 	 *
1292 	 * @param type
1293 	 * @param typeName
1294 	 * @param javaProject
1295 	 * @return
1296 	 */
1297 
getTypeInSameJavaProject(IType type, String typeName, IJavaProject javaProject)1298 	public static IType getTypeInSameJavaProject(IType type, String typeName, IJavaProject javaProject) {
1299 		if (type == null) {
1300 			return null;
1301 		}
1302 		IJavaElement ancestor = type.getAncestor(IJavaElement.JAVA_PROJECT);
1303 		IType newType = null;
1304 		try {
1305 			if (ancestor instanceof IJavaProject) {
1306 				IJavaProject pro = (IJavaProject) ancestor;
1307 				if (!pro.equals(javaProject)) {
1308 					newType = updateType(typeName, javaProject);
1309 					if (newType != null) {
1310 						return newType;
1311 					}
1312 				}
1313 			}
1314 		}
1315 		catch (Exception e) {
1316 			// return null
1317 		}
1318 		return newType;
1319 	}
1320 
getMethod(IType type, String key)1321 	private static IMember getMethod(IType type, String key) {
1322 		boolean isGeneric = false;
1323 		int indexOfTypeVariable = key.indexOf('<');
1324 		int index = 0;
1325 		if (indexOfTypeVariable == -1) {
1326 			int indexOfParen = key.indexOf('(');
1327 			if (indexOfParen == -1) {
1328 				return null;
1329 			}
1330 			index = indexOfParen;
1331 		} else {
1332 			int indexOfParen = key.indexOf('(');
1333 			if (indexOfParen == -1) {
1334 				return null;
1335 			}
1336 			if (indexOfParen < indexOfTypeVariable) {
1337 				index = indexOfParen;
1338 			} else {
1339 				index = indexOfTypeVariable;
1340 				isGeneric = true;
1341 			}
1342 		}
1343 		String selector = key.substring(0, index);
1344 		String descriptor = key.substring(index, key.length());
1345 		IMethod method = null;
1346 		String signature = descriptor.replace('/', '.');
1347 		String[] parameterTypes = null;
1348 		if (isGeneric) {
1349 			// remove all type variables first
1350 			signature = signature.substring(signature.indexOf('('));
1351 			parameterTypes = Signature.getParameterTypes(signature);
1352 		} else {
1353 			parameterTypes = Signature.getParameterTypes(signature);
1354 		}
1355 
1356 		try {
1357 			method = type.getMethod(selector, parameterTypes);
1358 		} catch (IllegalArgumentException e) {
1359 			ApiPlugin.log(e);
1360 		}
1361 		if (method == null) {
1362 			return null;
1363 		}
1364 		if (method.exists()) {
1365 			return method;
1366 		}
1367 		// if the method is not null and it doesn't exist, it might be the
1368 		// default constructor or a constructor in inner type
1369 		if (selector.equals(type.getElementName())) {
1370 			if (parameterTypes.length == 0) {
1371 				return null;
1372 			}
1373 			// Perhaps a constructor on an inner type?
1374 			IJavaElement parent = type.getParent();
1375 			if (parent instanceof IType) {
1376 				String parentTypeSig = Signature.createTypeSignature(((IType) parent).getFullyQualifiedName(), true);
1377 				if (Signatures.matches(parentTypeSig, parameterTypes[0])) {
1378 					IMethod constructor = type.getMethod(selector, Arrays.copyOfRange(parameterTypes, 1, parameterTypes.length));
1379 					try {
1380 						if (constructor.exists() && constructor.isConstructor()) {
1381 							return constructor;
1382 						}
1383 						String contructorSig = Signature.createMethodSignature(Arrays.copyOfRange(parameterTypes, 1, parameterTypes.length), Signature.getReturnType(signature));
1384 						IMethod[] methods = type.findMethods(constructor);
1385 						if (methods != null) {
1386 							if (methods.length == 1 && methods[0].isConstructor()) {
1387 								return methods[0];
1388 							}
1389 							// findMethods() checks simple type names, so
1390 							// it's possible to have multiple matches with
1391 							// different package names
1392 							for (IMethod m : methods) {
1393 								try {
1394 									if (m.isConstructor() && m.getNumberOfParameters() == parameterTypes.length - 1 && Signatures.matchesSignatures(generateBinarySignature(m), contructorSig)) {
1395 										return m;
1396 									}
1397 								} catch (JavaModelException e) {
1398 									// ignore
1399 								}
1400 							}
1401 						}
1402 					} catch (JavaModelException e) {
1403 						// ignore
1404 					}
1405 				}
1406 			}
1407 		}
1408 		// Let JDT have a go
1409 		IMethod[] methods = type.findMethods(method);
1410 		if (methods != null && methods.length == 1) {
1411 			/* exact match found */
1412 			return methods[0];
1413 		}
1414 		if (methods == null || methods.length == 0) {
1415 			/* no methods found: may be due to type erasure */
1416 			try {
1417 				methods = type.getMethods();
1418 			} catch (JavaModelException e) {
1419 				ApiPlugin.log(new Status(IStatus.ERROR, ApiPlugin.PLUGIN_ID, NLS.bind("Unable to retrieve methods for {0}", type.getFullyQualifiedName()), e)); //$NON-NLS-1$
1420 				return null;
1421 			}
1422 		}
1423 
1424 		/*
1425 		 * findMethods() checks simple type names, so it's possible to have
1426 		 * multiple matches with different package names. Or we may need to
1427 		 * check with type erasure.
1428 		 */
1429 		for (IMethod m : methods) {
1430 			try {
1431 				if (!m.getElementName().equals(selector) || m.getNumberOfParameters() != parameterTypes.length) {
1432 					continue;
1433 				}
1434 				if (Signatures.matchesSignatures(generateBinarySignature(m), signature)) {
1435 					return m;
1436 				}
1437 			} catch (JavaModelException e) {
1438 				// ignore
1439 			}
1440 		}
1441 
1442 		/*
1443 		 * Unclear what circumstances that this could happen, so provide more
1444 		 * information to help understand why
1445 		 */
1446 		StringBuilder sb = new StringBuilder();
1447 		for (IMethod m : methods) {
1448 			sb.append('\n').append(m.getHandleIdentifier());
1449 		}
1450 		ApiPlugin.log(new Status(IStatus.ERROR, ApiPlugin.PLUGIN_ID, NLS.bind(UtilMessages.Util_6, new String[] {
1451 				selector, descriptor }) + sb.toString()));
1452 		// do not default to the enclosing type - see bug 224713
1453 		return null;
1454 	}
1455 
1456 	/**
1457 	 * Generate the binary signature for the provided method. This is the
1458 	 * type-erased signature written out to the .class file.
1459 	 *
1460 	 * @param method the method
1461 	 * @return the method signature as would be encoded in a .class file
1462 	 * @throws JavaModelException
1463 	 */
generateBinarySignature(IMethod method)1464 	private static String generateBinarySignature(IMethod method) throws JavaModelException {
1465 		ITypeParameter[] typeTPs = method.getDeclaringType().getTypeParameters();
1466 		ITypeParameter[] methodTPs = method.getTypeParameters();
1467 		if (typeTPs.length == 0 && methodTPs.length == 0) {
1468 			return method.getSignature();
1469 		}
1470 		Map<String, String> lookup = new HashMap<>();
1471 		Stream.concat(Stream.of(typeTPs), Stream.of(methodTPs)).forEach(tp -> {
1472 			try {
1473 				String sigs[] = tp.getBoundsSignatures();
1474 				lookup.put(tp.getElementName(), sigs.length == 1 ? sigs[0] : "Ljava.lang.Object;"); //$NON-NLS-1$
1475 			} catch (JavaModelException e) {
1476 				/* ignore */
1477 			}
1478 		});
1479 		String[] parameterTypes = Stream.of(method.getParameterTypes()).map(p -> expandParameterType(p, lookup)).toArray(String[]::new);
1480 		return Signature.createMethodSignature(parameterTypes, expandParameterType(method.getReturnType(), lookup));
1481 	}
1482 
1483 	/**
1484 	 * Rewrite a parameter type signature with type erasure and using the
1485 	 * parameterized type bounds lookup table. For example:
1486 	 *
1487 	 * <pre>
1488 	 *     expand("QList&lt;QE;&gt;;", {"E" &rarr; "Ljava.lang.Object;"}) = "QList;"
1489 	 *     expand("QE;", {"E" &rarr; "Ljava.lang.Object;"}) = "Ljava.lang.Object;"
1490 	 * </pre>
1491 	 *
1492 	 * @param parameterTypeSig the type signature for a parameter
1493 	 * @param bounds the type bounds as expressed on the method and class
1494 	 * @return a rewritten parameter type signature as would be found in the .class file
1495 	 */
expandParameterType(String parameterTypeSig, Map<String, String> bounds)1496 	private static String expandParameterType(String parameterTypeSig, Map<String, String> bounds) {
1497 		String erased = Signature.getTypeErasure(parameterTypeSig);
1498 		if (erased.charAt(0) == Signature.C_UNRESOLVED || erased.charAt(0) == Signature.C_TYPE_VARIABLE) {
1499 			String repl = bounds.get(Signature.getSignatureSimpleName(erased));
1500 			if (repl != null) {
1501 				return repl;
1502 			}
1503 		}
1504 		return erased;
1505 	}
1506 
1507 	/**
1508 	 * Returns the given input stream as a byte array
1509 	 *
1510 	 * @param stream the stream to get as a byte array
1511 	 * @param length the length to read from the stream or -1 for unknown
1512 	 * @return the given input stream as a byte array
1513 	 * @throws IOException
1514 	 */
getInputStreamAsByteArray(InputStream stream, int length)1515 	public static byte[] getInputStreamAsByteArray(InputStream stream, int length) throws IOException {
1516 		byte[] contents;
1517 		if (length == -1) {
1518 			contents = new byte[0];
1519 			int contentsLength = 0;
1520 			int amountRead = -1;
1521 			do {
1522 				// read at least 8K
1523 				int amountRequested = Math.max(stream.available(), DEFAULT_READING_SIZE);
1524 				// resize contents if needed
1525 				if (contentsLength + amountRequested > contents.length) {
1526 					System.arraycopy(contents, 0, contents = new byte[contentsLength + amountRequested], 0, contentsLength);
1527 				}
1528 				// read as many bytes as possible
1529 				amountRead = stream.read(contents, contentsLength, amountRequested);
1530 				if (amountRead > 0) {
1531 					// remember length of contents
1532 					contentsLength += amountRead;
1533 				}
1534 			} while (amountRead != -1);
1535 			// resize contents if necessary
1536 			if (contentsLength < contents.length) {
1537 				System.arraycopy(contents, 0, contents = new byte[contentsLength], 0, contentsLength);
1538 			}
1539 		} else {
1540 			contents = new byte[length];
1541 			int len = 0;
1542 			int readSize = 0;
1543 			while ((readSize != -1) && (len != length)) {
1544 				// See PR 1FMS89U
1545 				// We record first the read size. In this case length is the
1546 				// actual
1547 				// read size.
1548 				len += readSize;
1549 				readSize = stream.read(contents, len, length - len);
1550 			}
1551 		}
1552 		return contents;
1553 	}
1554 
1555 	/**
1556 	 * Returns the given input stream's contents as a character array. If a
1557 	 * length is specified (i.e. if length != -1), this represents the number of
1558 	 * bytes in the stream. Note the specified stream is not closed in this
1559 	 * method
1560 	 *
1561 	 * @param stream the stream to get convert to the char array
1562 	 * @param length the length of the input stream, or -1 if unknown
1563 	 * @param encoding the encoding to use when reading the stream
1564 	 * @return the given input stream's contents as a character array.
1565 	 * @throws IOException if a problem occurred reading the stream.
1566 	 */
getInputStreamAsCharArray(InputStream stream, int length, String encoding)1567 	public static char[] getInputStreamAsCharArray(InputStream stream, int length, String encoding) throws IOException {
1568 		Charset charset = null;
1569 		try {
1570 			charset = Charset.forName(encoding);
1571 		} catch (IllegalCharsetNameException e) {
1572 			System.err.println("Illegal charset name : " + encoding); //$NON-NLS-1$
1573 			return null;
1574 		} catch (UnsupportedCharsetException e) {
1575 			System.err.println("Unsupported charset : " + encoding); //$NON-NLS-1$
1576 			return null;
1577 		}
1578 		return getInputStreamAsCharArray(stream, length, charset);
1579 	}
1580 
1581 	/**
1582 	 * Returns the given input stream's contents as a character array. If a
1583 	 * length is specified (i.e. if length != -1), this represents the number of
1584 	 * bytes in the stream. Note the specified stream is not closed in this
1585 	 * method
1586 	 *
1587 	 * @param stream the stream to get convert to the char array
1588 	 * @param length the length of the input stream, or -1 if unknown
1589 	 * @param charset the encoding to use when reading the stream
1590 	 * @return the given input stream's contents as a character array.
1591 	 * @throws IOException if a problem occurred reading the stream.
1592 	 */
getInputStreamAsCharArray(InputStream stream, int length, Charset charset)1593 	public static char[] getInputStreamAsCharArray(InputStream stream, int length, Charset charset) throws IOException {
1594 		CharsetDecoder charsetDecoder = charset.newDecoder();
1595 
1596 		charsetDecoder.onMalformedInput(CodingErrorAction.REPLACE).onUnmappableCharacter(CodingErrorAction.REPLACE);
1597 		byte[] contents = getInputStreamAsByteArray(stream, length);
1598 		ByteBuffer byteBuffer = ByteBuffer.allocate(contents.length);
1599 		byteBuffer.put(contents);
1600 		byteBuffer.flip();
1601 		CharBuffer charBuffer = charsetDecoder.decode(byteBuffer);
1602 		charBuffer.compact(); // ensure pay-load starting at 0
1603 		char[] array = charBuffer.array();
1604 		int lengthToBe = charBuffer.position();
1605 		if (array.length > lengthToBe) {
1606 			System.arraycopy(array, 0, (array = new char[lengthToBe]), 0, lengthToBe);
1607 		}
1608 		return array;
1609 	}
1610 
1611 	/**
1612 	 * Tries to find the 'MANIFEST.MF' file with in the given project in the
1613 	 * 'META-INF folder'.
1614 	 *
1615 	 * @param currentProject
1616 	 * @return a handle to the manifest file or <code>null</code> if not found
1617 	 */
getManifestFile(IProject currentProject)1618 	public static IResource getManifestFile(IProject currentProject) {
1619 		return currentProject.findMember("META-INF/MANIFEST.MF"); //$NON-NLS-1$
1620 	}
1621 
1622 	/**
1623 	 * Returns if the given {@link IMarker} is representing an
1624 	 * {@link org.eclipse.pde.api.tools.internal.provisional.problems.IApiProblem}
1625 	 * or not
1626 	 *
1627 	 * @param marker the marker to check
1628 	 * @return true if the marker is for an
1629 	 *         {@link org.eclipse.pde.api.tools.internal.provisional.problems.IApiProblem}
1630 	 *         false otherwise
1631 	 * @throws CoreException
1632 	 */
isApiProblemMarker(IMarker marker)1633 	public static boolean isApiProblemMarker(IMarker marker) {
1634 		return marker.getAttribute(IApiMarkerConstants.API_MARKER_ATTR_ID, -1) > 0;
1635 	}
1636 
1637 	/**
1638 	 * Returns a reference type for the given fully qualified type name.
1639 	 *
1640 	 * @param fullyQualifiedName type name
1641 	 * @return reference type
1642 	 */
getType(String fullyQualifiedName)1643 	public static IReferenceTypeDescriptor getType(String fullyQualifiedName) {
1644 		int index = fullyQualifiedName.lastIndexOf('.');
1645 		String pkg = index == -1 ? DEFAULT_PACKAGE_NAME : fullyQualifiedName.substring(0, index);
1646 		String type = index == -1 ? fullyQualifiedName : fullyQualifiedName.substring(index + 1);
1647 		return Factory.packageDescriptor(pkg).getType(type);
1648 	}
1649 
1650 	/**
1651 	 * Returns if the given project is API enabled
1652 	 *
1653 	 * @param project the given project
1654 	 * @return true if the project is API enabled, false otherwise
1655 	 */
isApiProject(IProject project)1656 	public static boolean isApiProject(IProject project) {
1657 		try {
1658 			return project.hasNature(ApiPlugin.NATURE_ID);
1659 		} catch (CoreException e) {
1660 			return false;
1661 		}
1662 	}
1663 
1664 	/**
1665 	 * Returns if the given project is a java project
1666 	 *
1667 	 * @param project the given project
1668 	 * @return <code>true</code> if the project is a java project,
1669 	 *         <code>false</code> otherwise
1670 	 */
isJavaProject(IProject project)1671 	public static boolean isJavaProject(IProject project) {
1672 		try {
1673 			return project.hasNature(JavaCore.NATURE_ID);
1674 		} catch (CoreException e) {
1675 			return false;
1676 		}
1677 	}
1678 
1679 	/**
1680 	 * Returns if the given project is API enabled
1681 	 *
1682 	 * @param project the given project
1683 	 * @return <code>true</code> if the project is API enabled,
1684 	 *         <code>false</code> otherwise
1685 	 */
isApiProject(IJavaProject project)1686 	public static boolean isApiProject(IJavaProject project) {
1687 		if (project != null) {
1688 			return isApiProject(project.getProject());
1689 		}
1690 		return false;
1691 	}
1692 
1693 	/**
1694 	 * Returns if the given {@link IApiComponent} is a valid
1695 	 * {@link IApiComponent}
1696 	 *
1697 	 * @param apiComponent the given component
1698 	 * @return true if the given {@link IApiComponent} is valid, false otherwise
1699 	 */
isApiToolsComponent(IApiComponent apiComponent)1700 	public static boolean isApiToolsComponent(IApiComponent apiComponent) {
1701 		File file = new File(apiComponent.getLocation());
1702 		if (file.exists()) {
1703 			if (file.isDirectory()) {
1704 				// directory binary bundle
1705 				File apiDescription = new File(file, IApiCoreConstants.API_DESCRIPTION_XML_NAME);
1706 				return apiDescription.exists();
1707 			}
1708 			ZipFile zipFile = null;
1709 			try {
1710 				zipFile = new ZipFile(file);
1711 				return zipFile.getEntry(IApiCoreConstants.API_DESCRIPTION_XML_NAME) != null;
1712 			} catch (IOException e) {
1713 				// ignore
1714 			} finally {
1715 				try {
1716 					if (zipFile != null) {
1717 						zipFile.close();
1718 					}
1719 				} catch (IOException e) {
1720 					// ignore
1721 				}
1722 			}
1723 		}
1724 		return false;
1725 	}
1726 
1727 	/**
1728 	 * Returns if the given {@link IApiComponent} has java package/s
1729 	 * {@link IApiComponent}
1730 	 *
1731 	 * @param apiComponent the given component
1732 	 * @return true if the given {@link IApiComponent} has java package/s, false
1733 	 *         otherwise
1734 	 */
hasJavaPackages(IApiComponent apiComponent)1735 	public static boolean hasJavaPackages(IApiComponent apiComponent) {
1736 		try {
1737 			String[] packageNames = apiComponent.getPackageNames();
1738 			return packageNames.length > 0;
1739 
1740 		} catch (CoreException e) {
1741 			ApiPlugin.log("Failed to check java packages for " + apiComponent.getName(), e); //$NON-NLS-1$
1742 		}
1743 		return true;
1744 	}
1745 
1746 	/**
1747 	 * Returns if the specified file name is an archive name. A name is
1748 	 * considered to be an archive name if it ends with either '.zip' or '.jar'
1749 	 *
1750 	 * @param fileName
1751 	 * @return true if the file name is an archive name false otherwise
1752 	 */
isArchive(String fileName)1753 	public static boolean isArchive(String fileName) {
1754 		return isZipJarFile(fileName) || isTGZFile(fileName);
1755 	}
1756 
1757 	/**
1758 	 * Returns if the given file name represents a 'standard' archive, where the
1759 	 * name has an extension of *.zip or *.jar
1760 	 *
1761 	 * @param fileName
1762 	 * @return true if the given file name is that of a 'standard' archive,
1763 	 *         false otherwise
1764 	 */
isZipJarFile(String fileName)1765 	public static boolean isZipJarFile(String fileName) {
1766 		String normalizedFileName = fileName.toLowerCase();
1767 		return normalizedFileName.endsWith(DOT_ZIP) || normalizedFileName.endsWith(DOT_JAR);
1768 	}
1769 
1770 	/**
1771 	 * Returns if the given file name represents a G-zip file name, where the
1772 	 * name has an extension of *.tar.gz or *.tgz
1773 	 *
1774 	 * @param fileName
1775 	 * @return true if the given file name is that of a G-zip archive, false
1776 	 *         otherwise
1777 	 */
isTGZFile(String fileName)1778 	public static boolean isTGZFile(String fileName) {
1779 		String normalizedFileName = fileName.toLowerCase();
1780 		return normalizedFileName.endsWith(DOT_TAR_GZ) || normalizedFileName.endsWith(DOT_TGZ);
1781 	}
1782 
1783 	/**
1784 	 * Returns if the flags are for a class
1785 	 *
1786 	 * @param accessFlags the given access flags
1787 	 * @return
1788 	 */
isClass(int accessFlags)1789 	public static boolean isClass(int accessFlags) {
1790 		return (accessFlags & (Opcodes.ACC_ENUM | Opcodes.ACC_ANNOTATION | Opcodes.ACC_INTERFACE)) == 0;
1791 	}
1792 
1793 	/**
1794 	 * Returns if the specified file name is for a class file. A name is
1795 	 * considered to be a class file if it ends in '.class'
1796 	 *
1797 	 * @param fileName
1798 	 * @return true if the name is for a class file false otherwise
1799 	 */
isClassFile(String fileName)1800 	public static boolean isClassFile(String fileName) {
1801 		return fileName.toLowerCase().endsWith(DOT_CLASS_SUFFIX);
1802 	}
1803 
isDefault(int accessFlags)1804 	public static boolean isDefault(int accessFlags) {
1805 		// none of the private, protected or public bit is set
1806 		return (accessFlags & (Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED | Opcodes.ACC_PUBLIC)) == 0;
1807 	}
1808 
isDifferentVersion(String versionToBeChecked, String referenceVersion)1809 	public static final boolean isDifferentVersion(String versionToBeChecked, String referenceVersion) {
1810 		SinceTagVersion sinceTagVersion1 = null;
1811 		SinceTagVersion sinceTagVersion2 = null;
1812 		try {
1813 			sinceTagVersion1 = new SinceTagVersion(versionToBeChecked);
1814 			sinceTagVersion2 = new SinceTagVersion(referenceVersion);
1815 		} catch (IllegalArgumentException e) {
1816 			// We cannot compare the two versions as their format is unknown
1817 			// TODO (olivier) should we report these as malformed tags?
1818 			return false;
1819 		}
1820 		Version version1 = sinceTagVersion1.getVersion();
1821 		Version version2 = sinceTagVersion2.getVersion();
1822 		if (version1.getMajor() != version2.getMajor()) {
1823 			return true;
1824 		}
1825 		if (version1.getMinor() != version2.getMinor()) {
1826 			return true;
1827 		}
1828 		if (version1.getMicro() != version2.getMicro()) {
1829 			return true;
1830 		}
1831 		return false;
1832 	}
1833 
1834 	/**
1835 	 * Returns if the specified file name is for a java source file. A name is
1836 	 * considered to be a java source file if it ends in '.java'
1837 	 *
1838 	 * @param fileName
1839 	 * @return true if the name is for a java source file, false otherwise
1840 	 */
isJavaFileName(String fileName)1841 	public static boolean isJavaFileName(String fileName) {
1842 		return fileName.toLowerCase().endsWith(DOT_JAVA_SUFFIX);
1843 	}
1844 
1845 	/**
1846 	 * Returns if the given name is {@link java.lang.Object}
1847 	 *
1848 	 * @param name
1849 	 * @return true if the name is java.lang.Object, false otherwise
1850 	 */
isJavaLangObject(String name)1851 	public static boolean isJavaLangObject(String name) {
1852 		return name != null && name.equals(JAVA_LANG_OBJECT);
1853 	}
1854 
1855 	/**
1856 	 * Return if the name is {@link java.lang.RuntimeException}
1857 	 *
1858 	 * @param name
1859 	 * @return true if the name is java.lang.RuntimeException, false otherwise
1860 	 */
isJavaLangRuntimeException(String name)1861 	public static boolean isJavaLangRuntimeException(String name) {
1862 		return name != null && name.equals(JAVA_LANG_RUNTIMEEXCEPTION);
1863 	}
1864 
isVisible(int modifiers)1865 	public static boolean isVisible(int modifiers) {
1866 		return Flags.isProtected(modifiers) || Flags.isPublic(modifiers);
1867 	}
1868 
isBinaryProject(IProject project)1869 	public static boolean isBinaryProject(IProject project) {
1870 		return org.eclipse.pde.internal.core.WorkspaceModelManager.isBinaryProject(project);
1871 	}
1872 
1873 	/**
1874 	 * Returns a new XML document.
1875 	 *
1876 	 * @return document
1877 	 * @throws CoreException if unable to create a new document
1878 	 */
newDocument()1879 	public static Document newDocument() throws CoreException {
1880 		DocumentBuilderFactory dfactory = DocumentBuilderFactory.newInstance();
1881 		DocumentBuilder docBuilder = null;
1882 		try {
1883 			docBuilder = dfactory.newDocumentBuilder();
1884 		} catch (ParserConfigurationException e) {
1885 			abort("Unable to create new XML document.", e); //$NON-NLS-1$
1886 		}
1887 		Document doc = docBuilder.newDocument();
1888 		return doc;
1889 	}
1890 
1891 	/**
1892 	 * Parses the given string representing an XML document, returning its root
1893 	 * element.
1894 	 *
1895 	 * @param document XML document as a string
1896 	 * @return the document's root element
1897 	 * @throws CoreException if unable to parse the document
1898 	 */
parseDocument(String document)1899 	public static Element parseDocument(String document) throws CoreException {
1900 		Element root = null;
1901 		InputStream stream = null;
1902 		try {
1903 			DocumentBuilder parser = DocumentBuilderFactory.newInstance().newDocumentBuilder();
1904 			parser.setErrorHandler(new DefaultHandler());
1905 			stream = new ByteArrayInputStream(document.getBytes(StandardCharsets.UTF_8));
1906 			root = parser.parse(stream).getDocumentElement();
1907 		} catch (ParserConfigurationException | FactoryConfigurationError | SAXException | IOException e) {
1908 			abort("Unable to parse XML document.", e); //$NON-NLS-1$
1909 		} finally {
1910 			try {
1911 				if (stream != null) {
1912 					stream.close();
1913 				}
1914 			} catch (IOException e) {
1915 				abort("Unable to parse XML document.", e); //$NON-NLS-1$
1916 			}
1917 		}
1918 		return root;
1919 	}
1920 
1921 	/**
1922 	 * Save the given contents into the given file. The file parent folder must
1923 	 * exist.
1924 	 *
1925 	 * @param file the given file target
1926 	 * @param contents the given contents
1927 	 * @throws IOException if an IOException occurs while saving the file
1928 	 */
saveFile(File file, String contents)1929 	public static void saveFile(File file, String contents) throws IOException {
1930 		BufferedWriter writer = null;
1931 		try {
1932 			writer = new BufferedWriter(new FileWriter(file));
1933 			writer.write(contents);
1934 			writer.flush();
1935 		} finally {
1936 			if (writer != null) {
1937 				try {
1938 					writer.close();
1939 				} catch (IOException e) {
1940 					// ignore
1941 				}
1942 			}
1943 		}
1944 	}
1945 
1946 	/**
1947 	 * Returns the contents of the given file as a string, or <code>null</code>
1948 	 *
1949 	 * @param file the file to get the contents for
1950 	 * @return the contents of the file as a {@link String} or <code>null</code>
1951 	 */
getFileContentAsString(File file)1952 	public static String getFileContentAsString(File file) {
1953 		String contents = null;
1954 		FileInputStream stream = null;
1955 		try {
1956 			stream = new FileInputStream(file);
1957 			char[] array = getInputStreamAsCharArray(stream, -1, StandardCharsets.UTF_8);
1958 			contents = new String(array);
1959 		} catch (IOException ioe) {
1960 			ApiPlugin.log(ioe);
1961 		} finally {
1962 			if (stream != null) {
1963 				try {
1964 					stream.close();
1965 				} catch (IOException e) {
1966 					// ignore
1967 				}
1968 			}
1969 		}
1970 		return contents;
1971 	}
1972 
1973 	/**
1974 	 * Returns the given string as an {@link InputStream}. It is up to the
1975 	 * caller to close the new stream.
1976 	 *
1977 	 * @param string the string to convert
1978 	 * @return the {@link InputStream} for the given string
1979 	 */
getInputStreamFromString(String string)1980 	public static InputStream getInputStreamFromString(String string) {
1981 		return new ByteArrayInputStream(string.getBytes(StandardCharsets.UTF_8));
1982 	}
1983 
1984 	/**
1985 	 * Serializes the given XML document into a UTF-8 string.
1986 	 *
1987 	 * @param document XML document to serialize
1988 	 * @return a string representing the given document
1989 	 * @throws CoreException if unable to serialize the document
1990 	 */
serializeDocument(Document document)1991 	public static String serializeDocument(Document document) throws CoreException {
1992 		try {
1993 			ByteArrayOutputStream s = new ByteArrayOutputStream();
1994 			TransformerFactory factory = TransformerFactory.newInstance();
1995 			Transformer transformer = factory.newTransformer();
1996 			transformer.setOutputProperty(OutputKeys.METHOD, "xml"); //$NON-NLS-1$
1997 			transformer.setOutputProperty(OutputKeys.INDENT, "yes"); //$NON-NLS-1$
1998 			transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4"); //$NON-NLS-1$ //$NON-NLS-2$
1999 			DOMSource source = new DOMSource(document);
2000 			StreamResult outputTarget = new StreamResult(s);
2001 			transformer.transform(source, outputTarget);
2002 			return s.toString(IApiCoreConstants.UTF_8);
2003 		} catch (TransformerException | IOException e) {
2004 			abort("Unable to serialize XML document.", e); //$NON-NLS-1$
2005 		}
2006 		return null;
2007 	}
2008 
2009 	/**
2010 	 * Unzip the contents of the given zip in the given directory (create it if
2011 	 * it doesn't exist)
2012 	 *
2013 	 * @throws IOException, CoreException
2014 	 */
unzip(String zipPath, String destDirPath)2015 	public static void unzip(String zipPath, String destDirPath) throws IOException, CoreException {
2016 		byte[] buf = new byte[8192];
2017 		File destDir = new File(destDirPath);
2018 		try (InputStream zipIn = new FileInputStream(zipPath); ZipInputStream zis = new ZipInputStream(new BufferedInputStream(zipIn));) {
2019 			ZipEntry zEntry;
2020 			while ((zEntry = zis.getNextEntry()) != null) {
2021 				// if it is empty directory, create it
2022 				if (zEntry.isDirectory()) {
2023 					new File(destDir, zEntry.getName()).mkdirs();
2024 					continue;
2025 				}
2026 				// if it is a file, extract it
2027 				String filePath = zEntry.getName();
2028 				int lastSeparator = filePath.lastIndexOf("/"); //$NON-NLS-1$
2029 				String fileDir = ""; //$NON-NLS-1$
2030 				if (lastSeparator >= 0) {
2031 					fileDir = filePath.substring(0, lastSeparator);
2032 				}
2033 				// create directory for a file
2034 				new File(destDir, fileDir).mkdirs();
2035 				// write file
2036 				String destDirCanonicalPath = destDir.getCanonicalPath();
2037 				File outFile = new File(destDir, filePath);
2038 				String outFileCanonicalPath = outFile.getCanonicalPath();
2039 				if (!outFileCanonicalPath.startsWith(destDirCanonicalPath + File.separator)) {
2040 					throw new CoreException(new Status(IStatus.ERROR, ApiPlugin.PLUGIN_ID, MessageFormat.format("Entry is outside of the target dir: : {0}", filePath), null)); //$NON-NLS-1$
2041 				}
2042 				try (BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(outFile))) {
2043 					int n = 0;
2044 					while ((n = zis.read(buf)) >= 0) {
2045 						outputStream.write(buf, 0, n);
2046 					}
2047 				}
2048 			}
2049 		}
2050 	}
2051 
2052 	/**
2053 	 * Unzip the contents of the given zip in the given directory (create it if
2054 	 * it doesn't exist)
2055 	 */
guntar(String zipPath, String destDirPath)2056 	public static void guntar(String zipPath, String destDirPath) throws TarException, IOException {
2057 		TarFile tarFile = new TarFile(zipPath);
2058 		Enumeration<?> entries = tarFile.entries();
2059 		byte[] buf = new byte[8192];
2060 		for (; entries.hasMoreElements();) {
2061 			TarEntry zEntry;
2062 			while ((zEntry = (TarEntry) entries.nextElement()) != null) {
2063 				// if it is empty directory, create it
2064 				if (zEntry.getFileType() == TarEntry.DIRECTORY) {
2065 					new File(destDirPath, zEntry.getName()).mkdirs();
2066 					continue;
2067 				}
2068 				// if it is a file, extract it
2069 				String filePath = zEntry.getName();
2070 				int lastSeparator = filePath.lastIndexOf("/"); //$NON-NLS-1$
2071 				String fileDir = ""; //$NON-NLS-1$
2072 				if (lastSeparator >= 0) {
2073 					fileDir = filePath.substring(0, lastSeparator);
2074 				}
2075 				// create directory for a file
2076 				new File(destDirPath, fileDir).mkdirs();
2077 				// write file
2078 				File outFile = new File(destDirPath, filePath);
2079 				BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(outFile));
2080 				int n = 0;
2081 				InputStream inputStream = tarFile.getInputStream(zEntry);
2082 				BufferedInputStream stream = new BufferedInputStream(inputStream);
2083 				while ((n = stream.read(buf)) >= 0) {
2084 					outputStream.write(buf, 0, n);
2085 				}
2086 				outputStream.close();
2087 				stream.close();
2088 			}
2089 		}
2090 	}
2091 
2092 	/**
2093 	 * Gets the .ee file supplied to run tests based on system property.
2094 	 *
2095 	 * @return
2096 	 */
getEEDescriptionFile()2097 	public static File getEEDescriptionFile() {
2098 		// generate a fake 1.6 ee file
2099 		File fakeEEFile = null;
2100 		PrintWriter writer = null;
2101 		try {
2102 			fakeEEFile = createTempFile("eefile", ".ee"); //$NON-NLS-1$ //$NON-NLS-2$
2103 			writer = new PrintWriter(new BufferedWriter(new FileWriter(fakeEEFile)));
2104 			writer.print("-Djava.home="); //$NON-NLS-1$
2105 			writer.println(System.getProperty("java.home")); //$NON-NLS-1$
2106 			writer.print("-Dee.bootclasspath="); //$NON-NLS-1$
2107 			writer.println(getJavaClassLibsAsString());
2108 			writer.println("-Dee.language.level=1.6"); //$NON-NLS-1$
2109 			writer.println("-Dee.class.library.level=JavaSE-1.6"); //$NON-NLS-1$
2110 			writer.flush();
2111 		} catch (IOException e) {
2112 			// ignore
2113 		} finally {
2114 			if (writer != null) {
2115 				writer.close();
2116 			}
2117 		}
2118 		return fakeEEFile;
2119 	}
2120 
2121 	/**
2122 	 * Creates a new file in the users' <code>temp</code> directory
2123 	 *
2124 	 * @param prefix
2125 	 * @param suffix
2126 	 * @return a new temp file
2127 	 * @throws IOException
2128 	 * @since 1.1
2129 	 */
createTempFile(String prefix, String suffix)2130 	public static File createTempFile(String prefix, String suffix) throws IOException {
2131 		File file = File.createTempFile(prefix, suffix);
2132 		file.deleteOnExit();
2133 		FileManager.getManager().recordTempFileRoot(file.getCanonicalPath());
2134 		return file;
2135 	}
2136 
2137 	/**
2138 	 * @return a string representation of all of the libraries from the bootpath
2139 	 *         of the current default system VM.
2140 	 */
getJavaClassLibsAsString()2141 	public static String getJavaClassLibsAsString() {
2142 		String[] libs = Util.getJavaClassLibs();
2143 		StringBuilder buffer = new StringBuilder();
2144 		for (int i = 0, max = libs.length; i < max; i++) {
2145 			if (i > 0) {
2146 				buffer.append(File.pathSeparatorChar);
2147 			}
2148 			buffer.append(libs[i]);
2149 		}
2150 		return String.valueOf(buffer);
2151 	}
2152 
2153 	/**
2154 	 * @return an array of the library names from the bootpath of the current
2155 	 *         default system VM
2156 	 */
getJavaClassLibs()2157 	public static String[] getJavaClassLibs() {
2158 		// check bootclasspath properties for Sun, JRockit and Harmony VMs
2159 		String bootclasspathProperty = System.getProperty("sun.boot.class.path"); //$NON-NLS-1$
2160 		if ((bootclasspathProperty == null) || (bootclasspathProperty.length() == 0)) {
2161 			// IBM J9 VMs
2162 			bootclasspathProperty = System.getProperty("vm.boot.class.path"); //$NON-NLS-1$
2163 			if ((bootclasspathProperty == null) || (bootclasspathProperty.length() == 0)) {
2164 				// Harmony using IBM VME
2165 				bootclasspathProperty = System.getProperty("org.apache.harmony.boot.class.path"); //$NON-NLS-1$
2166 			}
2167 		}
2168 		String[] jars = null;
2169 		if ((bootclasspathProperty != null) && (bootclasspathProperty.length() != 0)) {
2170 			StringTokenizer tokenizer = new StringTokenizer(bootclasspathProperty, File.pathSeparator);
2171 			final int size = tokenizer.countTokens();
2172 			jars = new String[size];
2173 			int i = 0;
2174 			while (tokenizer.hasMoreTokens()) {
2175 				final String fileName = toNativePath(tokenizer.nextToken());
2176 				if (new File(fileName).exists()) {
2177 					jars[i] = fileName;
2178 					i++;
2179 				}
2180 			}
2181 			if (size != i) {
2182 				// resize
2183 				System.arraycopy(jars, 0, (jars = new String[i]), 0, i);
2184 			}
2185 		} else {
2186 			String jreDir = System.getProperty("java.home"); //$NON-NLS-1$
2187 			final String osName = System.getProperty("os.name"); //$NON-NLS-1$
2188 			if (jreDir == null) {
2189 				return new String[] {};
2190 			}
2191 			if (osName.startsWith("Mac")) { //$NON-NLS-1$
2192 				return new String[] { toNativePath(jreDir + "/../Classes/classes.jar") //$NON-NLS-1$
2193 				};
2194 			}
2195 			final String vmName = System.getProperty("java.vm.name"); //$NON-NLS-1$
2196 			if ("J9".equals(vmName)) { //$NON-NLS-1$
2197 				return new String[] { toNativePath(jreDir + "/lib/jclMax/classes.zip") //$NON-NLS-1$
2198 				};
2199 			}
2200 			String[] jarsNames = null;
2201 			ArrayList<String> paths = new ArrayList<>();
2202 			if ("DRLVM".equals(vmName)) { //$NON-NLS-1$
2203 				FilenameFilter jarFilter = (dir, name) -> name.endsWith(DOT_JAR) & !name.endsWith("-src.jar"); //$NON-NLS-1$
2204 				jarsNames = new File(jreDir + "/lib/boot/").list(jarFilter); //$NON-NLS-1$
2205 				addJarEntries(jreDir + "/lib/boot/", jarsNames, paths); //$NON-NLS-1$
2206 			} else {
2207 				jarsNames = new String[] { "/lib/vm.jar", //$NON-NLS-1$
2208 						"/lib/rt.jar", //$NON-NLS-1$
2209 						"/lib/core.jar", //$NON-NLS-1$
2210 						"/lib/security.jar", //$NON-NLS-1$
2211 						"/lib/xml.jar", //$NON-NLS-1$
2212 						"/lib/graphics.jar" //$NON-NLS-1$
2213 				};
2214 				addJarEntries(jreDir, jarsNames, paths);
2215 			}
2216 			jars = new String[paths.size()];
2217 			paths.toArray(jars);
2218 		}
2219 		return jars;
2220 	}
2221 
2222 	/**
2223 	 * Makes the given path a path using native path separators as returned by
2224 	 * File.getPath() and trimming any extra slash.
2225 	 */
toNativePath(String path)2226 	public static String toNativePath(String path) {
2227 		String nativePath = path.replace('\\', File.separatorChar).replace('/', File.separatorChar);
2228 		return nativePath.endsWith("/") || nativePath.endsWith("\\") ? //$NON-NLS-1$ //$NON-NLS-2$
2229 		nativePath.substring(0, nativePath.length() - 1) : nativePath;
2230 	}
2231 
addJarEntries(String jreDir, String[] jarNames, ArrayList<String> paths)2232 	private static void addJarEntries(String jreDir, String[] jarNames, ArrayList<String> paths) {
2233 		for (String jarName : jarNames) {
2234 			final String currentName = jreDir + jarName;
2235 			File f = new File(currentName);
2236 			if (f.exists()) {
2237 				paths.add(toNativePath(currentName));
2238 			}
2239 		}
2240 	}
2241 
2242 	/**
2243 	 * Delete a file or directory and insure that the file is no longer present
2244 	 * on file system. In case of directory, delete all the hierarchy
2245 	 * underneath.
2246 	 *
2247 	 * @param file The file or directory to delete
2248 	 * @return true iff the file was really delete, false otherwise
2249 	 */
delete(File file)2250 	public static boolean delete(File file) {
2251 		if (!file.exists()) {
2252 			return true;
2253 		}
2254 		// flush all directory content
2255 		if (file.isDirectory()) {
2256 			flushDirectoryContent(file);
2257 		}
2258 		// remove file
2259 		file.delete();
2260 		if (isFileDeleted(file)) {
2261 			return true;
2262 		}
2263 		return waitUntilFileDeleted(file);
2264 	}
2265 
flushDirectoryContent(File dir)2266 	public static void flushDirectoryContent(File dir) {
2267 		File[] files = dir.listFiles();
2268 		if (files == null) {
2269 			return;
2270 		}
2271 		for (File file : files) {
2272 			delete(file);
2273 		}
2274 	}
2275 
2276 	/**
2277 	 * Wait until the file is _really_ deleted on file system.
2278 	 *
2279 	 * @param file Deleted file
2280 	 * @return true if the file was finally deleted, false otherwise
2281 	 */
waitUntilFileDeleted(File file)2282 	private static boolean waitUntilFileDeleted(File file) {
2283 		int count = 0;
2284 		int delay = 10; // ms
2285 		int maxRetry = DELETE_MAX_WAIT / delay;
2286 		int time = 0;
2287 		while (count < maxRetry) {
2288 			try {
2289 				count++;
2290 				Thread.sleep(delay);
2291 				time += delay;
2292 				if (time > DELETE_MAX_TIME) {
2293 					DELETE_MAX_TIME = time;
2294 				}
2295 				if (DELETE_DEBUG) {
2296 					System.out.print('.');
2297 				}
2298 				if (file.exists()) {
2299 					if (file.delete()) {
2300 						// SUCCESS
2301 						return true;
2302 					}
2303 				}
2304 				if (isFileDeleted(file)) {
2305 					// SUCCESS
2306 					return true;
2307 				}
2308 				// Increment waiting delay exponentially
2309 				if (count >= 10 && delay <= 100) {
2310 					count = 1;
2311 					delay *= 10;
2312 					maxRetry = DELETE_MAX_WAIT / delay;
2313 					if ((DELETE_MAX_WAIT % delay) != 0) {
2314 						maxRetry++;
2315 					}
2316 				}
2317 			} catch (InterruptedException ie) {
2318 				break; // end loop
2319 			}
2320 		}
2321 		System.err.println();
2322 		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$
2323 		System.err.println();
2324 		return false;
2325 	}
2326 
2327 	/**
2328 	 * Returns whether a file is really deleted or not. Does not only rely on
2329 	 * {@link File#exists()} method but also look if it's not in its parent
2330 	 * children {@link #getParentChildFile(File)}.
2331 	 *
2332 	 * @param file The file to test if deleted
2333 	 * @return true if the file does not exist and was not found in its parent
2334 	 *         children.
2335 	 */
isFileDeleted(File file)2336 	public static boolean isFileDeleted(File file) {
2337 		return !file.exists() && getParentChildFile(file) == null;
2338 	}
2339 
2340 	/**
2341 	 * Returns the parent's child file matching the given file or null if not
2342 	 * found.
2343 	 *
2344 	 * @param file The searched file in parent
2345 	 * @return The parent's child matching the given file or null if not found.
2346 	 */
getParentChildFile(File file)2347 	private static File getParentChildFile(File file) {
2348 		File parent = file.getParentFile();
2349 		if (parent == null || !parent.exists()) {
2350 			return null;
2351 		}
2352 		File[] files = parent.listFiles();
2353 		if (files == null) {
2354 			return null;
2355 		}
2356 		int length = files == null ? 0 : files.length;
2357 		if (length > 0) {
2358 			for (int i = 0; i < length; i++) {
2359 				if (files[i] == file) {
2360 					return files[i];
2361 				} else if (files[i].equals(file)) {
2362 					return files[i];
2363 				} else if (files[i].getPath().equals(file.getPath())) {
2364 					return files[i];
2365 				}
2366 			}
2367 		}
2368 		return null;
2369 	}
2370 
2371 	/**
2372 	 * Turns the given array of strings into a {@link HashSet}
2373 	 *
2374 	 * @param values
2375 	 * @return a new {@link HashSet} of the string array
2376 	 */
convertAsSet(String[] values)2377 	public static Set<String> convertAsSet(String[] values) {
2378 		Set<String> set = new HashSet<>();
2379 		if (values != null && values.length != 0) {
2380 			Collections.addAll(set, values);
2381 		}
2382 		return set;
2383 	}
2384 
2385 	/**
2386 	 * Returns an identifier for the given API component including its version
2387 	 * identifier (component id + '(' + major + . + minor + . + micro + ')' )
2388 	 *
2389 	 * @param component API component
2390 	 * @return API component + version identifier
2391 	 */
getDeltaComponentVersionsId(IApiComponent component)2392 	public static String getDeltaComponentVersionsId(IApiComponent component) {
2393 		StringBuilder buffer = new StringBuilder(component.getSymbolicName());
2394 		String version = component.getVersion();
2395 		// remove the qualifier part
2396 		if (version != null) {
2397 			buffer.append(Util.VERSION_SEPARATOR);
2398 			try {
2399 				Version version2 = new Version(version);
2400 				buffer.append(version2.getMajor()).append('.').append(version2.getMinor()).append('.').append(version2.getMicro());
2401 			} catch (IllegalArgumentException e) {
2402 				// the version string doesn't follow the Eclipse pattern
2403 				// we keep the version as is
2404 				buffer.append(version);
2405 			}
2406 			buffer.append(')');
2407 		}
2408 		return String.valueOf(buffer);
2409 	}
2410 
2411 	/**
2412 	 * Returns an identifier for the given API component including its version
2413 	 * identifier (component id + _ + major + _ + minor + _ + micro)
2414 	 *
2415 	 * @param component API component
2416 	 * @return API component + version identifier
2417 	 */
getComponentVersionsId(IApiComponent component)2418 	public static String getComponentVersionsId(IApiComponent component) {
2419 		StringBuilder buffer = new StringBuilder(component.getSymbolicName());
2420 		String version = component.getVersion();
2421 		// remove the qualifier part
2422 		if (version != null) {
2423 			buffer.append('_');
2424 			try {
2425 				Version version2 = new Version(version);
2426 				buffer.append(version2.getMajor()).append('.').append(version2.getMinor()).append('.').append(version2.getMicro());
2427 			} catch (IllegalArgumentException e) {
2428 				// the version string doesn't follow the Eclipse pattern
2429 				// we keep the version as is
2430 				buffer.append(version);
2431 			}
2432 		}
2433 		return String.valueOf(buffer);
2434 	}
2435 
getDescriptorName(IApiType descriptor)2436 	public static String getDescriptorName(IApiType descriptor) {
2437 		String typeName = descriptor.getName();
2438 		int index = typeName.lastIndexOf('$');
2439 		if (index != -1) {
2440 			return typeName.replace('$', '.');
2441 		}
2442 		return typeName;
2443 	}
2444 
getDeltaArgumentString(IDelta delta)2445 	public static String getDeltaArgumentString(IDelta delta) {
2446 		String[] arguments = delta.getArguments();
2447 		switch (delta.getFlags()) {
2448 			case IDelta.TYPE_MEMBER:
2449 			case IDelta.TYPE:
2450 				return arguments[0];
2451 			case IDelta.METHOD:
2452 			case IDelta.DEFAULT_METHOD:
2453 			case IDelta.SUPER_INTERFACE_DEFAULT_METHOD:
2454 			case IDelta.CONSTRUCTOR:
2455 			case IDelta.ENUM_CONSTANT:
2456 			case IDelta.METHOD_WITH_DEFAULT_VALUE:
2457 			case IDelta.METHOD_WITHOUT_DEFAULT_VALUE:
2458 			case IDelta.FIELD:
2459 				return arguments[1];
2460 			case IDelta.INCREASE_ACCESS:
2461 				switch (delta.getElementType()) {
2462 					case IDelta.FIELD_ELEMENT_TYPE:
2463 					case IDelta.METHOD_ELEMENT_TYPE:
2464 					case IDelta.CONSTRUCTOR_ELEMENT_TYPE:
2465 						return arguments[1];
2466 					default:
2467 						return arguments[0];
2468 				}
2469 			default:
2470 				break;
2471 		}
2472 		return EMPTY_STRING;
2473 	}
2474 
2475 	/**
2476 	 * Returns the string representation of the {@link IApiElement} type
2477 	 *
2478 	 * @param type
2479 	 * @return the string of the {@link IApiElement} type
2480 	 */
getApiElementType(int type)2481 	public static String getApiElementType(int type) {
2482 		switch (type) {
2483 			case IApiElement.API_TYPE_CONTAINER:
2484 				return "API_TYPE_CONTAINER"; //$NON-NLS-1$
2485 			case IApiElement.API_TYPE_ROOT:
2486 				return "API_TYPE_ROOT"; //$NON-NLS-1$
2487 			case IApiElement.BASELINE:
2488 				return "BASELINE"; //$NON-NLS-1$
2489 			case IApiElement.COMPONENT:
2490 				return "COMPONENT"; //$NON-NLS-1$
2491 			case IApiElement.FIELD:
2492 				return "FIELD"; //$NON-NLS-1$
2493 			case IApiElement.METHOD:
2494 				return "METHOD"; //$NON-NLS-1$
2495 			case IApiElement.TYPE:
2496 				return "TYPE"; //$NON-NLS-1$
2497 			default:
2498 				return "UNKNOWN"; //$NON-NLS-1$
2499 		}
2500 	}
2501 
isConstructor(String referenceMemberName)2502 	public static boolean isConstructor(String referenceMemberName) {
2503 		return Arrays.equals(ConstantPool.Init, referenceMemberName.toCharArray());
2504 	}
2505 
isManifest(IPath path)2506 	public static boolean isManifest(IPath path) {
2507 		return MANIFEST_PROJECT_RELATIVE_PATH.equals(path);
2508 	}
2509 
touchCorrespondingResource(IProject project, IResource resource, String typeName)2510 	public static void touchCorrespondingResource(IProject project, IResource resource, String typeName) {
2511 		if (typeName != null && typeName != FilterStore.GLOBAL) {
2512 			if (Util.isManifest(resource.getProjectRelativePath())) {
2513 				try {
2514 					IJavaProject javaProject = JavaCore.create(project);
2515 					IType findType = javaProject.findType(typeName);
2516 					IType typeInProject = Util.getTypeInSameJavaProject(findType, typeName, javaProject);
2517 					if (typeInProject != null) {
2518 						findType = typeInProject;
2519 					}
2520 					if (findType != null) {
2521 						ICompilationUnit compilationUnit = findType.getCompilationUnit();
2522 						if (compilationUnit != null) {
2523 							IResource cuResource = compilationUnit.getResource();
2524 							if (cuResource != null) {
2525 								cuResource.touch(null);
2526 							}
2527 						}
2528 					}
2529 				} catch (CoreException e) {
2530 					ApiPlugin.log(e);
2531 				}
2532 			} else {
2533 				try {
2534 					resource.touch(null);
2535 				} catch (CoreException e) {
2536 					ApiPlugin.log(e);
2537 				}
2538 			}
2539 		}
2540 	}
2541 
getTypeNameFromMarker(IMarker marker)2542 	public static String getTypeNameFromMarker(IMarker marker) {
2543 		return marker.getAttribute(IApiMarkerConstants.MARKER_ATTR_PROBLEM_TYPE_NAME, null);
2544 	}
2545 
getReexportedComponents(IApiComponent component)2546 	public static IApiComponent[] getReexportedComponents(IApiComponent component) {
2547 		try {
2548 			IRequiredComponentDescription[] requiredComponents = component.getRequiredComponents();
2549 			int length = requiredComponents.length;
2550 			if (length != 0) {
2551 				List<IApiComponent> reexportedComponents = null;
2552 				IApiBaseline baseline = component.getBaseline();
2553 				for (int i = 0; i < length; i++) {
2554 					IRequiredComponentDescription description = requiredComponents[i];
2555 					if (description.isExported()) {
2556 						String id = description.getId();
2557 						IApiComponent reexportedComponent = baseline.getApiComponent(id);
2558 						if (reexportedComponent != null) {
2559 							if (reexportedComponents == null) {
2560 								reexportedComponents = new ArrayList<>();
2561 							}
2562 							reexportedComponents.add(reexportedComponent);
2563 						}
2564 					}
2565 				}
2566 				if (reexportedComponents == null || reexportedComponents.isEmpty()) {
2567 					return null;
2568 				}
2569 				return reexportedComponents.toArray(new IApiComponent[reexportedComponents.size()]);
2570 			}
2571 		} catch (CoreException e) {
2572 			ApiPlugin.log(e);
2573 		}
2574 		return null;
2575 	}
2576 
2577 	/**
2578 	 * Returns the {@link IResource} to create markers on when building. If the
2579 	 * {@link IType} is <code>null</code> or the type cannot be located (does
2580 	 * not exist) than the MANIFEST.MF will be returned. <code>null</code> can
2581 	 * be returned in the case that the project does not have a manifest file.
2582 	 *
2583 	 * @param project the project to look in for the {@link IResource}
2584 	 * @param type the type we are looking for the resource for, or
2585 	 *            <code>null</code>
2586 	 * @return the {@link IResource} associated with the given {@link IType} or
2587 	 *         the MANIFEST.MF file, or <code>null</code> if the project does
2588 	 *         not have a manifest
2589 	 */
getResource(IProject project, IType type)2590 	public static IResource getResource(IProject project, IType type) {
2591 		try {
2592 			if (type != null) {
2593 				ICompilationUnit unit = type.getCompilationUnit();
2594 				if (unit != null) {
2595 					IResource resource = unit.getCorrespondingResource();
2596 					if (resource != null && resource.exists()) {
2597 						return resource;
2598 					}
2599 				}
2600 			}
2601 		} catch (JavaModelException e) {
2602 			ApiPlugin.log(e);
2603 		}
2604 		return getManifestFile(project);
2605 	}
2606 
2607 	/**
2608 	 * Default comparator that orders {@link IApiComponent} by their ID
2609 	 */
2610 	public static final Comparator<Object> componentsorter = (o1, o2) -> {
2611 		if (o1 instanceof IApiComponent && o2 instanceof IApiComponent) {
2612 			return ((IApiComponent) o1).getSymbolicName().compareTo(((IApiComponent) o2).getSymbolicName());
2613 		}
2614 		if (o1 instanceof SkippedComponent && o2 instanceof SkippedComponent) {
2615 			return ((SkippedComponent) o1).getComponentId().compareTo(((SkippedComponent) o2).getComponentId());
2616 		}
2617 		if (o1 instanceof String && o2 instanceof String) {
2618 			return ((String) o1).compareTo((String) o2);
2619 		}
2620 		return -1;
2621 	};
2622 
2623 	/**
2624 	 * Initializes the exclude set with regex support. The API baseline is used
2625 	 * to determine which bundles should be added to the list when processing
2626 	 * regex expressions.
2627 	 *
2628 	 * @param location
2629 	 * @param baseline
2630 	 * @return the list of bundles to be excluded
2631 	 * @throws CoreException if the location does not describe a includes file
2632 	 *             or an IOException occurs
2633 	 */
initializeRegexFilterList(String location, IApiBaseline baseline, boolean debug)2634 	public static FilteredElements initializeRegexFilterList(String location, IApiBaseline baseline, boolean debug) throws CoreException {
2635 		FilteredElements excludedElements = new FilteredElements();
2636 		if (location != null) {
2637 			File file = new File(location);
2638 			char[] contents = null;
2639 			try (InputStream stream = new BufferedInputStream(new FileInputStream(file));) {
2640 				contents = getInputStreamAsCharArray(stream, -1, StandardCharsets.ISO_8859_1);
2641 			} catch (FileNotFoundException e) {
2642 				abort(NLS.bind(UtilMessages.Util_couldNotFindFilterFile, location), e);
2643 			} catch (IOException e) {
2644 				abort(NLS.bind(UtilMessages.Util_problemWithFilterFile, location), e);
2645 			}
2646 			if (contents != null) {
2647 				try(LineNumberReader reader = new LineNumberReader(new StringReader(new String(contents)));){
2648 					String line = null;
2649 					while ((line = reader.readLine()) != null) {
2650 						line = line.trim();
2651 						if (line.startsWith("#") || line.length() == 0) { //$NON-NLS-1$
2652 							continue;
2653 						}
2654 						if (line.startsWith(REGULAR_EXPRESSION_START)) {
2655 							if (baseline != null) {
2656 								Util.collectRegexIds(line, excludedElements, baseline.getApiComponents(), debug);
2657 							}
2658 						} else {
2659 							excludedElements.addExactMatch(line);
2660 						}
2661 					}
2662 				} catch (IOException e) {
2663 					abort(NLS.bind(UtilMessages.Util_problemWithFilterFile, location), e);
2664 				}
2665 			}
2666 		}
2667 		return excludedElements;
2668 	}
2669 
2670 	/**
2671 	 * Collects the set of component ids that match a given regex in the exclude
2672 	 * file
2673 	 *
2674 	 * @param line
2675 	 * @param list
2676 	 * @param components
2677 	 */
collectRegexIds(String line, FilteredElements excludedElements, IApiComponent[] components, boolean debug)2678 	public static void collectRegexIds(String line, FilteredElements excludedElements, IApiComponent[] components, boolean debug) throws CoreException {
2679 		if (line.startsWith(REGULAR_EXPRESSION_START)) {
2680 			String componentname = line;
2681 			// regular expression
2682 			componentname = componentname.substring(2);
2683 			Pattern pattern = null;
2684 			try {
2685 				if (debug) {
2686 					System.out.println("Pattern to match : " + componentname); //$NON-NLS-1$
2687 				}
2688 				pattern = Pattern.compile(componentname);
2689 				String componentid = null;
2690 				for (IApiComponent component : components) {
2691 					componentid = component.getSymbolicName();
2692 					if (pattern.matcher(componentid).matches()) {
2693 						if (debug) {
2694 							System.out.println(componentid + " matched the pattern " + componentname); //$NON-NLS-1$
2695 						}
2696 						excludedElements.addPartialMatch(componentid);
2697 					} else if (debug) {
2698 						System.out.println(componentid + " didn't match the pattern " + componentname); //$NON-NLS-1$
2699 					}
2700 				}
2701 			} catch (PatternSyntaxException e) {
2702 				abort(NLS.bind(UtilMessages.comparison_invalidRegularExpression, componentname), e);
2703 			}
2704 		}
2705 	}
2706 
2707 	/**
2708 	 * Default comparator that orders {@link File}s by their name
2709 	 */
2710 	public static final Comparator<Object> filesorter = (o1, o2) -> {
2711 		if (o1 instanceof File && o2 instanceof File) {
2712 			return ((File) o1).getName().compareTo(((File) o2).getName());
2713 		}
2714 		return 0;
2715 	};
2716 
2717 	/**
2718 	 * Returns true if the given {@link IApiType} is API or not, where API is
2719 	 * defined as having API visibility in an API description and having either
2720 	 * the public of protected Java flag set
2721 	 *
2722 	 * @param visibility
2723 	 * @param typeDescriptor
2724 	 * @return true if the given type is API, false otherwise
2725 	 */
isAPI(int visibility, IApiType typeDescriptor)2726 	public static boolean isAPI(int visibility, IApiType typeDescriptor) {
2727 		int access = typeDescriptor.getModifiers();
2728 		return VisibilityModifiers.isAPI(visibility) && (Flags.isPublic(access) || Flags.isProtected(access));
2729 	}
2730 
2731 	/**
2732 	 * Simple method to walk an array and call <code>toString()</code> on each
2733 	 * of the entries. Does not descend into sub-collections.
2734 	 *
2735 	 * @param array the array
2736 	 * @return the comma-separated string representation of the the array
2737 	 * @since 1.0.3
2738 	 */
deepToString(Object[] array)2739 	public static String deepToString(Object[] array) {
2740 		StringBuilder buffer = new StringBuilder();
2741 		for (int i = 0; i < array.length; i++) {
2742 			buffer.append(array[i].toString());
2743 			if (i < array.length - 1) {
2744 				buffer.append(',');
2745 			}
2746 		}
2747 		return buffer.toString();
2748 	}
2749 
getSinceVersionTagPrefererenceKey(int id)2750 	public static String getSinceVersionTagPrefererenceKey(int id) {
2751 		int problemCategory = ApiProblemFactory.getProblemCategory(id);
2752 		int problemKind = ApiProblemFactory.getProblemKind(id);
2753 		switch (problemCategory) {
2754 		case IApiProblem.CATEGORY_SINCETAGS: {
2755 				switch (problemKind) {
2756 					case IApiProblem.SINCE_TAG_INVALID:
2757 						return IApiProblemTypes.INVALID_SINCE_TAG_VERSION;
2758 					case IApiProblem.SINCE_TAG_MALFORMED:
2759 						return IApiProblemTypes.MALFORMED_SINCE_TAG;
2760 					case IApiProblem.SINCE_TAG_MISSING:
2761 						return IApiProblemTypes.MISSING_SINCE_TAG;
2762 					default:
2763 						break;
2764 				}
2765 			}
2766 				break;
2767 			case IApiProblem.CATEGORY_VERSION: {
2768 				switch (problemKind) {
2769 					case IApiProblem.MAJOR_VERSION_CHANGE_NO_BREAKAGE:
2770 						return IApiProblemTypes.INCOMPATIBLE_API_COMPONENT_VERSION_REPORT_MAJOR_WITHOUT_BREAKING_CHANGE;
2771 
2772 					case IApiProblem.MINOR_VERSION_CHANGE_NO_NEW_API:
2773 					case IApiProblem.MICRO_VERSION_CHANGE_UNNECESSARILY:
2774 					case IApiProblem.MINOR_VERSION_CHANGE_UNNECESSARILY:
2775 						return IApiProblemTypes.INCOMPATIBLE_API_COMPONENT_VERSION_REPORT_MINOR_WITHOUT_API_CHANGE;
2776 
2777 					case IApiProblem.MINOR_VERSION_CHANGE_EXECUTION_ENV_CHANGED:
2778 						return IApiProblemTypes.CHANGED_EXECUTION_ENV;
2779 
2780 					default:
2781 						return IApiProblemTypes.INCOMPATIBLE_API_COMPONENT_VERSION;
2782 
2783 				}
2784 			}
2785 			default:
2786 				break;
2787 
2788 
2789 		}
2790 
2791 		return null;
2792 	}
2793 
getUsagePrefererenceKey(int id)2794 	public static String getUsagePrefererenceKey(int id) {
2795 		int problemKind = ApiProblemFactory.getProblemKind(id);
2796 		int problemFlag = ApiProblemFactory.getProblemFlags(id);
2797 
2798 		switch (problemKind) {
2799 			case IApiProblem.ILLEGAL_IMPLEMENT:
2800 				return IApiProblemTypes.ILLEGAL_IMPLEMENT;
2801 
2802 			case IApiProblem.ILLEGAL_EXTEND:
2803 				return IApiProblemTypes.ILLEGAL_EXTEND;
2804 			case IApiProblem.ILLEGAL_INSTANTIATE:
2805 				return IApiProblemTypes.ILLEGAL_INSTANTIATE;
2806 			case IApiProblem.ILLEGAL_OVERRIDE:
2807 				return IApiProblemTypes.ILLEGAL_OVERRIDE;
2808 			case IApiProblem.ILLEGAL_REFERENCE:
2809 				return IApiProblemTypes.ILLEGAL_REFERENCE;
2810 
2811 				case IApiProblem.API_LEAK: {
2812 				switch (problemFlag) {
2813 					case IApiProblem.LEAK_BY_EXTENDING_NO_EXTEND_CLASS_TYPE:
2814 					case IApiProblem.LEAK_BY_EXTENDING_NO_EXTEND_INTERFACE_TYPE:
2815 					case IApiProblem.LEAK_EXTENDS:
2816 						return IApiProblemTypes.LEAK_EXTEND;
2817 					case IApiProblem.LEAK_IMPLEMENTS:
2818 						return IApiProblemTypes.LEAK_IMPLEMENT;
2819 					case IApiProblem.LEAK_FIELD:
2820 						return IApiProblemTypes.LEAK_FIELD_DECL;
2821 					case IApiProblem.LEAK_RETURN_TYPE:
2822 						return IApiProblemTypes.LEAK_METHOD_RETURN_TYPE;
2823 					case IApiProblem.LEAK_CONSTRUCTOR_PARAMETER:
2824 					case IApiProblem.LEAK_METHOD_PARAMETER:
2825 						return IApiProblemTypes.LEAK_METHOD_PARAM;
2826 
2827 						default:
2828 							break;
2829 					}
2830 					break;
2831 				}
2832 			case IApiProblem.UNSUPPORTED_TAG_USE:
2833 				return IApiProblemTypes.INVALID_JAVADOC_TAG;
2834 
2835 			case IApiProblem.UNSUPPORTED_ANNOTATION_USE:
2836 				return IApiProblemTypes.INVALID_ANNOTATION;
2837 
2838 			case IApiProblem.INVALID_REFERENCE_IN_SYSTEM_LIBRARIES:
2839 				return IApiProblemTypes.INVALID_REFERENCE_IN_SYSTEM_LIBRARIES;
2840 
2841 			case IApiProblem.MISSING_EE_DESCRIPTIONS:
2842 				return IApiProblemTypes.MISSING_EE_DESCRIPTIONS;
2843 
2844 			// this is usage??
2845 			case IApiProblem.UNUSED_PROBLEM_FILTERS:
2846 				return IApiProblemTypes.UNUSED_PROBLEM_FILTERS;
2847 
2848 
2849 				default:
2850 					break;
2851 			}
2852 		return null;
2853 
2854 		}
2855 
getComponentResolutionKey(int id)2856 	public static String getComponentResolutionKey(int id) {
2857 		int problemKind = ApiProblemFactory.getProblemKind(id);
2858 
2859 
2860 		switch (problemKind) {
2861 			case IApiProblem.API_COMPONENT_RESOLUTION:
2862 				return IApiProblemTypes.REPORT_RESOLUTION_ERRORS_API_COMPONENT;
2863 			case IApiProblem.UNUSED_PROBLEM_FILTERS:
2864 				return IApiProblemTypes.UNUSED_PROBLEM_FILTERS;
2865 			default:
2866 				break;
2867 				}
2868 		return null;
2869 	}
2870 
getAPIUseScanKey(int id)2871 	public static String getAPIUseScanKey(int id) {
2872 
2873 		int problemKind = ApiProblemFactory.getProblemKind(id);
2874 
2875 		switch (problemKind) {
2876 			case IApiProblem.API_USE_SCAN_TYPE_PROBLEM:
2877 				return IApiProblemTypes.API_USE_SCAN_TYPE_SEVERITY;
2878 			case IApiProblem.API_USE_SCAN_METHOD_PROBLEM:
2879 				return IApiProblemTypes.API_USE_SCAN_METHOD_SEVERITY;
2880 			case IApiProblem.API_USE_SCAN_FIELD_PROBLEM:
2881 				return IApiProblemTypes.API_USE_SCAN_FIELD_SEVERITY;
2882 			default:
2883 				break;
2884 		}
2885 		return null;
2886 
2887 	}
2888 
getAPIToolPreferenceKey(int id)2889 	public static String getAPIToolPreferenceKey(int id) {
2890 		String key = null;
2891 		int category = ApiProblemFactory.getProblemCategory(id);
2892 
2893 		if (category == IApiProblem.CATEGORY_USAGE) {
2894 			key = Util.getUsagePrefererenceKey(id);
2895 
2896 		}
2897 		if (category == IApiProblem.CATEGORY_COMPATIBILITY) {
2898 
2899 			key = Util.getDeltaPrefererenceKey(ApiProblemFactory.getProblemElementKind(id), ApiProblemFactory.getProblemKind(id), ApiProblemFactory.getProblemFlags(id));
2900 		}
2901 		if (category == IApiProblem.CATEGORY_SINCETAGS || category == IApiProblem.CATEGORY_VERSION) {
2902 			key = Util.getSinceVersionTagPrefererenceKey(id);
2903 
2904 		}
2905 		if (category == IApiProblem.CATEGORY_API_COMPONENT_RESOLUTION) {
2906 			key = Util.getComponentResolutionKey(id);
2907 
2908 		}
2909 		if (category == IApiProblem.CATEGORY_API_USE_SCAN_PROBLEM) {
2910 			key = Util.getAPIUseScanKey(id);
2911 
2912 		}
2913 		return key;
2914 	}
2915 
getAPIToolPreferenceTab(int id)2916 	public static int getAPIToolPreferenceTab(int id) {
2917 		int category = ApiProblemFactory.getProblemCategory(id);
2918 		int tab = -1;
2919 		if (category == IApiProblem.CATEGORY_USAGE) {
2920 			tab = 0;
2921 			int problemKind = ApiProblemFactory.getProblemKind(id);
2922 			switch (problemKind) {
2923 				case IApiProblem.UNUSED_PROBLEM_FILTERS:
2924 					return 3;
2925 				default:
2926 					break;
2927 			}
2928 		}
2929 		if (category == IApiProblem.CATEGORY_COMPATIBILITY) {
2930 			tab = 1;
2931 		}
2932 		if (category == IApiProblem.CATEGORY_SINCETAGS || category == IApiProblem.CATEGORY_VERSION) {
2933 			tab = 2;
2934 		}
2935 		if (category == IApiProblem.CATEGORY_API_COMPONENT_RESOLUTION) {
2936 			tab = 3;
2937 		}
2938 		if (category == IApiProblem.CATEGORY_API_USE_SCAN_PROBLEM) {
2939 			tab = 4;
2940 		}
2941 		return tab;
2942 	}
2943 
2944 	/**
2945 	 * Tries to find SourceType of name typeName in the project. It returns null
2946 	 * if it cannot find SourceType of name typeName
2947 	 *
2948 	 * @param javaProject
2949 	 * @param typeName
2950 	 * @return
2951 	 */
2952 
findSourceTypeinJavaProject(IJavaProject javaProject, String typeName)2953 	public static IType findSourceTypeinJavaProject(IJavaProject javaProject, String typeName) {
2954 		IType type = null;
2955 		try {
2956 			String pkgName = typeName.substring(0, typeName.lastIndexOf('.'));
2957 			if (javaProject instanceof JavaProject) {
2958 				JavaProject jp = (JavaProject) javaProject;
2959 				NameLookup newNameLookup = null;
2960 				try {
2961 					newNameLookup = jp.newNameLookup(DefaultWorkingCopyOwner.PRIMARY);
2962 
2963 				} catch (JavaModelException e) {
2964 					ApiPlugin.log(e);
2965 
2966 				}
2967 				IPackageFragment[] findPackageFragment = newNameLookup.findPackageFragments(pkgName, false);
2968 				for (IPackageFragment iJavaElement : findPackageFragment) {
2969 					type = newNameLookup.findType(typeName.substring(typeName.lastIndexOf('.') + 1, typeName.length()), iJavaElement, false, NameLookup.ACCEPT_ALL);
2970 					if (type instanceof SourceType) {
2971 						break;
2972 
2973 					}
2974 				}
2975 			}
2976 		} catch (Exception e) {
2977 			// return null
2978 		}
2979 		return type;
2980 	}
2981 
2982 
2983 }
2984