1 /*******************************************************************************
2  * Copyright (c) 2000, 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  *     Stephan Herrmann <stephan@cs.tu-berlin.de> - Contributions for
14  *     						Bug 320618 - inconsistent initialization of classpath container backed by external class folder
15  *     						Bug 346010 - [model] strange initialization dependency in OptionTests
16  *******************************************************************************/
17 package org.eclipse.jdt.internal.core;
18 
19 import java.io.*;
20 import java.net.URI;
21 import java.nio.charset.StandardCharsets;
22 import java.nio.file.FileVisitResult;
23 import java.nio.file.attribute.BasicFileAttributes;
24 import java.util.ArrayList;
25 import java.util.Arrays;
26 import java.util.Collection;
27 import java.util.Collections;
28 import java.util.HashMap;
29 import java.util.HashSet;
30 import java.util.Hashtable;
31 import java.util.Iterator;
32 import java.util.LinkedHashSet;
33 import java.util.List;
34 import java.util.Map;
35 import java.util.Optional;
36 import java.util.Set;
37 import java.util.function.Function;
38 import java.util.jar.Manifest;
39 import java.util.stream.Collectors;
40 
41 import javax.xml.parsers.DocumentBuilder;
42 import javax.xml.parsers.DocumentBuilderFactory;
43 import javax.xml.parsers.ParserConfigurationException;
44 import org.eclipse.core.resources.ICommand;
45 import org.eclipse.core.resources.IFile;
46 import org.eclipse.core.resources.IFolder;
47 import org.eclipse.core.resources.IMarker;
48 import org.eclipse.core.resources.IProject;
49 import org.eclipse.core.resources.IProjectDescription;
50 import org.eclipse.core.resources.IProjectNature;
51 import org.eclipse.core.resources.IResource;
52 import org.eclipse.core.resources.IWorkspace;
53 import org.eclipse.core.resources.IWorkspaceRoot;
54 import org.eclipse.core.resources.ProjectScope;
55 import org.eclipse.core.resources.ResourcesPlugin;
56 import org.eclipse.core.runtime.CoreException;
57 import org.eclipse.core.runtime.IPath;
58 import org.eclipse.core.runtime.IProgressMonitor;
59 import org.eclipse.core.runtime.IStatus;
60 import org.eclipse.core.runtime.Path;
61 import org.eclipse.core.runtime.Platform;
62 import org.eclipse.core.runtime.QualifiedName;
63 import org.eclipse.core.runtime.Status;
64 import org.eclipse.core.runtime.content.IContentDescription;
65 import org.eclipse.core.runtime.preferences.IEclipsePreferences;
66 import org.eclipse.core.runtime.preferences.IScopeContext;
67 import org.eclipse.jdt.core.IClasspathAttribute;
68 import org.eclipse.jdt.core.IClasspathContainer;
69 import org.eclipse.jdt.core.IClasspathEntry;
70 import org.eclipse.jdt.core.ICompilationUnit;
71 import org.eclipse.jdt.core.IJavaElement;
72 import org.eclipse.jdt.core.IJavaModelMarker;
73 import org.eclipse.jdt.core.IJavaModelStatus;
74 import org.eclipse.jdt.core.IJavaModelStatusConstants;
75 import org.eclipse.jdt.core.IJavaProject;
76 import org.eclipse.jdt.core.IModuleDescription;
77 import org.eclipse.jdt.core.IPackageFragment;
78 import org.eclipse.jdt.core.IPackageFragmentRoot;
79 import org.eclipse.jdt.core.IRegion;
80 import org.eclipse.jdt.core.IType;
81 import org.eclipse.jdt.core.ITypeHierarchy;
82 import org.eclipse.jdt.core.JavaCore;
83 import org.eclipse.jdt.core.JavaModelException;
84 import org.eclipse.jdt.core.WorkingCopyOwner;
85 import org.eclipse.jdt.core.compiler.CategorizedProblem;
86 import org.eclipse.jdt.core.compiler.CharOperation;
87 import org.eclipse.jdt.core.eval.IEvaluationContext;
88 import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
89 import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader;
90 import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException;
91 import org.eclipse.jdt.internal.compiler.env.AutomaticModuleNaming;
92 import org.eclipse.jdt.internal.compiler.env.IModule;
93 import org.eclipse.jdt.internal.compiler.env.IModule.IModuleReference;
94 import org.eclipse.jdt.internal.compiler.env.IModule.IPackageExport;
95 import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
96 import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
97 import org.eclipse.jdt.internal.compiler.util.JRTUtil;
98 import org.eclipse.jdt.internal.compiler.util.ObjectVector;
99 import org.eclipse.jdt.internal.compiler.util.SuffixConstants;
100 import org.eclipse.jdt.internal.core.JavaModelManager.PerProjectInfo;
101 import org.eclipse.jdt.internal.core.JavaProjectElementInfo.ProjectCache;
102 import org.eclipse.jdt.internal.core.builder.JavaBuilder;
103 import org.eclipse.jdt.internal.core.eval.EvaluationContextWrapper;
104 import org.eclipse.jdt.internal.core.util.JavaElementFinder;
105 import org.eclipse.jdt.internal.core.util.MementoTokenizer;
106 import org.eclipse.jdt.internal.core.util.Messages;
107 import org.eclipse.jdt.internal.core.util.Util;
108 import org.eclipse.jdt.internal.eval.EvaluationContext;
109 import org.osgi.service.prefs.BackingStoreException;
110 import org.w3c.dom.Element;
111 import org.w3c.dom.Node;
112 import org.w3c.dom.NodeList;
113 import org.xml.sax.InputSource;
114 import org.xml.sax.SAXException;
115 
116 /**
117  * Handle for a Java Project.
118  *
119  * <p>A Java Project internally maintains a devpath that corresponds
120  * to the project's classpath. The classpath may include source folders
121  * from the current project; jars in the current project, other projects,
122  * and the local file system; and binary folders (output location) of other
123  * projects. The Java Model presents source elements corresponding to output
124  * .class files in other projects, and thus uses the devpath rather than
125  * the classpath (which is really a compilation path). The devpath mimics
126  * the classpath, except has source folder entries in place of output
127  * locations in external projects.
128  *
129  * <p>Each JavaProject has a NameLookup facility that locates elements
130  * on by name, based on the devpath.
131  *
132  * @see IJavaProject
133  */
134 @SuppressWarnings({ "rawtypes", "unchecked" })
135 public class JavaProject
136 	extends Openable
137 	implements IJavaProject, IProjectNature, SuffixConstants {
138 
139 	/**
140 	 * Name of file containing project classpath
141 	 */
142 	public static final String CLASSPATH_FILENAME = IJavaProject.CLASSPATH_FILE_NAME;
143 
144 	/**
145 	 * Value of the project's raw classpath if the .classpath file contains invalid entries.
146 	 */
147 	public static final IClasspathEntry[] INVALID_CLASSPATH = new IClasspathEntry[0];
148 
149 	/**
150 	 * Whether the underlying file system is case sensitive.
151 	 */
152 	protected static final boolean IS_CASE_SENSITIVE = !new File("Temp").equals(new File("temp")); //$NON-NLS-1$ //$NON-NLS-2$
153 
154 	/**
155 	 * An empty array of strings indicating that a project doesn't have any prerequesite projects.
156 	 */
157 	protected static final String[] NO_PREREQUISITES = CharOperation.NO_STRINGS;
158 
159 	/**
160 	 * Name of file containing custom project preferences
161 	 * @see <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=59258">bug 59258</a>
162 	 */
163 	private static final String PREF_FILENAME = ".jprefs";  //$NON-NLS-1$
164 
165 	/**
166 	 * Name of directory containing preferences file
167 	 */
168 	public static final String DEFAULT_PREFERENCES_DIRNAME = ".settings"; //$NON-NLS-1$
169 
170 	/**
171 	 * Extension for file containing custom project preferences
172 	 */
173 	public static final String JAVA_CORE_PREFS_FILE = JavaCore.PLUGIN_ID+".prefs"; //$NON-NLS-1$
174 
175 	/*
176 	 * Value of project's resolved classpath while it is being resolved
177 	 */
178 	private static final IClasspathEntry[] RESOLUTION_IN_PROGRESS = new IClasspathEntry[0];
179 
180 	/*
181 	 * For testing purpose only
182 	 */
183 	private static ArrayList CP_RESOLUTION_BP_LISTENERS;
184 	public static class ClasspathResolutionBreakpointListener {
breakpoint(int bp)185 		public void breakpoint(int bp) {
186 			// override in listener implementation
187 		}
188 	}
189 
190 	/**
191 	 * The platform project this <code>IJavaProject</code> is based on
192 	 */
193 	protected IProject project;
194 
195 	/**
196 	 * Preferences listeners
197 	 */
198 	private IEclipsePreferences.INodeChangeListener preferencesNodeListener;
199 	private IEclipsePreferences.IPreferenceChangeListener preferencesChangeListener;
200 
201 	/**
202 	 * Constructor needed for <code>IProject.getNature()</code> and <code>IProject.addNature()</code>.
203 	 *
204 	 * @see #setProject(IProject)
205 	 */
JavaProject()206 	public JavaProject() {
207 		super(null);
208 	}
209 
JavaProject(IProject project, JavaElement parent)210 	public JavaProject(IProject project, JavaElement parent) {
211 		super(parent);
212 		this.project = project;
213 	}
214 
215 	/*
216 	 * For testing purpose only
217 	 */
addCPResolutionBPListener(ClasspathResolutionBreakpointListener listener)218 	public static synchronized void addCPResolutionBPListener(ClasspathResolutionBreakpointListener listener) {
219 		if (CP_RESOLUTION_BP_LISTENERS == null)
220 			CP_RESOLUTION_BP_LISTENERS = new ArrayList();
221 		CP_RESOLUTION_BP_LISTENERS.add(listener);
222 	}
223 
224 	/*
225 	 * For testing purpose only
226 	 */
removeCPResolutionBPListener(ClasspathResolutionBreakpointListener listener)227 	public static synchronized void removeCPResolutionBPListener(ClasspathResolutionBreakpointListener listener) {
228 		if (CP_RESOLUTION_BP_LISTENERS == null)
229 			return;
230 		CP_RESOLUTION_BP_LISTENERS.remove(listener);
231 		if (CP_RESOLUTION_BP_LISTENERS.size() == 0)
232 			CP_RESOLUTION_BP_LISTENERS = null;
233 	}
234 
getBPListeners()235 	private static synchronized ClasspathResolutionBreakpointListener[] getBPListeners() {
236 		if (CP_RESOLUTION_BP_LISTENERS == null)
237 			return null;
238 		return (ClasspathResolutionBreakpointListener[]) CP_RESOLUTION_BP_LISTENERS.toArray(new ClasspathResolutionBreakpointListener[CP_RESOLUTION_BP_LISTENERS.size()]);
239 	}
240 
breakpoint(int bp, JavaProject project)241 	private static void breakpoint(int bp, JavaProject project) {
242 		ClasspathResolutionBreakpointListener[] listeners = getBPListeners();
243 		if (listeners == null)
244 			return;
245 		for (int j = 0, length = listeners.length; j < length; j++) {
246 			listeners[j].breakpoint(bp);
247 		}
248 	}
249 
areClasspathsEqual( IClasspathEntry[] firstClasspath, IClasspathEntry[] secondClasspath, IPath firstOutputLocation, IPath secondOutputLocation)250 	public static boolean areClasspathsEqual(
251 			IClasspathEntry[] firstClasspath, IClasspathEntry[] secondClasspath,
252 			IPath firstOutputLocation, IPath secondOutputLocation) {
253 		int length = firstClasspath.length;
254 		if (length != secondClasspath.length) return false;
255 		for (int i = 0; i < length; i++) {
256 			if (!firstClasspath[i].equals(secondClasspath[i]))
257 				return false;
258 		}
259 		if (firstOutputLocation == null)
260 			return secondOutputLocation == null;
261 		return firstOutputLocation.equals(secondOutputLocation);
262 	}
263 
264 	/**
265 	 * Compare current classpath with given one to see if any different.
266 	 * Note that the argument classpath contains its binary output.
267 	 * @param newClasspath IClasspathEntry[]
268 	 * @param newOutputLocation IPath
269 	 * @param otherClasspathWithOutput IClasspathEntry[]
270 	 * @return boolean
271 	 */
areClasspathsEqual(IClasspathEntry[] newClasspath, IPath newOutputLocation, IClasspathEntry[] otherClasspathWithOutput)272 	private static boolean areClasspathsEqual(IClasspathEntry[] newClasspath, IPath newOutputLocation, IClasspathEntry[] otherClasspathWithOutput) {
273 
274 		if (otherClasspathWithOutput == null || otherClasspathWithOutput.length == 0)
275 			return false;
276 
277 		int length = otherClasspathWithOutput.length;
278 		if (length != newClasspath.length + 1)
279 				// output is amongst file entries (last one)
280 				return false;
281 
282 
283 		// compare classpath entries
284 		for (int i = 0; i < length - 1; i++) {
285 			if (!otherClasspathWithOutput[i].equals(newClasspath[i]))
286 				return false;
287 		}
288 		// compare binary outputs
289 		IClasspathEntry output = otherClasspathWithOutput[length - 1];
290 		if (output.getContentKind() != ClasspathEntry.K_OUTPUT
291 				|| !output.getPath().equals(newOutputLocation))
292 			return false;
293 		return true;
294 	}
295 
areClasspathsEqual(IClasspathEntry[] first, IClasspathEntry[] second)296 	private static boolean areClasspathsEqual(IClasspathEntry[] first, IClasspathEntry[] second) {
297 		if (first != second){
298 		    if (first == null) return false;
299 			int length = first.length;
300 			if (second == null || second.length != length)
301 				return false;
302 			for (int i = 0; i < length; i++) {
303 				if (!first[i].equals(second[i]))
304 					return false;
305 			}
306 		}
307 		return true;
308 	}
309 
310 	/**
311 	 * Returns a canonicalized path from the given external path.
312 	 * Note that the return path contains the same number of segments
313 	 * and it contains a device only if the given path contained one.
314 	 * @param externalPath IPath
315 	 * @see java.io.File for the definition of a canonicalized path
316 	 * @return IPath
317 	 */
canonicalizedPath(IPath externalPath)318 	public static IPath canonicalizedPath(IPath externalPath) {
319 
320 		if (externalPath == null)
321 			return null;
322 
323 		if (IS_CASE_SENSITIVE) {
324 			return externalPath;
325 		}
326 
327 		// if not external path, return original path
328 		IWorkspace workspace = ResourcesPlugin.getWorkspace();
329 		if (workspace == null) return externalPath; // protection during shutdown (30487)
330 		if (workspace.getRoot().findMember(externalPath) != null) {
331 			return externalPath;
332 		}
333 
334 		IPath canonicalPath = null;
335 		try {
336 			canonicalPath =
337 				new Path(new File(externalPath.toOSString()).getCanonicalPath());
338 		} catch (IOException e) {
339 			// default to original path
340 			return externalPath;
341 		}
342 
343 		IPath result;
344 		int canonicalLength = canonicalPath.segmentCount();
345 		if (canonicalLength == 0) {
346 			// the java.io.File canonicalization failed
347 			return externalPath;
348 		} else if (externalPath.isAbsolute()) {
349 			result = canonicalPath;
350 		} else {
351 			// if path is relative, remove the first segments that were added by the java.io.File canonicalization
352 			// e.g. 'lib/classes.zip' was converted to 'd:/myfolder/lib/classes.zip'
353 			int externalLength = externalPath.segmentCount();
354 			if (canonicalLength >= externalLength) {
355 				result = canonicalPath.removeFirstSegments(canonicalLength - externalLength);
356 			} else {
357 				return externalPath;
358 			}
359 		}
360 
361 		// keep device only if it was specified (this is because File.getCanonicalPath() converts '/lib/classes.zip' to 'd:/lib/classes/zip')
362 		if (externalPath.getDevice() == null) {
363 			result = result.setDevice(null);
364 		}
365 		// keep trailing separator only if it was specified (this is because File.getCanonicalPath() converts 'd:/lib/classes/' to 'd:/lib/classes')
366 		if (externalPath.hasTrailingSeparator()) {
367 			result = result.addTrailingSeparator();
368 		}
369 		return result;
370 	}
371 
372 	/**
373 	 * Returns true if the given project is accessible and it has
374 	 * a java nature, otherwise false.
375 	 * @param project IProject
376 	 * @return boolean
377 	 */
hasJavaNature(IProject project)378 	public static boolean hasJavaNature(IProject project) {
379 		try {
380 			return project.hasNature(JavaCore.NATURE_ID);
381 		} catch (CoreException e) {
382 			if (ExternalJavaProject.EXTERNAL_PROJECT_NAME.equals(project.getName()))
383 				return true;
384 			// project does not exist or is not open
385 		}
386 		return false;
387 	}
388 
389 	/*
390 	 * Detect cycles in the classpath of the workspace's projects
391 	 * and create markers if necessary.
392 	 * @param preferredClasspaths Map
393 	 * @throws JavaModelException
394 	 */
validateCycles(Map preferredClasspaths)395 	public static void validateCycles(Map preferredClasspaths) throws JavaModelException {
396 		//long start = System.currentTimeMillis();
397 
398 		IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
399 		IProject[] rscProjects = workspaceRoot.getProjects();
400 		int length = rscProjects.length;
401 		JavaProject[] projects = new JavaProject[length];
402 
403 		LinkedHashSet<IPath> cycleParticipants = new LinkedHashSet<>();
404 		HashSet traversed = new HashSet();
405 
406 		// compute cycle participants
407 		List<IPath> prereqChain = new ArrayList<>();
408 		Map<IPath,List<CycleInfo>> cyclesPerProject = new HashMap<>();
409 		for (int i = 0; i < length; i++){
410 			if (hasJavaNature(rscProjects[i])) {
411 				JavaProject project = (projects[i] = (JavaProject)JavaCore.create(rscProjects[i]));
412 				if (!traversed.contains(project.getPath())){
413 					prereqChain.clear();
414 					project.updateCycleParticipants(prereqChain, cycleParticipants, cyclesPerProject, workspaceRoot, traversed, preferredClasspaths);
415 				}
416 			}
417 		}
418 		//System.out.println("updateAllCycleMarkers: " + (System.currentTimeMillis() - start) + " ms");
419 
420 		for (int i = 0; i < length; i++){
421 			JavaProject project = projects[i];
422 			if (project != null) {
423 				List<CycleInfo> cycles = cyclesPerProject.get(project.getPath());
424 				if (cycles != null) {
425 					StringBuilder cycleString = new StringBuilder();
426 					boolean first = true;
427 					for (CycleInfo cycleInfo : cycles) {
428 						if (!first) cycleString.append('\n');
429 						cycleString.append(cycleInfo.pathToCycleAsString());
430 						cycleString.append("->{"); //$NON-NLS-1$
431 						cycleString.append(cycleInfo.cycleAsString());
432 						cycleString.append('}');
433 						first = false;
434 					}
435 
436 					IMarker cycleMarker = project.getCycleMarker();
437 					String circularCPOption = project.getOption(JavaCore.CORE_CIRCULAR_CLASSPATH, true);
438 					int circularCPSeverity = JavaCore.ERROR.equals(circularCPOption) ? IMarker.SEVERITY_ERROR : IMarker.SEVERITY_WARNING;
439 					if (cycleMarker != null) {
440 						// update existing cycle marker if needed
441 						try {
442 							int existingSeverity = ((Integer)cycleMarker.getAttribute(IMarker.SEVERITY)).intValue();
443 							if (existingSeverity != circularCPSeverity) {
444 								cycleMarker.setAttribute(IMarker.SEVERITY, circularCPSeverity);
445 							}
446 							String existingMessage = cycleMarker.getAttribute(IMarker.MESSAGE, ""); //$NON-NLS-1$
447 							String newMessage = new JavaModelStatus(IJavaModelStatusConstants.CLASSPATH_CYCLE,
448 									project, cycleString.toString()).getMessage();
449 							newMessage = truncateIfNecessary(newMessage);
450 							if (!newMessage.equals(existingMessage)) {
451 								cycleMarker.setAttribute(IMarker.MESSAGE, newMessage);
452 							}
453 						} catch (CoreException e) {
454 							throw new JavaModelException(e);
455 						}
456 					} else {
457 						// create new marker
458 						project.createClasspathProblemMarker(
459 							new JavaModelStatus(IJavaModelStatusConstants.CLASSPATH_CYCLE, project, cycleString.toString()));
460 					}
461 				} else {
462 					project.flushClasspathProblemMarkers(true, false, false);
463 				}
464 			}
465 		}
466 	}
467 
truncateIfNecessary(String markerMessage)468 	static String truncateIfNecessary(String markerMessage) {
469 		// cf. org.eclipse.core.internal.resources.MarkerInfo.checkValidAttribute(Object)
470 		if (markerMessage.length() > 21000) {
471 			byte[] bytes = markerMessage.getBytes(StandardCharsets.UTF_8);
472 			if (bytes.length > 65535) {
473 				bytes = Arrays.copyOfRange(bytes, 0, 65500);
474 				markerMessage = new String(bytes, StandardCharsets.UTF_8)+"..."; //$NON-NLS-1$
475 			}
476 		}
477 		return markerMessage;
478 	}
479 
480 	/**
481 	 * Adds a builder to the build spec for the given project.
482 	 */
addToBuildSpec(String builderID)483 	protected void addToBuildSpec(String builderID) throws CoreException {
484 
485 		IProjectDescription description = this.project.getDescription();
486 		int javaCommandIndex = getJavaCommandIndex(description.getBuildSpec());
487 
488 		if (javaCommandIndex == -1) {
489 
490 			// Add a Java command to the build spec
491 			ICommand command = description.newCommand();
492 			command.setBuilderName(builderID);
493 			setJavaCommand(description, command);
494 		}
495 	}
496 	/**
497 	 * @see Openable
498 	 */
499 	@Override
buildStructure(OpenableElementInfo info, IProgressMonitor pm, Map newElements, IResource underlyingResource)500 	protected boolean buildStructure(OpenableElementInfo info, IProgressMonitor pm, Map newElements, IResource underlyingResource) throws JavaModelException {
501 		// cannot refresh cp markers on opening (emulate cp check on startup) since can create deadlocks (see bug 37274)
502 		IClasspathEntry[] resolvedClasspath = getResolvedClasspath();
503 
504 		// compute the pkg fragment roots
505 		IPackageFragmentRoot[] roots = computePackageFragmentRoots(resolvedClasspath, false, true, null /*no reverse map*/);
506 		info.setChildren(roots);
507 		IModuleDescription module = null;
508 		IModuleDescription current = null;
509 		for (IPackageFragmentRoot root : roots) {
510 			if (root.getKind() != IPackageFragmentRoot.K_SOURCE)
511 				continue;
512 			module = root.getModuleDescription();
513 			if (module != null) {
514 				if (current != null) {
515 					throw new JavaModelException(new Status(IStatus.ERROR, JavaCore.PLUGIN_ID,
516 							Messages.bind(Messages.classpath_duplicateEntryPath, TypeConstants.MODULE_INFO_FILE_NAME_STRING, getElementName())));
517 				}
518 				current = module;
519 				JavaModelManager.getModulePathManager().addEntry(module, this);
520 				//break; continue looking, there may be other roots containing module-info
521 				info.setModule(module);
522 			}
523 		}
524 		return true;
525 	}
526 
527 	@Override
close()528 	public void close() throws JavaModelException {
529 		if (JavaProject.hasJavaNature(this.project)) {
530 			// Get cached preferences if exist
531 			JavaModelManager.PerProjectInfo perProjectInfo = JavaModelManager.getJavaModelManager().getPerProjectInfo(this.project, false);
532 			if (perProjectInfo != null && perProjectInfo.preferences != null) {
533 				IEclipsePreferences eclipseParentPreferences = (IEclipsePreferences) perProjectInfo.preferences.parent();
534 				if (this.preferencesNodeListener != null) {
535 					eclipseParentPreferences.removeNodeChangeListener(this.preferencesNodeListener);
536 					this.preferencesNodeListener = null;
537 				}
538 				if (this.preferencesChangeListener != null) {
539 					perProjectInfo.preferences.removePreferenceChangeListener(this.preferencesChangeListener);
540 					this.preferencesChangeListener = null;
541 				}
542 			}
543 		}
544 		super.close();
545 	}
546 
547 	/**
548 	 * Internal computation of an expanded classpath. It will eliminate duplicates, and produce copies
549 	 * of exported or restricted classpath entries to avoid possible side-effects ever after.
550 	 * @param excludeTestCode
551 	 */
computeExpandedClasspath( ClasspathEntry referringEntry, HashMap<String, Boolean> rootIDs, ArrayList<ClasspathEntry> accumulatedEntries, boolean excludeTestCode)552 	private void computeExpandedClasspath(
553 		ClasspathEntry referringEntry,
554 		HashMap<String, Boolean> rootIDs,
555 		ArrayList<ClasspathEntry> accumulatedEntries, boolean excludeTestCode) throws JavaModelException {
556 
557 		IClasspathEntry[] resolvedClasspath = getResolvedClasspath();
558 
559 		IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
560 		boolean isInitialProject = referringEntry == null;
561 		for (int i = 0, length = resolvedClasspath.length; i < length; i++){
562 			ClasspathEntry entry = (ClasspathEntry) resolvedClasspath[i];
563 			if (excludeTestCode && entry.isTest()) {
564 				continue;
565 			}
566 			if (isInitialProject || entry.isExported()){
567 				String rootID = entry.rootID();
568 
569 				// recurse in project to get all its indirect exports (only consider exported entries from there on)
570 				if (entry.getEntryKind() == IClasspathEntry.CPE_PROJECT) {
571 					boolean nestedWithoutTestCode = excludeTestCode || entry.isWithoutTestCode();
572 					Boolean previousValue = rootIDs.get(rootID);
573 					ClasspathEntry combinedEntry;
574 					if (previousValue == Boolean.FALSE) {
575 						continue; // already handled including test code
576 					} else if (previousValue == Boolean.TRUE) {
577 						// project was already handled, but without test code.
578 						if (nestedWithoutTestCode) {
579 							continue;
580 						} else {
581 							// find the existing entry and update it.
582 							rootIDs.put(rootID, Boolean.FALSE);
583 							for (int j = 0; j < accumulatedEntries.size(); j++) {
584 								// it is unclear how oldEntry and combinedEntry could be merged.
585 								// main code compilation should remain untouched as far as possible,
586 								// so take all settings from oldEntry and just remove WITHOUT_TEST_CODE
587 								ClasspathEntry oldEntry = accumulatedEntries.get(j);
588 								if (oldEntry.rootID().equals(rootID)) {
589 									accumulatedEntries.set(j, oldEntry.withExtraAttributeRemoved(IClasspathAttribute.WITHOUT_TEST_CODE));
590 									break;
591 								}
592 							}
593 							// combine restrictions along the project chain
594 							combinedEntry = entry.combineWith(referringEntry);
595 						}
596 					} else {
597 						rootIDs.put(rootID, nestedWithoutTestCode);
598 						// combine restrictions along the project chain
599 						combinedEntry = entry.combineWith(referringEntry);
600 						accumulatedEntries.add(combinedEntry);
601 					}
602 					IResource member = workspaceRoot.findMember(entry.getPath());
603 					if (member != null && member.getType() == IResource.PROJECT){ // double check if bound to project (23977)
604 						IProject projRsc = (IProject) member;
605 						if (JavaProject.hasJavaNature(projRsc)) {
606 							JavaProject javaProject = (JavaProject) JavaCore.create(projRsc);
607 								javaProject.computeExpandedClasspath(
608 									combinedEntry,
609 									rootIDs,
610 									accumulatedEntries, nestedWithoutTestCode);
611 						}
612 					}
613 				} else {
614 					if (!rootIDs.containsKey(rootID)) {
615 						// combine restrictions along the project chain
616 						ClasspathEntry combinedEntry = entry.combineWith(referringEntry);
617 						accumulatedEntries.add(combinedEntry);
618 						rootIDs.put(rootID, excludeTestCode); // value actually doesn't matter for non-projects
619 					}
620 				}
621 			}
622 		}
623 	}
624 
625 	/**
626 	 * Computes the package fragment roots identified by the given entry.
627 	 * Only works with resolved entry
628 	 * @param resolvedEntry IClasspathEntry
629 	 * @return IPackageFragmentRoot[]
630 	 */
computePackageFragmentRoots(IClasspathEntry resolvedEntry)631 	public IPackageFragmentRoot[] computePackageFragmentRoots(IClasspathEntry resolvedEntry) {
632 		try {
633 			return
634 				computePackageFragmentRoots(
635 					new IClasspathEntry[]{ resolvedEntry },
636 					false, // don't retrieve exported roots
637 					true, // respect limit-modules
638 					null /* no reverse map */
639 				);
640 		} catch (JavaModelException e) {
641 			return new IPackageFragmentRoot[] {};
642 		}
643 	}
644 
computePackageFragmentRoots( IClasspathEntry resolvedEntry, ObjectVector accumulatedRoots, HashSet rootIDs, IClasspathEntry referringEntry, boolean retrieveExportedRoots, boolean filterModuleRoots, Map rootToResolvedEntries)645 	public void computePackageFragmentRoots(
646 			IClasspathEntry resolvedEntry,
647 			ObjectVector accumulatedRoots,
648 			HashSet rootIDs,
649 			IClasspathEntry referringEntry,
650 			boolean retrieveExportedRoots,
651 			boolean filterModuleRoots,
652 			Map rootToResolvedEntries) throws JavaModelException {
653 		computePackageFragmentRoots(resolvedEntry, accumulatedRoots, rootIDs, referringEntry, retrieveExportedRoots, filterModuleRoots,
654 				rootToResolvedEntries, false);
655 	}
656 
657 	/**
658 	 * Returns the package fragment roots identified by the given entry. In case it refers to
659 	 * a project, it will follow its classpath so as to find exported roots as well.
660 	 * Only works with resolved entry
661 	 * <p><strong>Note:</strong> this method is retained for the sole purpose of supporting
662 	 * old versions of Xtext [2.8.x,2.12], which illegally call this internal method.
663 	 * </p>
664 	 * @param resolvedEntry IClasspathEntry
665 	 * @param accumulatedRoots ObjectVector
666 	 * @param rootIDs HashSet
667 	 * @param referringEntry the CP entry (project) referring to this entry, or null if initial project
668 	 * @param retrieveExportedRoots boolean
669 	 * @throws JavaModelException
670 	 */
computePackageFragmentRoots( IClasspathEntry resolvedEntry, ObjectVector accumulatedRoots, HashSet rootIDs, IClasspathEntry referringEntry, boolean retrieveExportedRoots, Map rootToResolvedEntries)671 	public void computePackageFragmentRoots(
672 		IClasspathEntry resolvedEntry,
673 		ObjectVector accumulatedRoots,
674 		HashSet rootIDs,
675 		IClasspathEntry referringEntry,
676 		boolean retrieveExportedRoots,
677 		Map rootToResolvedEntries) throws JavaModelException {
678 		computePackageFragmentRoots(resolvedEntry, accumulatedRoots, rootIDs, referringEntry, retrieveExportedRoots, true, rootToResolvedEntries);
679 	}
680 
681 	/**
682 	 * Returns the package fragment roots identified by the given entry. In case it refers to
683 	 * a project, it will follow its classpath so as to find exported roots as well.
684 	 * Only works with resolved entry
685 	 * @param resolvedEntry IClasspathEntry
686 	 * @param accumulatedRoots ObjectVector
687 	 * @param rootIDs HashSet
688 	 * @param referringEntry the CP entry (project) referring to this entry, or null if initial project
689 	 * @param retrieveExportedRoots boolean
690 	 * @param filterModuleRoots if true, roots corresponding to modules will be filtered if applicable:
691 	 *    if a limit-modules attribute exists, this is used, otherwise system modules will be filtered
692 	 *    according to the rules of root modules per JEP 261.
693 	 * @throws JavaModelException
694 	 */
computePackageFragmentRoots( IClasspathEntry resolvedEntry, ObjectVector accumulatedRoots, HashSet rootIDs, IClasspathEntry referringEntry, boolean retrieveExportedRoots, boolean filterModuleRoots, Map rootToResolvedEntries, boolean excludeTestCode)695 	public void computePackageFragmentRoots(
696 		IClasspathEntry resolvedEntry,
697 		ObjectVector accumulatedRoots,
698 		HashSet rootIDs,
699 		IClasspathEntry referringEntry,
700 		boolean retrieveExportedRoots,
701 		boolean filterModuleRoots,
702 		Map rootToResolvedEntries,
703 		boolean excludeTestCode) throws JavaModelException {
704 
705 		String rootID = ((ClasspathEntry)resolvedEntry).rootID();
706 		if (rootIDs.contains(rootID)) return;
707 		if(excludeTestCode && ((ClasspathEntry)resolvedEntry).isTest()) {
708 			return;
709 		}
710 
711 		IPath projectPath = this.project.getFullPath();
712 		IPath entryPath = resolvedEntry.getPath();
713 		IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
714 		IPackageFragmentRoot root = null;
715 
716 		switch(resolvedEntry.getEntryKind()){
717 			// source folder
718 			case IClasspathEntry.CPE_SOURCE :
719 
720 				if (projectPath.isPrefixOf(entryPath)){
721 					Object target = JavaModel.getTarget(entryPath, true/*check existency*/);
722 					if (target == null) return;
723 
724 					if (target instanceof IFolder || target instanceof IProject){
725 						root = getPackageFragmentRoot((IResource)target);
726 					}
727 				}
728 				break;
729 
730 			// internal/external JAR or folder
731 			case IClasspathEntry.CPE_LIBRARY :
732 				if (referringEntry != null  && !resolvedEntry.isExported())
733 					return;
734 				Object target = JavaModel.getTarget(entryPath, true/*check existency*/);
735 				if (target == null)
736 					return;
737 
738 				if (target instanceof IResource){
739 					// internal target
740 					root = getPackageFragmentRoot((IResource) target, entryPath, resolvedEntry.getExtraAttributes());
741 				} else if (target instanceof File) {
742 					// external target
743 					if (JavaModel.isFile(target)) {
744 						if (JavaModel.isJimage((File) target)) {
745 							PerProjectInfo info = getPerProjectInfo();
746 							ObjectVector imageRoots;
747 							if (info.jrtRoots == null || !info.jrtRoots.containsKey(entryPath)) {
748 								imageRoots = new ObjectVector();
749 								loadModulesInJimage(entryPath, imageRoots, rootToResolvedEntries, resolvedEntry, referringEntry);
750 								info.setJrtPackageRoots(entryPath, imageRoots); // unfiltered
751 								rootIDs.add(rootID);
752 							} else {
753 								imageRoots = info.jrtRoots.get(entryPath);
754 							}
755 							if (filterModuleRoots) {
756 								List<String> rootModules = null;
757 								String limitModules = ClasspathEntry.getExtraAttribute(resolvedEntry, IClasspathAttribute.LIMIT_MODULES);
758 								if (limitModules != null) {
759 									rootModules = Arrays.asList(limitModules.split(",")); //$NON-NLS-1$
760 								} else if (isUnNamedModule()) {
761 									rootModules = defaultRootModules((Iterable) imageRoots);
762 								}
763 								if (rootModules != null) {
764 									imageRoots = filterLimitedModules(entryPath, imageRoots, rootModules);
765 								}
766 							}
767 							accumulatedRoots.addAll(imageRoots);
768 						} else if (JavaModel.isJmod((File) target)) {
769 							root = new JModPackageFragmentRoot(entryPath, this, resolvedEntry.getExtraAttributes());
770 						}
771 						else {
772 							root = new JarPackageFragmentRoot(null, entryPath, this, resolvedEntry.getExtraAttributes());
773 						}
774 					} else if (((File) target).isDirectory()) {
775 						root = new ExternalPackageFragmentRoot(entryPath, this);
776 					}
777 				}
778 				break;
779 
780 			// recurse into required project
781 			case IClasspathEntry.CPE_PROJECT :
782 
783 				if (!retrieveExportedRoots) return;
784 				if (referringEntry != null && !resolvedEntry.isExported()) return;
785 
786 				IResource member = workspaceRoot.findMember(entryPath);
787 				if (member != null && member.getType() == IResource.PROJECT){// double check if bound to project (23977)
788 					IProject requiredProjectRsc = (IProject) member;
789 					if (JavaProject.hasJavaNature(requiredProjectRsc)){ // special builder binary output
790 						rootIDs.add(rootID);
791 						JavaProject requiredProject = (JavaProject)JavaCore.create(requiredProjectRsc);
792 						requiredProject.computePackageFragmentRoots(
793 							requiredProject.getResolvedClasspath(),
794 							accumulatedRoots,
795 							rootIDs,
796 							rootToResolvedEntries == null ? resolvedEntry : ((ClasspathEntry)resolvedEntry).combineWith((ClasspathEntry) referringEntry), // only combine if need to build the reverse map
797 							retrieveExportedRoots,
798 							filterModuleRoots,
799 							rootToResolvedEntries,
800 							excludeTestCode);
801 					}
802 				break;
803 			}
804 		}
805 		if (root != null) {
806 			accumulatedRoots.add(root);
807 			rootIDs.add(rootID);
808 			if (rootToResolvedEntries != null) rootToResolvedEntries.put(root, ((ClasspathEntry)resolvedEntry).combineWith((ClasspathEntry) referringEntry));
809 		}
810 	}
811 
812 	/** Implements selection of root modules per JEP 261. */
defaultRootModules(Iterable<IPackageFragmentRoot> allSystemRoots)813 	public static List<String> defaultRootModules(Iterable<IPackageFragmentRoot> allSystemRoots) {
814 		return internalDefaultRootModules(allSystemRoots,
815 				IPackageFragmentRoot::getElementName,
816 				r ->  (r instanceof JrtPackageFragmentRoot) ? ((JrtPackageFragmentRoot) r).getModule() : null);
817 	}
818 
internalDefaultRootModules(Iterable<T> allSystemModules, Function<T,String> getModuleName, Function<T,IModule> getModule)819 	public static <T> List<String> internalDefaultRootModules(Iterable<T> allSystemModules, Function<T,String> getModuleName, Function<T,IModule> getModule) {
820 		List<String> result = new ArrayList<>();
821 		boolean hasJavaDotSE = false;
822 		for (T mod : allSystemModules) {
823 			String moduleName = getModuleName.apply(mod);
824 			if ("java.se".equals(moduleName)) { //$NON-NLS-1$
825 				result.add(moduleName);
826 				hasJavaDotSE = true;
827 				break;
828 			}
829 		}
830 		for (T mod : allSystemModules) {
831 			String moduleName = getModuleName.apply(mod);
832 			boolean isJavaDotStart = moduleName.startsWith("java."); //$NON-NLS-1$
833 			boolean isPotentialRoot = !isJavaDotStart;	// always include non-java.*
834 			if (!hasJavaDotSE)
835 				isPotentialRoot |= isJavaDotStart;		// no java.se => add all java.*
836 
837 			if (isPotentialRoot) {
838 				IModule module = getModule.apply(mod);
839 				if (module != null) {
840 					for (IPackageExport packageExport : module.exports()) {
841 						if (!packageExport.isQualified()) {
842 							result.add(moduleName);
843 							break;
844 						}
845 					}
846 				}
847 			}
848 		}
849 		return result;
850 	}
851 
filterLimitedModules(IPath jrtPath, ObjectVector imageRoots, List<String> rootModuleNames)852 	private ObjectVector filterLimitedModules(IPath jrtPath, ObjectVector imageRoots, List<String> rootModuleNames) {
853 		Set<String> limitModulesSet = new HashSet<>(rootModuleNames);
854 		ModuleLookup lookup = new ModuleLookup(jrtPath.toFile());
855 		// collect all module roots:
856 		for (int i = 0; i < imageRoots.size(); i++) {
857 			lookup.recordRoot((JrtPackageFragmentRoot) imageRoots.elementAt(i));
858 		}
859 		// for those contained in limitModules, add the transitive closure:
860 		for (int i = 0; i < imageRoots.size(); i++) {
861 			String moduleName = ((JrtPackageFragmentRoot) imageRoots.elementAt(i)).moduleName;
862 			if (limitModulesSet.contains(moduleName))
863 				lookup.addTransitive(moduleName);
864 		}
865 		// map the result back to package fragment roots:
866 		ObjectVector result = new ObjectVector(lookup.resultModuleSet.size());
867 		for (IModule mod : lookup.resultModuleSet) {
868 			result.add(lookup.getRoot(mod));
869 		}
870 		return result;
871 	}
872 
873 	/** Helper for computing the transitive closure of a set of modules. */
874 	private static class ModuleLookup {
875 		File jrtFile;
876 		Map<String, JrtPackageFragmentRoot> modNames2Roots = new HashMap<>();
877 		Map<String, IModule> modules = new HashMap<>();
878 		Set<IModule> resultModuleSet = new HashSet<>();
879 
ModuleLookup(File jrtFile)880 		public ModuleLookup(File jrtFile) {
881 			this.jrtFile = jrtFile;
882 		}
883 
recordRoot(JrtPackageFragmentRoot root)884 		void recordRoot(JrtPackageFragmentRoot root) {
885 			this.modNames2Roots.put(root.moduleName, root);
886 		}
addTransitive(String moduleName)887 		void addTransitive(String moduleName) {
888 			IModule module = getModule(moduleName);
889 			if (module != null && this.resultModuleSet.add(module)) {
890 				for (IModuleReference reqRef : module.requires())
891 					addTransitive(String.valueOf(reqRef.name()));
892 			}
893 		}
getModule(String moduleName)894 		private IModule getModule(String moduleName) {
895 			IModule result = this.modules.get(moduleName);
896 			if (result == null) {
897 				JrtPackageFragmentRoot root = this.modNames2Roots.get(moduleName);
898 				if (root != null) {
899 					try {
900 						ClassFileReader classFile = JRTUtil.getClassfile(this.jrtFile, TypeConstants.MODULE_INFO_CLASS_NAME_STRING, root.moduleName, null);
901 						result = classFile.getModuleDeclaration();
902 						this.modules.put(moduleName, result);
903 					} catch (IOException | ClassFormatException e) {
904 						JavaCore.getJavaCore().getLog().log(new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, "Failed to read module-info.class", e)); //$NON-NLS-1$
905 					}
906 				}
907 			}
908 			return result;
909 		}
getRoot(IModule module)910 		JrtPackageFragmentRoot getRoot(IModule module) {
911 			return this.modNames2Roots.get(String.valueOf(module.name()));
912 		}
913 	}
914 
915 	/**
916 	 * This bogus package fragment root acts as placeholder plus bridge for the
917 	 * real one until the module name becomes available. It is useful in certain
918 	 * scenarios like creating package roots from delta processors, search etc.
919 	 */
920 	class JImageModuleFragmentBridge extends JarPackageFragmentRoot {
921 
JImageModuleFragmentBridge(IPath externalJarPath, IClasspathAttribute[] extraAttributes)922 		protected JImageModuleFragmentBridge(IPath externalJarPath, IClasspathAttribute[] extraAttributes) {
923 			super(null, externalJarPath, JavaProject.this, extraAttributes);
924 		}
925 		@Override
getPackageFragment(String[] pkgName)926 		public PackageFragment getPackageFragment(String[] pkgName) {
927 			return getPackageFragment(pkgName, null);
928 		}
929 		@Override
getPackageFragment(String[] pkgName, String mod)930 		public PackageFragment getPackageFragment(String[] pkgName, String mod) {
931 			PackageFragmentRoot realRoot = new JrtPackageFragmentRoot(this.jarPath,
932 												mod == null ?  JRTUtil.JAVA_BASE : mod,
933 												JavaProject.this,
934 												this.extraAttributes);
935 			return new JarPackageFragment(realRoot, pkgName);
936 		}
937 		@Override
computeChildren(OpenableElementInfo info, IResource underlyingResource)938 		protected boolean computeChildren(OpenableElementInfo info, IResource underlyingResource) throws JavaModelException {
939 			// Do nothing, idea is to avoid this being read in JarPackageFragmentRoot as a Jar.
940 			return true;
941 		}
isModule()942 		public boolean isModule() {
943 			return true;
944 		}
945 	}
946 
loadModulesInJimage(final IPath imagePath, final ObjectVector roots, final Map rootToResolvedEntries, final IClasspathEntry resolvedEntry, final IClasspathEntry referringEntry)947 	private void loadModulesInJimage(final IPath imagePath, final ObjectVector roots, final Map rootToResolvedEntries,
948 				final IClasspathEntry resolvedEntry, final IClasspathEntry referringEntry) {
949 		try {
950 			org.eclipse.jdt.internal.compiler.util.JRTUtil.walkModuleImage(imagePath.toFile(),
951 					new org.eclipse.jdt.internal.compiler.util.JRTUtil.JrtFileVisitor<java.nio.file.Path>() {
952 				@Override
953 				public FileVisitResult visitPackage(java.nio.file.Path dir, java.nio.file.Path mod, BasicFileAttributes attrs) throws IOException {
954 					return FileVisitResult.SKIP_SIBLINGS;
955 				}
956 
957 				@Override
958 				public FileVisitResult visitFile(java.nio.file.Path path, java.nio.file.Path mod, BasicFileAttributes attrs) throws IOException {
959 					return FileVisitResult.SKIP_SIBLINGS;
960 				}
961 
962 				@Override
963 				public FileVisitResult visitModule(java.nio.file.Path path, String name) throws IOException {
964 					JrtPackageFragmentRoot root = new JrtPackageFragmentRoot(imagePath, name, JavaProject.this, resolvedEntry.getExtraAttributes());
965 					roots.add(root);
966 					if (rootToResolvedEntries != null)
967 						rootToResolvedEntries.put(root, ((ClasspathEntry)resolvedEntry).combineWith((ClasspathEntry) referringEntry));
968 					return FileVisitResult.SKIP_SUBTREE;
969 				}
970 			}, JRTUtil.NOTIFY_MODULES);
971 		} catch (IOException e) {
972 			Util.log(IStatus.ERROR, "Error reading modules from " + imagePath.toOSString()); //$NON-NLS-1$
973 		}
974 	}
975 
976 	@Override
findUnfilteredPackageFragmentRoots(IClasspathEntry entry)977 	public IPackageFragmentRoot[] findUnfilteredPackageFragmentRoots(IClasspathEntry entry) {
978 		try {
979 			IClasspathEntry[] resolvedEntries = resolveClasspath(new IClasspathEntry[]{ entry });
980 			return computePackageFragmentRoots(resolvedEntries, false /* not exported roots */, false /* don't filter! */, null /* no reverse map */);
981 		} catch (JavaModelException e) {
982 			// according to comment in findPackageFragmentRoots() we assume that this is caused by the project no longer existing
983 			return new IPackageFragmentRoot[] {};
984 		}
985 	}
986 
computePackageFragmentRoots( IClasspathEntry[] resolvedClasspath, boolean retrieveExportedRoots, boolean filterModuleRoots, Map rootToResolvedEntries)987 	public IPackageFragmentRoot[] computePackageFragmentRoots(
988 			IClasspathEntry[] resolvedClasspath,
989 			boolean retrieveExportedRoots,
990 			boolean filterModuleRoots,
991 			Map rootToResolvedEntries) throws JavaModelException {
992 		return computePackageFragmentRoots(resolvedClasspath, retrieveExportedRoots, filterModuleRoots, rootToResolvedEntries, false);
993 	}
994 	/**
995 	 * Returns (local/all) the package fragment roots identified by the given project's classpath.
996 	 * Note: this follows project classpath references to find required project contributions,
997 	 * eliminating duplicates silently.
998 	 * Only works with resolved entries
999 	 * @param resolvedClasspath IClasspathEntry[]
1000 	 * @param retrieveExportedRoots boolean
1001 	 * @param filterModuleRoots if true, roots corresponding to modules will be filtered if applicable:
1002 	 *    if a limit-modules attribute exists, this is used, otherwise system modules will be filtered
1003 	 *    according to the rules of root modules per JEP 261.
1004 	 * @return IPackageFragmentRoot[]
1005 	 * @throws JavaModelException
1006 	 */
computePackageFragmentRoots( IClasspathEntry[] resolvedClasspath, boolean retrieveExportedRoots, boolean filterModuleRoots, Map rootToResolvedEntries, boolean excludeTestCode)1007 	public IPackageFragmentRoot[] computePackageFragmentRoots(
1008 					IClasspathEntry[] resolvedClasspath,
1009 					boolean retrieveExportedRoots,
1010 					boolean filterModuleRoots,
1011 					Map rootToResolvedEntries,
1012 					boolean excludeTestCode) throws JavaModelException {
1013 
1014 		ObjectVector accumulatedRoots = new ObjectVector();
1015 		computePackageFragmentRoots(
1016 			resolvedClasspath,
1017 			accumulatedRoots,
1018 			new HashSet(5), // rootIDs
1019 			null, // inside original project
1020 			retrieveExportedRoots,
1021 			filterModuleRoots,
1022 			rootToResolvedEntries,
1023 			excludeTestCode);
1024 		IPackageFragmentRoot[] rootArray = new IPackageFragmentRoot[accumulatedRoots.size()];
1025 		accumulatedRoots.copyInto(rootArray);
1026 		return rootArray;
1027 	}
1028 
1029 	@Deprecated
computePackageFragmentRoots( IClasspathEntry[] resolvedClasspath, ObjectVector accumulatedRoots, HashSet rootIDs, IClasspathEntry referringEntry, boolean retrieveExportedRoots, boolean filterModuleRoots, Map rootToResolvedEntries)1030 	public void computePackageFragmentRoots(
1031 			IClasspathEntry[] resolvedClasspath,
1032 			ObjectVector accumulatedRoots,
1033 			HashSet rootIDs,
1034 			IClasspathEntry referringEntry,
1035 			boolean retrieveExportedRoots,
1036 			boolean filterModuleRoots,
1037 			Map rootToResolvedEntries) throws JavaModelException {
1038 		computePackageFragmentRoots(resolvedClasspath, accumulatedRoots, rootIDs, referringEntry, retrieveExportedRoots,
1039 				filterModuleRoots, rootToResolvedEntries, false);
1040 	}
1041 	/**
1042 	 * Returns (local/all) the package fragment roots identified by the given project's classpath.
1043 	 * Note: this follows project classpath references to find required project contributions,
1044 	 * eliminating duplicates silently.
1045 	 * Only works with resolved entries
1046 	 * @param resolvedClasspath IClasspathEntry[]
1047 	 * @param accumulatedRoots ObjectVector
1048 	 * @param rootIDs HashSet
1049 	 * @param referringEntry project entry referring to this CP or null if initial project
1050 	 * @param retrieveExportedRoots boolean
1051 	 * @param filterModuleRoots if true, roots corresponding to modules will be filtered if applicable:
1052 	 *    if a limit-modules attribute exists, this is used, otherwise system modules will be filtered
1053 	 *    according to the rules of root modules per JEP 261.
1054 	 * @throws JavaModelException
1055 	 */
computePackageFragmentRoots( IClasspathEntry[] resolvedClasspath, ObjectVector accumulatedRoots, HashSet rootIDs, IClasspathEntry referringEntry, boolean retrieveExportedRoots, boolean filterModuleRoots, Map rootToResolvedEntries, boolean excludeTestCode)1056 	public void computePackageFragmentRoots(
1057 		IClasspathEntry[] resolvedClasspath,
1058 		ObjectVector accumulatedRoots,
1059 		HashSet rootIDs,
1060 		IClasspathEntry referringEntry,
1061 		boolean retrieveExportedRoots,
1062 		boolean filterModuleRoots,
1063 		Map rootToResolvedEntries,
1064 		boolean excludeTestCode) throws JavaModelException {
1065 
1066 		if (referringEntry == null){
1067 			rootIDs.add(rootID());
1068 		}
1069 		for (int i = 0, length = resolvedClasspath.length; i < length; i++){
1070 			computePackageFragmentRoots(
1071 				resolvedClasspath[i],
1072 				accumulatedRoots,
1073 				rootIDs,
1074 				referringEntry,
1075 				retrieveExportedRoots,
1076 				filterModuleRoots,
1077 				rootToResolvedEntries,
1078 				excludeTestCode);
1079 		}
1080 	}
1081 	/**
1082 	 * Compute the file name to use for a given shared property
1083 	 * @param qName QualifiedName
1084 	 * @return String
1085 	 */
computeSharedPropertyFileName(QualifiedName qName)1086 	public String computeSharedPropertyFileName(QualifiedName qName) {
1087 
1088 		return '.' + qName.getLocalName();
1089 	}
1090 
1091 	/**
1092 	 * Configure the project with Java nature.
1093 	 */
1094 	@Override
configure()1095 	public void configure() throws CoreException {
1096 
1097 		// register Java builder
1098 		addToBuildSpec(JavaCore.BUILDER_ID);
1099 	}
1100 
1101 	/*
1102 	 * Returns whether the given resource is accessible through the children or the non-Java resources of this project.
1103 	 * Returns true if the resource is not in the project.
1104 	 * Assumes that the resource is a folder or a file.
1105 	 */
contains(IResource resource)1106 	public boolean contains(IResource resource) {
1107 
1108 		IClasspathEntry[] classpath;
1109 		IPath output;
1110 		try {
1111 			classpath = getResolvedClasspath();
1112 			output = getOutputLocation();
1113 		} catch (JavaModelException e) {
1114 			return false;
1115 		}
1116 
1117 		IPath fullPath = resource.getFullPath();
1118 		IPath innerMostOutput = output.isPrefixOf(fullPath) ? output : null;
1119 		IClasspathEntry innerMostEntry = null;
1120 		ExternalFoldersManager foldersManager = JavaModelManager.getExternalManager();
1121 		for (int j = 0, cpLength = classpath.length; j < cpLength; j++) {
1122 			IClasspathEntry entry = classpath[j];
1123 
1124 			IPath entryPath = entry.getPath();
1125 			if (entry.getEntryKind() == IClasspathEntry.CPE_LIBRARY) {
1126 				IResource linkedFolder = foldersManager.getFolder(entryPath);
1127 				if (linkedFolder != null)
1128 					entryPath = linkedFolder.getFullPath();
1129 			}
1130 			if ((innerMostEntry == null || innerMostEntry.getPath().isPrefixOf(entryPath))
1131 					&& entryPath.isPrefixOf(fullPath)) {
1132 				innerMostEntry = entry;
1133 			}
1134 			IPath entryOutput = classpath[j].getOutputLocation();
1135 			if (entryOutput != null && entryOutput.isPrefixOf(fullPath)) {
1136 				innerMostOutput = entryOutput;
1137 			}
1138 		}
1139 		if (innerMostEntry != null) {
1140 			// special case prj==src and nested output location
1141 			if (innerMostOutput != null && innerMostOutput.segmentCount() > 1 // output isn't project
1142 					&& innerMostEntry.getPath().segmentCount() == 1) { // 1 segment must be project name
1143 				return false;
1144 			}
1145 			if  (resource instanceof IFolder) {
1146 				 // folders are always included in src/lib entries
1147 				 return true;
1148 			}
1149 			switch (innerMostEntry.getEntryKind()) {
1150 				case IClasspathEntry.CPE_SOURCE:
1151 					// .class files are not visible in source folders
1152 					return !org.eclipse.jdt.internal.compiler.util.Util.isClassFileName(fullPath.lastSegment());
1153 				case IClasspathEntry.CPE_LIBRARY:
1154 					// .java files are not visible in library folders
1155 					return !org.eclipse.jdt.internal.core.util.Util.isJavaLikeFileName(fullPath.lastSegment());
1156 			}
1157 		}
1158 		if (innerMostOutput != null) {
1159 			return false;
1160 		}
1161 		return true;
1162 	}
1163 
1164 	/**
1165 	 * Record a new marker denoting a classpath problem
1166 	 */
createClasspathProblemMarker(IJavaModelStatus status)1167 	public void createClasspathProblemMarker(IJavaModelStatus status) {
1168 
1169 		IMarker marker = null;
1170 		int severity;
1171 		String[] arguments = CharOperation.NO_STRINGS;
1172 		boolean isCycleProblem = false, isClasspathFileFormatProblem = false, isOutputOverlapping = false;
1173 		switch (status.getCode()) {
1174 
1175 			case  IJavaModelStatusConstants.CLASSPATH_CYCLE :
1176 				isCycleProblem = true;
1177 				if (JavaCore.ERROR.equals(getOption(JavaCore.CORE_CIRCULAR_CLASSPATH, true))) {
1178 					severity = IMarker.SEVERITY_ERROR;
1179 				} else {
1180 					severity = IMarker.SEVERITY_WARNING;
1181 				}
1182 				break;
1183 
1184 			case  IJavaModelStatusConstants.INVALID_CLASSPATH_FILE_FORMAT :
1185 				isClasspathFileFormatProblem = true;
1186 				severity = IMarker.SEVERITY_ERROR;
1187 				break;
1188 
1189 			case  IJavaModelStatusConstants.INCOMPATIBLE_JDK_LEVEL :
1190 				String setting = getOption(JavaCore.CORE_INCOMPATIBLE_JDK_LEVEL, true);
1191 				if (JavaCore.ERROR.equals(setting)) {
1192 					severity = IMarker.SEVERITY_ERROR;
1193 				} else if (JavaCore.WARNING.equals(setting)) {
1194 					severity = IMarker.SEVERITY_WARNING;
1195 				} else {
1196 					return; // setting == IGNORE
1197 				}
1198 				break;
1199 			case IJavaModelStatusConstants.OUTPUT_LOCATION_OVERLAPPING_ANOTHER_SOURCE :
1200 				isOutputOverlapping = true;
1201 				setting = getOption(JavaCore.CORE_OUTPUT_LOCATION_OVERLAPPING_ANOTHER_SOURCE, true);
1202 				if (JavaCore.ERROR.equals(setting)) {
1203 					severity = IMarker.SEVERITY_ERROR;
1204 				} else if (JavaCore.WARNING.equals(setting)) {
1205 					severity = IMarker.SEVERITY_WARNING;
1206 				} else {
1207 					return; // setting == IGNORE
1208 				}
1209 				break;
1210 			case IJavaModelStatusConstants.MAIN_ONLY_PROJECT_DEPENDS_ON_TEST_ONLY_PROJECT:
1211 				setting = getOption(JavaCore.CORE_MAIN_ONLY_PROJECT_HAS_TEST_ONLY_DEPENDENCY, true);
1212 				if (JavaCore.ERROR.equals(setting)) {
1213 					severity = IMarker.SEVERITY_ERROR;
1214 				} else {
1215 					return; // setting == IGNORE
1216 				}
1217 				break;
1218 			default:
1219 				IPath path = status.getPath();
1220 				if (path != null) arguments = new String[] { path.toString() };
1221 				if (JavaCore.ERROR.equals(getOption(JavaCore.CORE_INCOMPLETE_CLASSPATH, true)) &&
1222 					status.getSeverity() != IStatus.WARNING) {
1223 					severity = IMarker.SEVERITY_ERROR;
1224 				} else {
1225 					severity = IMarker.SEVERITY_WARNING;
1226 				}
1227 				break;
1228 		}
1229 
1230 		try {
1231 			marker = this.project.createMarker(IJavaModelMarker.BUILDPATH_PROBLEM_MARKER);
1232 			marker.setAttributes(
1233 				new String[] {
1234 					IMarker.MESSAGE,
1235 					IMarker.SEVERITY,
1236 					IMarker.LOCATION,
1237 					IJavaModelMarker.CYCLE_DETECTED,
1238 					IJavaModelMarker.CLASSPATH_FILE_FORMAT,
1239 					IJavaModelMarker.OUTPUT_OVERLAPPING_SOURCE,
1240 					IJavaModelMarker.ID,
1241 					IJavaModelMarker.ARGUMENTS ,
1242 					IJavaModelMarker.CATEGORY_ID,
1243 					IMarker.SOURCE_ID,
1244 				},
1245 				new Object[] {
1246 					status.getMessage(),
1247 					Integer.valueOf(severity),
1248 					Messages.classpath_buildPath,
1249 					isCycleProblem ? "true" : "false",//$NON-NLS-1$ //$NON-NLS-2$
1250 					isClasspathFileFormatProblem ? "true" : "false",//$NON-NLS-1$ //$NON-NLS-2$
1251 					isOutputOverlapping ? "true" : "false", //$NON-NLS-1$ //$NON-NLS-2$
1252 					Integer.valueOf(status.getCode()),
1253 					Util.getProblemArgumentsForMarker(arguments) ,
1254 					Integer.valueOf(CategorizedProblem.CAT_BUILDPATH),
1255 					JavaBuilder.SOURCE_ID,
1256 				}
1257 			);
1258 		} catch (CoreException e) {
1259 			// could not create marker: cannot do much
1260 			if (JavaModelManager.VERBOSE) {
1261 				e.printStackTrace();
1262 			}
1263 		}
1264 	}
1265 
1266 	/**
1267 	 * Returns a new element info for this element.
1268 	 */
1269 	@Override
createElementInfo()1270 	protected Object createElementInfo() {
1271 		return new JavaProjectElementInfo();
1272 	}
1273 
1274 	/**
1275 	 * Reads and decode an XML classpath string. Returns a two-dimensional array, where the number of elements in the row is fixed to 2.
1276 	 * The first element is an array of raw classpath entries and the second element is an array of referenced entries that may have been stored
1277 	 * by the client earlier. See {@link IJavaProject#getReferencedClasspathEntries()} for more details.
1278 	 *
1279 	 */
decodeClasspath(String xmlClasspath, Map unknownElements)1280 	public IClasspathEntry[][] decodeClasspath(String xmlClasspath, Map unknownElements) throws IOException, ClasspathEntry.AssertionFailedException {
1281 
1282 		ArrayList paths = new ArrayList();
1283 		IClasspathEntry defaultOutput = null;
1284 		StringReader reader = new StringReader(xmlClasspath);
1285 		Element cpElement;
1286 		try {
1287 			DocumentBuilder parser = DocumentBuilderFactory.newInstance().newDocumentBuilder();
1288 			cpElement = parser.parse(new InputSource(reader)).getDocumentElement();
1289 		} catch (SAXException | ParserConfigurationException e) {
1290 			throw new IOException(Messages.file_badFormat, e);
1291 		} finally {
1292 			reader.close();
1293 		}
1294 
1295 		if (!cpElement.getNodeName().equalsIgnoreCase("classpath")) { //$NON-NLS-1$
1296 			throw new IOException(Messages.file_badFormat);
1297 		}
1298 		NodeList list = cpElement.getElementsByTagName(ClasspathEntry.TAG_CLASSPATHENTRY);
1299 		int length = list.getLength();
1300 
1301 		for (int i = 0; i < length; ++i) {
1302 			Node node = list.item(i);
1303 			if (node.getNodeType() == Node.ELEMENT_NODE) {
1304 				IClasspathEntry entry = ClasspathEntry.elementDecode((Element)node, this, unknownElements);
1305 				if (entry != null){
1306 					if (entry.getContentKind() == ClasspathEntry.K_OUTPUT) {
1307 						defaultOutput = entry; // separate output
1308 					} else {
1309 						paths.add(entry);
1310 					}
1311 				}
1312 			}
1313 		}
1314 		int pathSize = paths.size();
1315 		IClasspathEntry[][] entries = new IClasspathEntry[2][];
1316 		entries[0] = new IClasspathEntry[pathSize + (defaultOutput == null ? 0 : 1)];
1317 		paths.toArray(entries[0]);
1318 		if (defaultOutput != null) entries[0][pathSize] = defaultOutput; // ensure output is last item
1319 
1320 		paths.clear();
1321 		list = cpElement.getElementsByTagName(ClasspathEntry.TAG_REFERENCED_ENTRY);
1322 		length = list.getLength();
1323 
1324 		for (int i = 0; i < length; ++i) {
1325 			Node node = list.item(i);
1326 			if (node.getNodeType() == Node.ELEMENT_NODE) {
1327 				IClasspathEntry entry = ClasspathEntry.elementDecode((Element)node, this, unknownElements);
1328 				if (entry != null){
1329 					paths.add(entry);
1330 				}
1331 			}
1332 		}
1333 		entries[1] = new IClasspathEntry[paths.size()];
1334 		paths.toArray(entries[1]);
1335 
1336 		return entries;
1337 	}
1338 
1339 	@Override
decodeClasspathEntry(String encodedEntry)1340 	public IClasspathEntry decodeClasspathEntry(String encodedEntry) {
1341 
1342 		try {
1343 			if (encodedEntry == null) return null;
1344 			StringReader reader = new StringReader(encodedEntry);
1345 			Element node;
1346 
1347 			try {
1348 				DocumentBuilder parser =
1349 					DocumentBuilderFactory.newInstance().newDocumentBuilder();
1350 				node = parser.parse(new InputSource(reader)).getDocumentElement();
1351 			} catch (SAXException | ParserConfigurationException e) {
1352 				return null;
1353 			} finally {
1354 				reader.close();
1355 			}
1356 
1357 			if (!node.getNodeName().equalsIgnoreCase(ClasspathEntry.TAG_CLASSPATHENTRY)
1358 					|| node.getNodeType() != Node.ELEMENT_NODE) {
1359 				return null;
1360 			}
1361 			return ClasspathEntry.elementDecode(node, this, null/*not interested in unknown elements*/);
1362 		} catch (IOException e) {
1363 			// bad format
1364 			return null;
1365 		}
1366 	}
1367 
1368 	/**
1369 	/**
1370 	 * Removes the Java nature from the project.
1371 	 */
1372 	@Override
deconfigure()1373 	public void deconfigure() throws CoreException {
1374 
1375 		// deregister Java builder
1376 		removeFromBuildSpec(JavaCore.BUILDER_ID);
1377 
1378 		// remove .classpath file
1379 //		getProject().getFile(ClasspathHelper.CLASSPATH_FILENAME).delete(false, null);
1380 	}
1381 
1382 	/**
1383 	 * Returns a default class path.
1384 	 * This is the root of the project
1385 	 */
defaultClasspath()1386 	protected IClasspathEntry[] defaultClasspath() {
1387 
1388 		return new IClasspathEntry[] {
1389 			 JavaCore.newSourceEntry(this.project.getFullPath())};
1390 	}
1391 
1392 	/**
1393 	 * Returns a default output location.
1394 	 * This is the project bin folder
1395 	 */
defaultOutputLocation()1396 	protected IPath defaultOutputLocation() {
1397 		return this.project.getFullPath().append("bin"); //$NON-NLS-1$
1398 	}
1399 
1400 	/**
1401 	 * Returns the XML String encoding of the class path.
1402 	 */
encodeClasspath(IClasspathEntry[] classpath, IClasspathEntry[] referencedEntries, IPath outputLocation, boolean indent, Map unknownElements)1403 	protected String encodeClasspath(IClasspathEntry[] classpath, IClasspathEntry[] referencedEntries, IPath outputLocation, boolean indent, Map unknownElements) throws JavaModelException {
1404 		try {
1405 			ByteArrayOutputStream s = new ByteArrayOutputStream();
1406 			OutputStreamWriter writer = new OutputStreamWriter(s, "UTF8"); //$NON-NLS-1$
1407 			XMLWriter xmlWriter = new XMLWriter(writer, this, true/*print XML version*/);
1408 
1409 			xmlWriter.startTag(ClasspathEntry.TAG_CLASSPATH, indent);
1410 			for (int i = 0; i < classpath.length; ++i) {
1411 				((ClasspathEntry)classpath[i]).elementEncode(xmlWriter, this.project.getFullPath(), indent, true, unknownElements, false);
1412 			}
1413 
1414 			if (outputLocation != null) {
1415 				outputLocation = outputLocation.removeFirstSegments(1);
1416 				outputLocation = outputLocation.makeRelative();
1417 				HashMap parameters = new HashMap();
1418 				parameters.put(ClasspathEntry.TAG_KIND, ClasspathEntry.kindToString(ClasspathEntry.K_OUTPUT));
1419 				parameters.put(ClasspathEntry.TAG_PATH, String.valueOf(outputLocation));
1420 				xmlWriter.printTag(ClasspathEntry.TAG_CLASSPATHENTRY, parameters, indent, true, true);
1421 			}
1422 
1423 			if (referencedEntries != null) {
1424 				for (int i = 0; i < referencedEntries.length; ++i) {
1425 					((ClasspathEntry) referencedEntries[i]).elementEncode(xmlWriter, this.project.getFullPath(), indent, true, unknownElements, true);
1426 				}
1427 			}
1428 
1429 			xmlWriter.endTag(ClasspathEntry.TAG_CLASSPATH, indent, true/*insert new line*/);
1430 			writer.flush();
1431 			writer.close();
1432 			return s.toString("UTF8");//$NON-NLS-1$
1433 		} catch (IOException e) {
1434 			throw new JavaModelException(e, IJavaModelStatusConstants.IO_EXCEPTION);
1435 		}
1436 	}
1437 
1438 	@Override
encodeClasspathEntry(IClasspathEntry classpathEntry)1439 	public String encodeClasspathEntry(IClasspathEntry classpathEntry) {
1440 		try {
1441 			ByteArrayOutputStream s = new ByteArrayOutputStream();
1442 			OutputStreamWriter writer = new OutputStreamWriter(s, "UTF8"); //$NON-NLS-1$
1443 			XMLWriter xmlWriter = new XMLWriter(writer, this, false/*don't print XML version*/);
1444 
1445 			((ClasspathEntry)classpathEntry).elementEncode(xmlWriter, this.project.getFullPath(), true/*indent*/, true/*insert new line*/, null/*not interested in unknown elements*/, (classpathEntry.getReferencingEntry() != null));
1446 
1447 			writer.flush();
1448 			writer.close();
1449 			return s.toString("UTF8");//$NON-NLS-1$
1450 		} catch (IOException e) {
1451 			return null; // never happens since all is done in memory
1452 		}
1453 	}
1454 
1455 	/**
1456 	 * Returns true if this handle represents the same Java project
1457 	 * as the given handle. Two handles represent the same
1458 	 * project if they are identical or if they represent a project with
1459 	 * the same underlying resource and occurrence counts.
1460 	 *
1461 	 * @see JavaElement#equals(Object)
1462 	 */
1463 	@Override
equals(Object o)1464 	public boolean equals(Object o) {
1465 
1466 		if (this == o)
1467 			return true;
1468 
1469 		if (!(o instanceof JavaProject))
1470 			return false;
1471 
1472 		JavaProject other = (JavaProject) o;
1473 		return this.project.equals(other.getProject());
1474 	}
1475 
1476 	/**
1477 	 * @see IJavaProject#findElement(IPath)
1478 	 */
1479 	@Override
findElement(IPath path)1480 	public IJavaElement findElement(IPath path) throws JavaModelException {
1481 		return findElement(path, DefaultWorkingCopyOwner.PRIMARY);
1482 	}
1483 
1484 	/**
1485 	 * @see IJavaProject#findElement(IPath, WorkingCopyOwner)
1486 	 */
1487 	@Override
findElement(IPath path, WorkingCopyOwner owner)1488 	public IJavaElement findElement(IPath path, WorkingCopyOwner owner) throws JavaModelException {
1489 
1490 		if (path == null || path.isAbsolute()) {
1491 			throw new JavaModelException(
1492 				new JavaModelStatus(IJavaModelStatusConstants.INVALID_PATH, path));
1493 		}
1494 		try {
1495 
1496 			String extension = path.getFileExtension();
1497 			if (extension == null) {
1498 				String packageName = path.toString().replace(IPath.SEPARATOR, '.');
1499 				return findPackageFragment(packageName);
1500 			} else if (Util.isJavaLikeFileName(path.lastSegment())
1501 					|| extension.equalsIgnoreCase(EXTENSION_class)) {
1502 				IPath packagePath = path.removeLastSegments(1);
1503 				String packageName = packagePath.toString().replace(IPath.SEPARATOR, '.');
1504 				String typeName = path.lastSegment();
1505 				typeName = typeName.substring(0, typeName.length() - extension.length() - 1);
1506 				String qualifiedName = null;
1507 				if (packageName.length() > 0) {
1508 					qualifiedName = packageName + "." + typeName; //$NON-NLS-1$
1509 				} else {
1510 					qualifiedName = typeName;
1511 				}
1512 
1513 				// lookup type
1514 				NameLookup lookup = newNameLookup(owner);
1515 				NameLookup.Answer answer = lookup.findType(
1516 					qualifiedName,
1517 					false,
1518 					NameLookup.ACCEPT_ALL,
1519 					true/* consider secondary types */,
1520 					false/* do NOT wait for indexes */,
1521 					false/*don't check restrictions*/,
1522 					null);
1523 
1524 				if (answer != null) {
1525 					return answer.type.getParent();
1526 				} else {
1527 					return null;
1528 				}
1529 			} else {
1530 				// unsupported extension
1531 				return null;
1532 			}
1533 		} catch (JavaModelException e) {
1534 			if (e.getStatus().getCode()
1535 				== IJavaModelStatusConstants.ELEMENT_DOES_NOT_EXIST) {
1536 				return null;
1537 			} else {
1538 				throw e;
1539 			}
1540 		}
1541 	}
1542 
findPackageFragment(String packageName)1543 	public IJavaElement findPackageFragment(String packageName)
1544 			throws JavaModelException {
1545 		NameLookup lookup = newNameLookup((WorkingCopyOwner)null/*no need to look at working copies for pkgs*/);
1546 		IPackageFragment[] pkgFragments = lookup.findPackageFragments(packageName, false);
1547 		if (pkgFragments == null) {
1548 			return null;
1549 
1550 		} else {
1551 			// try to return one that is a child of this project
1552 			for (int i = 0, length = pkgFragments.length; i < length; i++) {
1553 
1554 				IPackageFragment pkgFragment = pkgFragments[i];
1555 				if (equals(pkgFragment.getParent().getParent())) {
1556 					return pkgFragment;
1557 				}
1558 			}
1559 			// default to the first one
1560 			return pkgFragments[0];
1561 		}
1562 	}
1563 
1564 	@Override
findElement(String bindingKey, WorkingCopyOwner owner)1565 	public IJavaElement findElement(String bindingKey, WorkingCopyOwner owner) throws JavaModelException {
1566 		JavaElementFinder elementFinder = new JavaElementFinder(bindingKey, this, owner);
1567 		elementFinder.parse();
1568 		if (elementFinder.exception != null)
1569 			throw elementFinder.exception;
1570 		return elementFinder.element;
1571 	}
1572 
1573 	/**
1574 	 * @see IJavaProject
1575 	 */
1576 	@Override
findPackageFragment(IPath path)1577 	public IPackageFragment findPackageFragment(IPath path)
1578 		throws JavaModelException {
1579 
1580 		return findPackageFragment0(JavaProject.canonicalizedPath(path));
1581 	}
1582 	/*
1583 	 * non path canonicalizing version
1584 	 */
findPackageFragment0(IPath path)1585 	private IPackageFragment findPackageFragment0(IPath path)
1586 		throws JavaModelException {
1587 
1588 		NameLookup lookup = newNameLookup((WorkingCopyOwner)null/*no need to look at working copies for pkgs*/);
1589 		return lookup.findPackageFragment(path);
1590 	}
1591 
1592 	/**
1593 	 * @see IJavaProject
1594 	 */
1595 	@Override
findPackageFragmentRoot(IPath path)1596 	public IPackageFragmentRoot findPackageFragmentRoot(IPath path)
1597 		throws JavaModelException {
1598 
1599 		return findPackageFragmentRoot0(JavaProject.canonicalizedPath(path));
1600 	}
1601 	/*
1602 	 * no path canonicalization
1603 	 */
findPackageFragmentRoot0(IPath path)1604 	public IPackageFragmentRoot findPackageFragmentRoot0(IPath path)
1605 		throws JavaModelException {
1606 
1607 		IPackageFragmentRoot[] allRoots = this.getAllPackageFragmentRoots();
1608 		if (!path.isAbsolute()) {
1609 			throw new IllegalArgumentException(Messages.path_mustBeAbsolute);
1610 		}
1611 		for (int i= 0; i < allRoots.length; i++) {
1612 			IPackageFragmentRoot classpathRoot= allRoots[i];
1613 			if (classpathRoot.getPath() != null && classpathRoot.getPath().equals(path)) {
1614 				return classpathRoot;
1615 			}
1616 		}
1617 		return null;
1618 	}
1619 	/**
1620 	 * @see IJavaProject
1621 	 */
1622 	@Override
findPackageFragmentRoots(IClasspathEntry entry)1623 	public IPackageFragmentRoot[] findPackageFragmentRoots(IClasspathEntry entry) {
1624 		try {
1625 			IClasspathEntry[] classpath = getRawClasspath();
1626 			for (int i = 0, length = classpath.length; i < length; i++) {
1627 				if (classpath[i].equals(entry)) { // entry may need to be resolved
1628 					return
1629 						computePackageFragmentRoots(
1630 							resolveClasspath(new IClasspathEntry[] {entry}),
1631 							false, // don't retrieve exported roots
1632 							true, // filterModuleRoots
1633 							null); /*no reverse map*/
1634 				}
1635 			}
1636 		} catch (JavaModelException e) {
1637 			// project doesn't exist: return an empty array
1638 		}
1639 		return new IPackageFragmentRoot[] {};
1640 	}
1641 	/**
1642 	 * @see IJavaProject#findType(String)
1643 	 */
1644 	@Override
findType(String fullyQualifiedName)1645 	public IType findType(String fullyQualifiedName) throws JavaModelException {
1646 		return findType(fullyQualifiedName, DefaultWorkingCopyOwner.PRIMARY);
1647 	}
1648 	/**
1649 	 * @see IJavaProject#findType(String, IProgressMonitor)
1650 	 */
1651 	@Override
findType(String fullyQualifiedName, IProgressMonitor progressMonitor)1652 	public IType findType(String fullyQualifiedName, IProgressMonitor progressMonitor) throws JavaModelException {
1653 		return findType(fullyQualifiedName, DefaultWorkingCopyOwner.PRIMARY, progressMonitor);
1654 	}
1655 
1656 	/*
1657 	 * Internal findType with instanciated name lookup
1658 	 */
findType(String fullyQualifiedName, NameLookup lookup, boolean considerSecondaryTypes, IProgressMonitor progressMonitor)1659 	IType findType(String fullyQualifiedName, NameLookup lookup, boolean considerSecondaryTypes, IProgressMonitor progressMonitor) throws JavaModelException {
1660 		NameLookup.Answer answer = lookup.findType(
1661 			fullyQualifiedName,
1662 			false,
1663 			NameLookup.ACCEPT_ALL,
1664 			considerSecondaryTypes,
1665 			true, /* wait for indexes (only if consider secondary types)*/
1666 			false/*don't check restrictions*/,
1667 			progressMonitor);
1668 		if (answer == null) {
1669 			// try to find enclosing type
1670 			int lastDot = fullyQualifiedName.lastIndexOf('.');
1671 			if (lastDot == -1) return null;
1672 			IType type = findType(fullyQualifiedName.substring(0, lastDot), lookup, considerSecondaryTypes, progressMonitor);
1673 			if (type != null) {
1674 				type = type.getType(fullyQualifiedName.substring(lastDot+1));
1675 				if (!type.exists()) {
1676 					return null;
1677 				}
1678 			}
1679 			return type;
1680 		}
1681 		return answer.type;
1682 	}
1683 	/**
1684 	 * @see IJavaProject#findType(String, String)
1685 	 */
1686 	@Override
findType(String packageName, String typeQualifiedName)1687 	public IType findType(String packageName, String typeQualifiedName) throws JavaModelException {
1688 		return findType(packageName, typeQualifiedName, DefaultWorkingCopyOwner.PRIMARY);
1689 	}
1690 	/**
1691 	 * @see IJavaProject#findType(String, String, IProgressMonitor)
1692 	 */
1693 	@Override
findType(String packageName, String typeQualifiedName, IProgressMonitor progressMonitor)1694 	public IType findType(String packageName, String typeQualifiedName, IProgressMonitor progressMonitor) throws JavaModelException {
1695 		return findType(packageName, typeQualifiedName, DefaultWorkingCopyOwner.PRIMARY, progressMonitor);
1696 	}
1697 	/*
1698 	 * Internal findType with instanciated name lookup
1699 	 */
findType(String packageName, String typeQualifiedName, NameLookup lookup, boolean considerSecondaryTypes, IProgressMonitor progressMonitor)1700 	IType findType(String packageName, String typeQualifiedName, NameLookup lookup, boolean considerSecondaryTypes, IProgressMonitor progressMonitor) throws JavaModelException {
1701 		NameLookup.Answer answer = lookup.findType(
1702 			typeQualifiedName,
1703 			packageName,
1704 			false,
1705 			NameLookup.ACCEPT_ALL,
1706 			considerSecondaryTypes,
1707 			true, // wait for indexes (in case we need to consider secondary types)
1708 			false/*don't check restrictions*/,
1709 			progressMonitor);
1710 		return answer == null ? null : answer.type;
1711 	}
1712 	/**
1713 	 * @see IJavaProject#findType(String, String, WorkingCopyOwner)
1714 	 */
1715 	@Override
findType(String packageName, String typeQualifiedName, WorkingCopyOwner owner)1716 	public IType findType(String packageName, String typeQualifiedName, WorkingCopyOwner owner) throws JavaModelException {
1717 		NameLookup lookup = newNameLookup(owner);
1718 		return findType(
1719 			packageName,
1720 			typeQualifiedName,
1721 			lookup,
1722 			false, // do not consider secondary types
1723 			null);
1724 	}
1725 
1726 	/**
1727 	 * @see IJavaProject#findType(String, String, WorkingCopyOwner, IProgressMonitor)
1728 	 */
1729 	@Override
findType(String packageName, String typeQualifiedName, WorkingCopyOwner owner, IProgressMonitor progressMonitor)1730 	public IType findType(String packageName, String typeQualifiedName, WorkingCopyOwner owner, IProgressMonitor progressMonitor) throws JavaModelException {
1731 		NameLookup lookup = newNameLookup(owner);
1732 		return findType(
1733 			packageName,
1734 			typeQualifiedName,
1735 			lookup,
1736 			true, // consider secondary types
1737 			progressMonitor);
1738 	}
1739 
1740 	/**
1741 	 * @see IJavaProject#findType(String, WorkingCopyOwner)
1742 	 */
1743 	@Override
findType(String fullyQualifiedName, WorkingCopyOwner owner)1744 	public IType findType(String fullyQualifiedName, WorkingCopyOwner owner) throws JavaModelException {
1745 		NameLookup lookup = newNameLookup(owner);
1746 		return findType(fullyQualifiedName, lookup, false, null);
1747 	}
1748 
1749 	/**
1750 	 * @see IJavaProject#findType(String, WorkingCopyOwner, IProgressMonitor)
1751 	 */
1752 	@Override
findType(String fullyQualifiedName, WorkingCopyOwner owner, IProgressMonitor progressMonitor)1753 	public IType findType(String fullyQualifiedName, WorkingCopyOwner owner, IProgressMonitor progressMonitor) throws JavaModelException {
1754 		NameLookup lookup = newNameLookup(owner);
1755 		return findType(fullyQualifiedName, lookup, true, progressMonitor);
1756 	}
1757 
1758 	@Override
findModule(String moduleName, WorkingCopyOwner owner)1759 	public IModuleDescription findModule(String moduleName, WorkingCopyOwner owner) throws JavaModelException {
1760 		NameLookup lookup = newNameLookup(owner);
1761 		return findModule(moduleName, lookup);
1762 	}
1763 
1764 	/*
1765 	 * Internal findModule with instantiated name lookup
1766 	 */
findModule(String moduleName, NameLookup lookup)1767 	IModuleDescription findModule(String moduleName, NameLookup lookup) throws JavaModelException {
1768 		NameLookup.Answer answer = lookup.findModule(moduleName.toCharArray());
1769 		if (answer != null)
1770 			return answer.module;
1771 		return null;
1772 	}
1773 
1774 	/**
1775 	 * Remove all markers denoting classpath problems
1776 	 */ //TODO (philippe) should improve to use a bitmask instead of booleans (CYCLE, FORMAT, VALID)
flushClasspathProblemMarkers(boolean flushCycleMarkers, boolean flushClasspathFormatMarkers, boolean flushOverlappingOutputMarkers)1777 	protected void flushClasspathProblemMarkers(boolean flushCycleMarkers, boolean flushClasspathFormatMarkers, boolean flushOverlappingOutputMarkers) {
1778 		try {
1779 			if (this.project.isAccessible()) {
1780 				IMarker[] markers = this.project.findMarkers(IJavaModelMarker.BUILDPATH_PROBLEM_MARKER, false, IResource.DEPTH_ZERO);
1781 				for (int i = 0, length = markers.length; i < length; i++) {
1782 					IMarker marker = markers[i];
1783 					if (flushCycleMarkers && flushClasspathFormatMarkers && flushOverlappingOutputMarkers) {
1784 						marker.delete();
1785 					} else {
1786 						String cycleAttr = (String)marker.getAttribute(IJavaModelMarker.CYCLE_DETECTED);
1787 						String classpathFileFormatAttr =  (String)marker.getAttribute(IJavaModelMarker.CLASSPATH_FILE_FORMAT);
1788 						String overlappingOutputAttr = (String) marker.getAttribute(IJavaModelMarker.OUTPUT_OVERLAPPING_SOURCE);
1789 						if ((flushCycleMarkers == (cycleAttr != null && cycleAttr.equals("true"))) //$NON-NLS-1$
1790 							&& (flushOverlappingOutputMarkers == (overlappingOutputAttr != null && overlappingOutputAttr.equals("true"))) //$NON-NLS-1$
1791 							&& (flushClasspathFormatMarkers == (classpathFileFormatAttr != null && classpathFileFormatAttr.equals("true")))){ //$NON-NLS-1$
1792 							marker.delete();
1793 						}
1794 					}
1795 				}
1796 			}
1797 		} catch (CoreException e) {
1798 			// could not flush markers: not much we can do
1799 			if (JavaModelManager.VERBOSE) {
1800 				e.printStackTrace();
1801 			}
1802 		}
1803 	}
1804 
1805 	/**
1806 	 * Returns the set of patterns corresponding to this project visibility given rules
1807 	 * @return an array of IPath or null if none
1808 	 */
getAccessRestrictions(String optionName)1809 	public IPath[] getAccessRestrictions(String optionName) {
1810 		String sequence = getOption(optionName, true); // inherit from workspace
1811 		if (sequence == null || sequence.length() == 0) return null;
1812 		IPath[] rules = null;
1813 		char[][] patterns = CharOperation.splitOn('|', sequence.toCharArray());
1814 		int patternCount;
1815 		if ((patternCount  = patterns.length) > 0) {
1816 			rules = new IPath[patternCount];
1817 			for (int j = 0; j < patterns.length; j++){
1818 				rules[j] = new Path(new String(patterns[j]));
1819 			}
1820 		}
1821 		return rules;
1822 	}
1823 
1824 	/**
1825 	 * @see IJavaProject
1826 	 */
1827 	@Override
getAllPackageFragmentRoots()1828 	public IPackageFragmentRoot[] getAllPackageFragmentRoots()
1829 		throws JavaModelException {
1830 		return getAllPackageFragmentRoots(null /*no reverse map*/, false);
1831 	}
1832 
1833 	@Deprecated
getAllPackageFragmentRoots(Map rootToResolvedEntries)1834 	public IPackageFragmentRoot[] getAllPackageFragmentRoots(Map rootToResolvedEntries) throws JavaModelException {
1835 		return getAllPackageFragmentRoots(rootToResolvedEntries, false);
1836 	}
getAllPackageFragmentRoots(Map rootToResolvedEntries, boolean excludeTestCode)1837 	public IPackageFragmentRoot[] getAllPackageFragmentRoots(Map rootToResolvedEntries, boolean excludeTestCode) throws JavaModelException {
1838 
1839 		return computePackageFragmentRoots(getResolvedClasspath(), true/*retrieveExportedRoots*/, true/*filterModuleRoots*/, rootToResolvedEntries, excludeTestCode);
1840 	}
1841 
1842 	@Override
getClasspathEntryFor(IPath path)1843 	public IClasspathEntry getClasspathEntryFor(IPath path) throws JavaModelException {
1844 		getResolvedClasspath(); // force resolution
1845 		PerProjectInfo perProjectInfo = getPerProjectInfo();
1846 		if (perProjectInfo == null)
1847 			return null;
1848 		Map rootPathToResolvedEntries = perProjectInfo.rootPathToResolvedEntries;
1849 		if (rootPathToResolvedEntries == null)
1850 			return null;
1851 		IClasspathEntry classpathEntry = (IClasspathEntry) rootPathToResolvedEntries.get(path);
1852 		if (classpathEntry == null) {
1853 			path = getProject().getWorkspace().getRoot().getLocation().append(path);
1854 			classpathEntry = (IClasspathEntry) rootPathToResolvedEntries.get(path);
1855 		}
1856 		return classpathEntry;
1857 	}
1858 
1859 	/*
1860 	 * Returns the cycle marker associated with this project or null if none.
1861 	 */
getCycleMarker()1862 	public IMarker getCycleMarker(){
1863 		try {
1864 			if (this.project.isAccessible()) {
1865 				IMarker[] markers = this.project.findMarkers(IJavaModelMarker.BUILDPATH_PROBLEM_MARKER, false, IResource.DEPTH_ZERO);
1866 				for (int i = 0, length = markers.length; i < length; i++) {
1867 					IMarker marker = markers[i];
1868 					String cycleAttr = (String)marker.getAttribute(IJavaModelMarker.CYCLE_DETECTED);
1869 					if (cycleAttr != null && cycleAttr.equals("true")){ //$NON-NLS-1$
1870 						return marker;
1871 					}
1872 				}
1873 			}
1874 		} catch (CoreException e) {
1875 			// could not get markers: return null
1876 		}
1877 		return null;
1878 	}
1879 
1880 	/**
1881 	 * Returns the project custom preference pool.
1882 	 * Project preferences may include custom encoding.
1883 	 * @return IEclipsePreferences or <code>null</code> if the project
1884 	 * 	does not have a java nature.
1885 	 */
getEclipsePreferences()1886 	public IEclipsePreferences getEclipsePreferences() {
1887 		if (!JavaProject.hasJavaNature(this.project)) return null;
1888 		// Get cached preferences if exist
1889 		JavaModelManager.PerProjectInfo perProjectInfo = JavaModelManager.getJavaModelManager().getPerProjectInfo(this.project, true);
1890 		if (perProjectInfo.preferences != null) return perProjectInfo.preferences;
1891 		// Init project preferences
1892 		IScopeContext context = new ProjectScope(getProject());
1893 		final IEclipsePreferences eclipsePreferences = context.getNode(JavaCore.PLUGIN_ID);
1894 		updatePreferences(eclipsePreferences);
1895 		perProjectInfo.preferences = eclipsePreferences;
1896 
1897 		// Listen to new preferences node
1898 		final IEclipsePreferences eclipseParentPreferences = (IEclipsePreferences) eclipsePreferences.parent();
1899 		if (eclipseParentPreferences != null) {
1900 			if (this.preferencesNodeListener != null) {
1901 				eclipseParentPreferences.removeNodeChangeListener(this.preferencesNodeListener);
1902 			}
1903 			this.preferencesNodeListener = new IEclipsePreferences.INodeChangeListener() {
1904 				@Override
1905 				public void added(IEclipsePreferences.NodeChangeEvent event) {
1906 					// do nothing
1907 				}
1908 				@Override
1909 				public void removed(IEclipsePreferences.NodeChangeEvent event) {
1910 					if (event.getChild() == eclipsePreferences) {
1911 						JavaModelManager.getJavaModelManager().resetProjectPreferences(JavaProject.this);
1912 					}
1913 				}
1914 			};
1915 			eclipseParentPreferences.addNodeChangeListener(this.preferencesNodeListener);
1916 		}
1917 
1918 		// Listen to preferences changes
1919 		if (this.preferencesChangeListener != null) {
1920 			eclipsePreferences.removePreferenceChangeListener(this.preferencesChangeListener);
1921 		}
1922 		this.preferencesChangeListener = new IEclipsePreferences.IPreferenceChangeListener() {
1923 			@Override
1924 			public void preferenceChange(IEclipsePreferences.PreferenceChangeEvent event) {
1925 				String propertyName = event.getKey();
1926 				JavaModelManager manager = JavaModelManager.getJavaModelManager();
1927 				if (propertyName.startsWith(JavaCore.PLUGIN_ID)) {
1928 					if (propertyName.equals(JavaCore.CORE_JAVA_BUILD_CLEAN_OUTPUT_FOLDER) ||
1929 						propertyName.equals(JavaCore.CORE_JAVA_BUILD_RESOURCE_COPY_FILTER) ||
1930 						propertyName.equals(JavaCore.CORE_JAVA_BUILD_DUPLICATE_RESOURCE) ||
1931 						propertyName.equals(JavaCore.CORE_JAVA_BUILD_RECREATE_MODIFIED_CLASS_FILES_IN_OUTPUT_FOLDER) ||
1932 						propertyName.equals(JavaCore.CORE_JAVA_BUILD_INVALID_CLASSPATH) ||
1933 						propertyName.equals(JavaCore.CORE_ENABLE_CLASSPATH_EXCLUSION_PATTERNS) ||
1934 						propertyName.equals(JavaCore.CORE_ENABLE_CLASSPATH_MULTIPLE_OUTPUT_LOCATIONS) ||
1935 						propertyName.equals(JavaCore.CORE_INCOMPLETE_CLASSPATH) ||
1936 						propertyName.equals(JavaCore.CORE_CIRCULAR_CLASSPATH) ||
1937 						propertyName.equals(JavaCore.CORE_OUTPUT_LOCATION_OVERLAPPING_ANOTHER_SOURCE) ||
1938 						propertyName.equals(JavaCore.CORE_INCOMPATIBLE_JDK_LEVEL) ||
1939 						propertyName.equals(JavaCore.CORE_MAIN_ONLY_PROJECT_HAS_TEST_ONLY_DEPENDENCY) ||
1940 						propertyName.equals(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM))
1941 					{
1942 						manager.deltaState.addClasspathValidation(JavaProject.this);
1943 					}
1944 					checkExpireModule(propertyName, event.getOldValue(), event.getNewValue());
1945 					manager.resetProjectOptions(JavaProject.this);
1946 					JavaProject.this.resetCaches(); // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=233568
1947 				}
1948 			}
1949 
1950 			void checkExpireModule(String propertyName, Object oldValue, Object newValue) {
1951 				if (propertyName.equals(JavaCore.COMPILER_SOURCE)) {
1952 					if (oldValue instanceof String && newValue instanceof String
1953 						&& CompilerOptions.versionToJdkLevel((String) oldValue) >= ClassFileConstants.JDK9
1954 						&& CompilerOptions.versionToJdkLevel((String) newValue) < ClassFileConstants.JDK9)
1955 					{
1956 						try {
1957 							// this is a change from modular to non-modular, forget the module if any:
1958 							IModuleDescription module = getModuleDescription();
1959 							if (module != null)
1960 								((JavaElement)module).close();
1961 						} catch (JavaModelException e) {
1962 							// ignore
1963 						}
1964 					}
1965 				}
1966 			}
1967 		};
1968 		eclipsePreferences.addPreferenceChangeListener(this.preferencesChangeListener);
1969 		return eclipsePreferences;
1970 	}
1971 
1972 	@Override
getElementName()1973 	public String getElementName() {
1974 		return this.project.getName();
1975 	}
1976 
1977 	/**
1978 	 * @see IJavaElement
1979 	 */
1980 	@Override
getElementType()1981 	public int getElementType() {
1982 		return JAVA_PROJECT;
1983 	}
1984 
1985 	/**
1986 	 * This is a helper method returning the expanded classpath for the project, as a list of classpath entries,
1987 	 * where all classpath variable entries have been resolved and substituted with their final target entries.
1988 	 * All project exports have been appended to project entries.
1989 	 * @return IClasspathEntry[]
1990 	 * @throws JavaModelException
1991 	 */
getExpandedClasspath()1992 	public IClasspathEntry[] getExpandedClasspath()	throws JavaModelException {
1993 		return getExpandedClasspath(false);
1994 	}
getExpandedClasspath(boolean excludeTestCode)1995 	public IClasspathEntry[] getExpandedClasspath(boolean excludeTestCode)	throws JavaModelException {
1996 
1997 			ArrayList<ClasspathEntry> accumulatedEntries = new ArrayList<>();
1998 			HashMap<String,Boolean> rootIDs = new HashMap<>(5);
1999 			rootIDs.put(this.rootID(), excludeTestCode);
2000 			computeExpandedClasspath(null, rootIDs, accumulatedEntries, excludeTestCode);
2001 
2002 			IClasspathEntry[] expandedPath = new IClasspathEntry[accumulatedEntries.size()];
2003 			accumulatedEntries.toArray(expandedPath);
2004 
2005 			return expandedPath;
2006 	}
2007 
2008 	/**
2009 	 * The path is known to match a source/library folder entry.
2010 	 * @param path IPath
2011 	 * @return IPackageFragmentRoot
2012 	 */
getFolderPackageFragmentRoot(IPath path)2013 	public IPackageFragmentRoot getFolderPackageFragmentRoot(IPath path) {
2014 		if (path.segmentCount() == 1) { // default project root
2015 			return getPackageFragmentRoot(this.project);
2016 		}
2017 		return getPackageFragmentRoot(this.project.getWorkspace().getRoot().getFolder(path));
2018 	}
2019 
2020 	/*
2021 	 * @see JavaElement
2022 	 */
2023 	@Override
getHandleFromMemento(String token, MementoTokenizer memento, WorkingCopyOwner owner)2024 	public IJavaElement getHandleFromMemento(String token, MementoTokenizer memento, WorkingCopyOwner owner) {
2025 		String mod = null;
2026 		switch (token.charAt(0)) {
2027 			case JEM_PACKAGEFRAGMENTROOT:
2028 				String rootPath = IPackageFragmentRoot.DEFAULT_PACKAGEROOT_PATH;
2029 				token = null;
2030 				List<IClasspathAttribute> attributes = new ArrayList<>();
2031 				while (memento.hasMoreTokens()) {
2032 					token = memento.nextToken();
2033 					// https://bugs.eclipse.org/bugs/show_bug.cgi?id=331821
2034 					if (token == MementoTokenizer.PACKAGEFRAGMENT || token == MementoTokenizer.COUNT) {
2035 						break;
2036 					} else if (token == MementoTokenizer.MODULE) {
2037 						if (memento.hasMoreTokens()) {
2038 							token = memento.nextToken();
2039 							if (token != null) {
2040 								mod = token;
2041 
2042 							}
2043 						}
2044 						continue;
2045 					} else if (token == MementoTokenizer.CLASSPATH_ATTRIBUTE) {
2046 						// PFR memento is optionally trailed by all extra classpath attributes ("=/name=/value=/"):
2047 						String name = memento.getStringDelimitedBy(MementoTokenizer.CLASSPATH_ATTRIBUTE);
2048 						String value = memento.getStringDelimitedBy(MementoTokenizer.CLASSPATH_ATTRIBUTE);
2049 						attributes.add(new ClasspathAttribute(name, value));
2050 						token = null; // consumed
2051 						continue;
2052 					}
2053 					rootPath += token;
2054 				}
2055 				IClasspathAttribute[] attributesArray = null;
2056 				if (!attributes.isEmpty())
2057 					attributesArray = attributes.toArray(new IClasspathAttribute[attributes.size()]);
2058 				JavaElement root = (mod == null) ?
2059 						(JavaElement)getPackageFragmentRoot(new Path(rootPath), attributesArray) :
2060 							new JrtPackageFragmentRoot(new Path(rootPath), mod, this, attributesArray);
2061 				if (token != null && (token.charAt(0) == JEM_PACKAGEFRAGMENT)) {
2062 					return root.getHandleFromMemento(token, memento, owner);
2063 				} else {
2064 					return root.getHandleFromMemento(memento, owner);
2065 				}
2066 		}
2067 		return null;
2068 	}
2069 
2070 	/**
2071 	 * Returns the <code>char</code> that marks the start of this handles
2072 	 * contribution to a memento.
2073 	 */
2074 	@Override
getHandleMementoDelimiter()2075 	protected char getHandleMementoDelimiter() {
2076 
2077 		return JEM_JAVAPROJECT;
2078 	}
2079 
2080 	/**
2081 	 * Find the specific Java command amongst the given build spec
2082 	 * and return its index or -1 if not found.
2083 	 */
getJavaCommandIndex(ICommand[] buildSpec)2084 	private int getJavaCommandIndex(ICommand[] buildSpec) {
2085 
2086 		for (int i = 0; i < buildSpec.length; ++i) {
2087 			if (buildSpec[i].getBuilderName().equals(JavaCore.BUILDER_ID)) {
2088 				return i;
2089 			}
2090 		}
2091 		return -1;
2092 	}
2093 
2094 	/**
2095 	 * Convenience method that returns the specific type of info for a Java project.
2096 	 */
getJavaProjectElementInfo()2097 	protected JavaProjectElementInfo getJavaProjectElementInfo()
2098 		throws JavaModelException {
2099 
2100 		return (JavaProjectElementInfo) getElementInfo();
2101 	}
2102 
2103 	/**
2104 	 * Returns an array of non-java resources contained in the receiver.
2105 	 */
2106 	@Override
getNonJavaResources()2107 	public Object[] getNonJavaResources() throws JavaModelException {
2108 
2109 		return ((JavaProjectElementInfo) getElementInfo()).getNonJavaResources(this);
2110 	}
2111 
2112 	/**
2113 	 * @see org.eclipse.jdt.core.IJavaProject#getOption(String, boolean)
2114 	 */
2115 	@Override
getOption(String optionName, boolean inheritJavaCoreOptions)2116 	public String getOption(String optionName, boolean inheritJavaCoreOptions) {
2117 		return JavaModelManager.getJavaModelManager().getOption(optionName, inheritJavaCoreOptions, getEclipsePreferences());
2118 	}
2119 
2120 	/**
2121 	 * @see org.eclipse.jdt.core.IJavaProject#getOptions(boolean)
2122 	 */
2123 	@Override
getOptions(boolean inheritJavaCoreOptions)2124 	public Map<String, String> getOptions(boolean inheritJavaCoreOptions) {
2125 
2126 		// initialize to the defaults from JavaCore options pool
2127 		Map<String, String> options = inheritJavaCoreOptions ? JavaCore.getOptions() : new Hashtable<String, String>(5);
2128 
2129 		// Get project specific options
2130 		JavaModelManager.PerProjectInfo perProjectInfo = null;
2131 		Hashtable projectOptions = null;
2132 		JavaModelManager javaModelManager = JavaModelManager.getJavaModelManager();
2133 		HashSet optionNames = javaModelManager.optionNames;
2134 		try {
2135 			perProjectInfo = getPerProjectInfo();
2136 			projectOptions = perProjectInfo.options;
2137 			if (projectOptions == null) {
2138 				// get eclipse preferences
2139 				IEclipsePreferences projectPreferences= getEclipsePreferences();
2140 				if (projectPreferences == null) return options; // cannot do better (non-Java project)
2141 				// create project options
2142 				String[] propertyNames = projectPreferences.keys();
2143 				projectOptions = new Hashtable(propertyNames.length);
2144 				for (int i = 0; i < propertyNames.length; i++){
2145 					String propertyName = propertyNames[i];
2146 					String value = projectPreferences.get(propertyName, null);
2147 					if (value != null) {
2148 						value = value.trim();
2149 						// Keep the option value, even if it's deprecated
2150 						// see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=324987
2151 						projectOptions.put(propertyName, value);
2152 						if (!optionNames.contains(propertyName)) {
2153 							// try to migrate deprecated options
2154 							String[] compatibleOptions = javaModelManager.deprecatedOptions.get(propertyName);
2155 							if (compatibleOptions != null) {
2156 								for (int co=0, length=compatibleOptions.length; co < length; co++) {
2157 									String compatibleOption = compatibleOptions[co];
2158 									if (!projectOptions.containsKey(compatibleOption))
2159 										projectOptions.put(compatibleOption, value);
2160 								}
2161 							}
2162 						}
2163 					}
2164 				}
2165 				// cache project options
2166 				perProjectInfo.options = projectOptions;
2167 			}
2168 		} catch (JavaModelException | BackingStoreException e) {
2169 			projectOptions = new Hashtable();
2170 		}
2171 
2172 		// Inherit from JavaCore options if specified
2173 		if (inheritJavaCoreOptions) {
2174 			Iterator propertyNames = projectOptions.entrySet().iterator();
2175 			while (propertyNames.hasNext()) {
2176 				Map.Entry entry = (Map.Entry) propertyNames.next();
2177 				String propertyName = (String) entry.getKey();
2178 				String propertyValue = (String) entry.getValue();
2179 				if (propertyValue != null && javaModelManager.knowsOption(propertyName)){
2180 					options.put(propertyName, propertyValue.trim());
2181 				}
2182 			}
2183 			Util.fixTaskTags(options);
2184 			return options;
2185 		}
2186 		Util.fixTaskTags(projectOptions);
2187 		return projectOptions;
2188 	}
2189 
2190 	/**
2191 	 * @see IJavaProject
2192 	 */
2193 	@Override
getOutputLocation()2194 	public IPath getOutputLocation() throws JavaModelException {
2195 		// Do not create marker while getting output location
2196 		JavaModelManager.PerProjectInfo perProjectInfo = getPerProjectInfo();
2197 		IPath outputLocation = perProjectInfo.outputLocation;
2198 		if (outputLocation != null) return outputLocation;
2199 
2200 		// force to read classpath - will position output location as well
2201 		getRawClasspath();
2202 
2203 		outputLocation = perProjectInfo.outputLocation;
2204 		if (outputLocation == null) {
2205 			return defaultOutputLocation();
2206 		}
2207 		return outputLocation;
2208 	}
2209 
2210 	/**
2211 	 * @param path IPath
2212 	 * @return A handle to the package fragment root identified by the given path.
2213 	 * This method is handle-only and the element may or may not exist. Returns
2214 	 * <code>null</code> if unable to generate a handle from the path (for example,
2215 	 * an absolute path that has less than 1 segment. The path may be relative or
2216 	 * absolute.
2217 	 */
getPackageFragmentRoot(IPath path, IClasspathAttribute[] extraAttributes)2218 	public IPackageFragmentRoot getPackageFragmentRoot(IPath path, IClasspathAttribute[] extraAttributes) {
2219 		if (!path.isAbsolute()) {
2220 			path = getPath().append(path);
2221 		}
2222 		int segmentCount = path.segmentCount();
2223 		if (segmentCount == 0) {
2224 			return null;
2225 		}
2226 		if (path.getDevice() != null || JavaModel.getExternalTarget(path, true/*check existence*/) != null) {
2227 			// external path
2228 			return getPackageFragmentRoot0(path, extraAttributes);
2229 		}
2230 		IWorkspaceRoot workspaceRoot = this.project.getWorkspace().getRoot();
2231 		IResource resource = workspaceRoot.findMember(path);
2232 		if (resource == null) {
2233 			// resource doesn't exist in workspace
2234 			if (path.getFileExtension() != null) {
2235 				if (!workspaceRoot.getProject(path.segment(0)).exists()) {
2236 					// assume it is an external ZIP archive
2237 					return getPackageFragmentRoot0(path, extraAttributes);
2238 				} else {
2239 					// assume it is an internal ZIP archive
2240 					resource = workspaceRoot.getFile(path);
2241 				}
2242 			} else if (segmentCount == 1) {
2243 				// assume it is a project
2244 				String projectName = path.segment(0);
2245 				if (getElementName().equals(projectName)) { // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=75814
2246 					// default root
2247 					resource = this.project;
2248 				} else {
2249 					// lib being another project
2250 					resource = workspaceRoot.getProject(projectName);
2251 				}
2252 			} else {
2253 				// assume it is an internal folder
2254 				resource = workspaceRoot.getFolder(path);
2255 			}
2256 		}
2257 		return getPackageFragmentRoot(resource, null, extraAttributes);
2258 	}
2259 
2260 	/**
2261 	 * @see IJavaProject
2262 	 */
2263 	@Override
getPackageFragmentRoot(IResource resource)2264 	public IPackageFragmentRoot getPackageFragmentRoot(IResource resource) {
2265 		return getPackageFragmentRoot(resource, null/*no entry path*/, null/*no extra attributes*/);
2266 	}
2267 
getPackageFragmentRoot(IResource resource, IPath entryPath, IClasspathAttribute[] extraAttributes)2268 	public IPackageFragmentRoot getPackageFragmentRoot(IResource resource, IPath entryPath, IClasspathAttribute[] extraAttributes) {
2269 		switch (resource.getType()) {
2270 			case IResource.FILE:
2271 				return new JarPackageFragmentRoot(resource, resource.getFullPath(), this, extraAttributes);
2272 			case IResource.FOLDER:
2273 				if (ExternalFoldersManager.isInternalPathForExternalFolder(resource.getFullPath()))
2274 					return new ExternalPackageFragmentRoot(resource, entryPath, this);
2275 				return new PackageFragmentRoot(resource, this);
2276 			case IResource.PROJECT:
2277 				return new PackageFragmentRoot(resource, this);
2278 			default:
2279 				return null;
2280 		}
2281 	}
2282 
2283 	/**
2284 	 * @see IJavaProject
2285 	 */
2286 	@Override
getPackageFragmentRoot(String externalLibraryPath)2287 	public IPackageFragmentRoot getPackageFragmentRoot(String externalLibraryPath) {
2288 		return getPackageFragmentRoot0(JavaProject.canonicalizedPath(new Path(externalLibraryPath)), null);
2289 	}
2290 
2291 	/*
2292 	 * no path canonicalization
2293 	 */
getPackageFragmentRoot0(IPath externalLibraryPath, IClasspathAttribute[] extraAttributes)2294 	public IPackageFragmentRoot getPackageFragmentRoot0(IPath externalLibraryPath, IClasspathAttribute[] extraAttributes) {
2295 		IFolder linkedFolder = JavaModelManager.getExternalManager().getFolder(externalLibraryPath);
2296 		if (linkedFolder != null)
2297 			return new ExternalPackageFragmentRoot(linkedFolder, externalLibraryPath, this);
2298 		if (JavaModelManager.isJrt(externalLibraryPath)) {
2299 			return this.new JImageModuleFragmentBridge(externalLibraryPath, extraAttributes);
2300 		}
2301 		Object target = JavaModel.getTarget(externalLibraryPath, true/*check existency*/);
2302 		if (target instanceof File && JavaModel.isFile(target)) {
2303 			if (JavaModel.isJmod((File) target)) {
2304 				return new JModPackageFragmentRoot(externalLibraryPath, this, extraAttributes);
2305 			}
2306 		}
2307 		return new JarPackageFragmentRoot(null, externalLibraryPath, this, extraAttributes);
2308 	}
2309 
2310 	/**
2311 	 * @see IJavaProject
2312 	 */
2313 	@Override
getPackageFragmentRoots()2314 	public IPackageFragmentRoot[] getPackageFragmentRoots()
2315 		throws JavaModelException {
2316 
2317 		Object[] children;
2318 		int length;
2319 		IPackageFragmentRoot[] roots;
2320 
2321 		System.arraycopy(
2322 			children = getChildren(),
2323 			0,
2324 			roots = new IPackageFragmentRoot[length = children.length],
2325 			0,
2326 			length);
2327 
2328 		return roots;
2329 	}
2330 
2331 	/**
2332 	 * @see IJavaProject
2333 	 * @deprecated
2334 	 */
2335 	@Override
getPackageFragmentRoots(IClasspathEntry entry)2336 	public IPackageFragmentRoot[] getPackageFragmentRoots(IClasspathEntry entry) {
2337 		return findPackageFragmentRoots(entry);
2338 	}
2339 
2340 	/**
2341 	 * @see IJavaProject
2342 	 */
2343 	@Override
getPackageFragments()2344 	public IPackageFragment[] getPackageFragments() throws JavaModelException {
2345 
2346 		IPackageFragmentRoot[] roots = getPackageFragmentRoots();
2347 		return getPackageFragmentsInRoots(roots);
2348 	}
2349 
2350 	/**
2351 	 * Returns all the package fragments found in the specified
2352 	 * package fragment roots.
2353 	 * @param roots IPackageFragmentRoot[]
2354 	 * @return IPackageFragment[]
2355 	 */
getPackageFragmentsInRoots(IPackageFragmentRoot[] roots)2356 	public IPackageFragment[] getPackageFragmentsInRoots(IPackageFragmentRoot[] roots) {
2357 
2358 		ArrayList frags = new ArrayList();
2359 		for (int i = 0; i < roots.length; i++) {
2360 			IPackageFragmentRoot root = roots[i];
2361 			try {
2362 				IJavaElement[] rootFragments = root.getChildren();
2363 				Collections.addAll(frags, rootFragments);
2364 			} catch (JavaModelException e) {
2365 				// do nothing
2366 			}
2367 		}
2368 		IPackageFragment[] fragments = new IPackageFragment[frags.size()];
2369 		frags.toArray(fragments);
2370 		return fragments;
2371 	}
2372 
2373 	/**
2374 	 * @see IJavaElement
2375 	 */
2376 	@Override
getPath()2377 	public IPath getPath() {
2378 		return this.project.getFullPath();
2379 	}
2380 
getPerProjectInfo()2381 	public JavaModelManager.PerProjectInfo getPerProjectInfo() throws JavaModelException {
2382 		return JavaModelManager.getJavaModelManager().getPerProjectInfoCheckExistence(this.project);
2383 	}
2384 
getPluginWorkingLocation()2385 	private IPath getPluginWorkingLocation() {
2386 		return this.project.getWorkingLocation(JavaCore.PLUGIN_ID);
2387 	}
2388 
2389 	/**
2390 	 * @see IJavaProject#getProject()
2391 	 */
2392 	@Override
getProject()2393 	public IProject getProject() {
2394 		return this.project;
2395 	}
2396 
2397 	@Deprecated
getProjectCache()2398 	public ProjectCache getProjectCache() throws JavaModelException {
2399 		return getProjectCache(false);
2400 	}
2401 
getProjectCache(boolean excludeTestCode)2402 	public ProjectCache getProjectCache(boolean excludeTestCode) throws JavaModelException {
2403 		return ((JavaProjectElementInfo) getElementInfo()).getProjectCache(this, excludeTestCode);
2404 	}
2405 
2406 	/**
2407 	 * @see IJavaProject
2408 	 */
2409 	@Override
getRawClasspath()2410 	public IClasspathEntry[] getRawClasspath() throws JavaModelException {
2411 		JavaModelManager.PerProjectInfo perProjectInfo = getPerProjectInfo();
2412 		IClasspathEntry[] classpath = perProjectInfo.rawClasspath;
2413 		if (classpath != null) return classpath;
2414 
2415 		classpath = perProjectInfo.readAndCacheClasspath(this)[0];
2416 
2417 		if (classpath == JavaProject.INVALID_CLASSPATH)
2418 			return defaultClasspath();
2419 
2420 		return classpath;
2421 	}
2422 
2423 	/**
2424 	 * @see IJavaProject
2425 	 */
2426 	@Override
getReferencedClasspathEntries()2427 	public IClasspathEntry[] getReferencedClasspathEntries() throws JavaModelException {
2428 		return getPerProjectInfo().referencedEntries;
2429 	}
2430 
2431 	/**
2432 	 * @see IJavaProject#getRequiredProjectNames()
2433 	 */
2434 	@Override
getRequiredProjectNames()2435 	public String[] getRequiredProjectNames() throws JavaModelException {
2436 
2437 		return projectPrerequisites(getResolvedClasspath());
2438 	}
2439 
getResolvedClasspath()2440 	public IClasspathEntry[] getResolvedClasspath() throws JavaModelException {
2441 		PerProjectInfo perProjectInfo = getPerProjectInfo();
2442 		IClasspathEntry[] resolvedClasspath = perProjectInfo.getResolvedClasspath();
2443 		if (resolvedClasspath == null) {
2444 			resolveClasspath(perProjectInfo, false/*don't use previous session values*/, true/*add classpath change*/);
2445 			resolvedClasspath = perProjectInfo.getResolvedClasspath();
2446 			if (resolvedClasspath == null) {
2447 				// another thread reset the resolved classpath, use a temporary PerProjectInfo
2448 				PerProjectInfo temporaryInfo = newTemporaryInfo();
2449 				resolveClasspath(temporaryInfo, false/*don't use previous session values*/, true/*add classpath change*/);
2450 				resolvedClasspath = temporaryInfo.getResolvedClasspath();
2451 			}
2452 		}
2453 		return resolvedClasspath;
2454 	}
2455 
2456 	/**
2457 	 * @see IJavaProject
2458 	 */
2459 	@Override
getResolvedClasspath(boolean ignoreUnresolvedEntry)2460 	public IClasspathEntry[] getResolvedClasspath(boolean ignoreUnresolvedEntry) throws JavaModelException {
2461 		if  (JavaModelManager.getJavaModelManager().isClasspathBeingResolved(this)) {
2462 			if (JavaModelManager.CP_RESOLVE_VERBOSE_ADVANCED)
2463 				verbose_reentering_classpath_resolution();
2464 		    return RESOLUTION_IN_PROGRESS;
2465 		}
2466 		PerProjectInfo perProjectInfo = getPerProjectInfo();
2467 
2468 		// use synchronized block to ensure consistency
2469 		IClasspathEntry[] resolvedClasspath;
2470 		IJavaModelStatus unresolvedEntryStatus;
2471 		synchronized (perProjectInfo) {
2472 			resolvedClasspath = perProjectInfo.getResolvedClasspath();
2473 			unresolvedEntryStatus = perProjectInfo.unresolvedEntryStatus;
2474 		}
2475 
2476 		if (resolvedClasspath == null
2477 				|| (unresolvedEntryStatus != null && !unresolvedEntryStatus.isOK())) { // force resolution to ensure initializers are run again
2478 			resolveClasspath(perProjectInfo, false/*don't use previous session values*/, true/*add classpath change*/);
2479 			synchronized (perProjectInfo) {
2480 				resolvedClasspath = perProjectInfo.getResolvedClasspath();
2481 				unresolvedEntryStatus = perProjectInfo.unresolvedEntryStatus;
2482 			}
2483 			if (resolvedClasspath == null) {
2484 				// another thread reset the resolved classpath, use a temporary PerProjectInfo
2485 				PerProjectInfo temporaryInfo = newTemporaryInfo();
2486 				resolveClasspath(temporaryInfo, false/*don't use previous session values*/, true/*add classpath change*/);
2487 				resolvedClasspath = temporaryInfo.getResolvedClasspath();
2488 				unresolvedEntryStatus = temporaryInfo.unresolvedEntryStatus;
2489 			}
2490 		}
2491 		if (!ignoreUnresolvedEntry && unresolvedEntryStatus != null && !unresolvedEntryStatus.isOK())
2492 			throw new JavaModelException(unresolvedEntryStatus);
2493 		return resolvedClasspath;
2494 	}
2495 
verbose_reentering_classpath_resolution()2496 	private void verbose_reentering_classpath_resolution() {
2497 		Util.verbose(
2498 			"CPResolution: reentering raw classpath resolution, will use empty classpath instead" + //$NON-NLS-1$
2499 			"	project: " + getElementName() + '\n' + //$NON-NLS-1$
2500 			"	invocation stack trace:"); //$NON-NLS-1$
2501 		new Exception("<Fake exception>").printStackTrace(System.out); //$NON-NLS-1$
2502 	}
2503 
2504 	/**
2505 	 * @see IJavaElement
2506 	 */
2507 	@Override
resource(PackageFragmentRoot root)2508 	public IResource resource(PackageFragmentRoot root) {
2509 		return this.project;
2510 	}
2511 
2512 	/**
2513 	 * Retrieve a shared property on a project. If the property is not defined, answers null.
2514 	 * Note that it is orthogonal to IResource persistent properties, and client code has to decide
2515 	 * which form of storage to use appropriately. Shared properties produce real resource files which
2516 	 * can be shared through a VCM onto a server. Persistent properties are not shareable.
2517 	 *
2518 	 * @param key String
2519 	 * @see JavaProject#setSharedProperty(String, String)
2520 	 * @return String
2521 	 * @throws CoreException
2522 	 */
getSharedProperty(String key)2523 	public String getSharedProperty(String key) throws CoreException {
2524 
2525 		String property = null;
2526 		IFile rscFile = this.project.getFile(key);
2527 		if (rscFile.exists()) {
2528 			byte[] bytes = Util.getResourceContentsAsByteArray(rscFile);
2529 			try {
2530 				property = new String(bytes, org.eclipse.jdt.internal.compiler.util.Util.UTF_8); // .classpath always encoded with UTF-8
2531 			} catch (UnsupportedEncodingException e) {
2532 				Util.log(e, "Could not read .classpath with UTF-8 encoding"); //$NON-NLS-1$
2533 				// fallback to default
2534 				property = new String(bytes);
2535 			}
2536 		} else {
2537 			// when a project is imported, we get a first delta for the addition of the .project, but the .classpath is not accessible
2538 			// so default to using java.io.File
2539 			// see https://bugs.eclipse.org/bugs/show_bug.cgi?id=96258
2540 			URI location = rscFile.getLocationURI();
2541 			if (location != null) {
2542 				File file = Util.toLocalFile(location, null/*no progress monitor available*/);
2543 				if (file != null && file.exists()) {
2544 					byte[] bytes;
2545 					try {
2546 						bytes = org.eclipse.jdt.internal.compiler.util.Util.getFileByteContent(file);
2547 					} catch (IOException e) {
2548 						return null;
2549 					}
2550 					try {
2551 						property = new String(bytes, org.eclipse.jdt.internal.compiler.util.Util.UTF_8); // .classpath always encoded with UTF-8
2552 					} catch (UnsupportedEncodingException e) {
2553 						Util.log(e, "Could not read .classpath with UTF-8 encoding"); //$NON-NLS-1$
2554 						// fallback to default
2555 						property = new String(bytes);
2556 					}
2557 				}
2558 			}
2559 		}
2560 		return property;
2561 	}
2562 
2563 	/**
2564 	 * @see JavaElement
2565 	 */
2566 	@Override
getSourceMapper()2567 	public SourceMapper getSourceMapper() {
2568 
2569 		return null;
2570 	}
2571 
2572 	/**
2573 	 * @see IJavaElement
2574 	 */
2575 	@Override
getUnderlyingResource()2576 	public IResource getUnderlyingResource() throws JavaModelException {
2577 		if (!exists()) throw newNotPresentException();
2578 		return this.project;
2579 	}
2580 
2581 	/**
2582 	 * @see IJavaProject
2583 	 */
2584 	@Override
hasBuildState()2585 	public boolean hasBuildState() {
2586 
2587 		return JavaModelManager.getJavaModelManager().getLastBuiltState(this.project, null) != null;
2588 	}
2589 
2590 	/**
2591 	 * @see IJavaProject
2592 	 */
2593 	@Override
hasClasspathCycle(IClasspathEntry[] preferredClasspath)2594 	public boolean hasClasspathCycle(IClasspathEntry[] preferredClasspath) {
2595 		LinkedHashSet cycleParticipants = new LinkedHashSet();
2596 		HashMap preferredClasspaths = new HashMap(1);
2597 		preferredClasspaths.put(this, preferredClasspath);
2598 		updateCycleParticipants(new ArrayList(2), cycleParticipants, new HashMap<>(), ResourcesPlugin.getWorkspace().getRoot(), new HashSet(2), preferredClasspaths);
2599 		return !cycleParticipants.isEmpty();
2600 	}
2601 
hasCycleMarker()2602 	public boolean hasCycleMarker(){
2603 		return getCycleMarker() != null;
2604 	}
2605 
2606 	@Override
hashCode()2607 	public int hashCode() {
2608 		return this.project.hashCode();
2609 	}
2610 
hasUTF8BOM(byte[] bytes)2611 	private boolean hasUTF8BOM(byte[] bytes) {
2612 		if (bytes.length > IContentDescription.BOM_UTF_8.length) {
2613 			for (int i = 0, length = IContentDescription.BOM_UTF_8.length; i < length; i++) {
2614 				if (IContentDescription.BOM_UTF_8[i] != bytes[i])
2615 					return false;
2616 			}
2617 			return true;
2618 		}
2619 		return false;
2620 	}
2621 
2622 	/**
2623 	 * Answers true if the project potentially contains any source. A project which has no source is immutable.
2624 	 * @return boolean
2625 	 */
hasSource()2626 	public boolean hasSource() {
2627 
2628 		// look if any source folder on the classpath
2629 		// no need for resolved path given source folder cannot be abstracted
2630 		IClasspathEntry[] entries;
2631 		try {
2632 			entries = getRawClasspath();
2633 		} catch (JavaModelException e) {
2634 			return true; // unsure
2635 		}
2636 		for (int i = 0, max = entries.length; i < max; i++) {
2637 			if (entries[i].getEntryKind() == IClasspathEntry.CPE_SOURCE) {
2638 				return true;
2639 			}
2640 		}
2641 		return false;
2642 	}
2643 
2644 
2645 
2646 	/*
2647 	 * @see IJavaProject
2648 	 */
2649 	@Override
isOnClasspath(IJavaElement element)2650 	public boolean isOnClasspath(IJavaElement element) {
2651 		IClasspathEntry[] rawClasspath;
2652 		try {
2653 			rawClasspath = getRawClasspath();
2654 		} catch(JavaModelException e){
2655 			return false; // not a Java project
2656 		}
2657 		int elementType = element.getElementType();
2658 		boolean isPackageFragmentRoot = false;
2659 		boolean isFolderPath = false;
2660 		boolean isSource = false;
2661 		switch (elementType) {
2662 			case IJavaElement.JAVA_MODEL:
2663 				return false;
2664 			case IJavaElement.JAVA_PROJECT:
2665 				break;
2666 			case IJavaElement.PACKAGE_FRAGMENT_ROOT:
2667 				isPackageFragmentRoot = true;
2668 				break;
2669 			case IJavaElement.PACKAGE_FRAGMENT:
2670 				isFolderPath = !((IPackageFragmentRoot)element.getParent()).isArchive();
2671 				break;
2672 			case IJavaElement.COMPILATION_UNIT:
2673 				isSource = true;
2674 				break;
2675 			default:
2676 				isSource = element.getAncestor(IJavaElement.COMPILATION_UNIT) != null;
2677 				break;
2678 		}
2679 		IPath elementPath = element.getPath();
2680 
2681 		// first look at unresolved entries
2682 		int length = rawClasspath.length;
2683 		for (int i = 0; i < length; i++) {
2684 			IClasspathEntry entry = rawClasspath[i];
2685 			switch (entry.getEntryKind()) {
2686 				case IClasspathEntry.CPE_LIBRARY:
2687 				case IClasspathEntry.CPE_PROJECT:
2688 				case IClasspathEntry.CPE_SOURCE:
2689 					if (isOnClasspathEntry(elementPath, isFolderPath, isPackageFragmentRoot, entry))
2690 						return true;
2691 					break;
2692 			}
2693 		}
2694 
2695 		// no need to go further for compilation units and elements inside a compilation unit
2696 		// it can only be in a source folder, thus on the raw classpath
2697 		if (isSource)
2698 			return false;
2699 
2700 		// https://bugs.eclipse.org/bugs/show_bug.cgi?id=304081
2701 		// All the resolved classpath entries need to be considered, including the referenced classpath entries
2702 		IClasspathEntry[] resolvedClasspath = null;
2703 		try {
2704 			resolvedClasspath = getResolvedClasspath();
2705 		} catch (JavaModelException e) {
2706 			return false; // Perhaps, not a Java project
2707 		}
2708 
2709 		for (int index = 0; index < resolvedClasspath.length; index++) {
2710 			if (isOnClasspathEntry(elementPath, isFolderPath, isPackageFragmentRoot, resolvedClasspath[index]))
2711 				return true;
2712 		}
2713 
2714 		return false;
2715 	}
2716 
2717 	/*
2718 	 * @see IJavaProject
2719 	 */
2720 	@Override
isOnClasspath(IResource resource)2721 	public boolean isOnClasspath(IResource resource) {
2722 		IPath exactPath = resource.getFullPath();
2723 		IPath path = exactPath;
2724 
2725 		// ensure that folders are only excluded if all of their children are excluded
2726 		int resourceType = resource.getType();
2727 		boolean isFolderPath = resourceType == IResource.FOLDER || resourceType == IResource.PROJECT;
2728 
2729 		IClasspathEntry[] classpath;
2730 		try {
2731 			classpath = this.getResolvedClasspath();
2732 		} catch(JavaModelException e){
2733 			return false; // not a Java project
2734 		}
2735 		for (int i = 0; i < classpath.length; i++) {
2736 			IClasspathEntry entry = classpath[i];
2737 			IPath entryPath = entry.getPath();
2738 			if (entryPath.equals(exactPath)) { // package fragment roots must match exactly entry pathes (no exclusion there)
2739 				return true;
2740 			}
2741 			// https://bugs.eclipse.org/bugs/show_bug.cgi?id=276373
2742 			// When a classpath entry is absolute, convert the resource's relative path to a file system path and compare
2743 			// e.g - /P/lib/variableLib.jar and /home/P/lib/variableLib.jar when compared should return true
2744 			if (entryPath.isAbsolute()
2745 					&& entryPath.equals(ResourcesPlugin.getWorkspace().getRoot().getLocation().append(exactPath))) {
2746 				return true;
2747 			}
2748 			if (entryPath.isPrefixOf(path)
2749 					&& !Util.isExcluded(path, ((ClasspathEntry)entry).fullInclusionPatternChars(), ((ClasspathEntry)entry).fullExclusionPatternChars(), isFolderPath)) {
2750 				return true;
2751 			}
2752 		}
2753 		return false;
2754 	}
2755 
isOnClasspathEntry(IPath elementPath, boolean isFolderPath, boolean isPackageFragmentRoot, IClasspathEntry entry)2756 	private boolean isOnClasspathEntry(IPath elementPath, boolean isFolderPath, boolean isPackageFragmentRoot, IClasspathEntry entry) {
2757 		IPath entryPath = entry.getPath();
2758 		if (isPackageFragmentRoot) {
2759 			// package fragment roots must match exactly entry pathes (no exclusion there)
2760 			if (entryPath.equals(elementPath))
2761 				return true;
2762 		} else {
2763 			if (entryPath.isPrefixOf(elementPath)
2764 					&& !Util.isExcluded(elementPath, ((ClasspathEntry)entry).fullInclusionPatternChars(), ((ClasspathEntry)entry).fullExclusionPatternChars(), isFolderPath))
2765 				return true;
2766 		}
2767 		// https://bugs.eclipse.org/bugs/show_bug.cgi?id=276373
2768 		if (entryPath.isAbsolute()
2769 				&& entryPath.equals(ResourcesPlugin.getWorkspace().getRoot().getLocation().append(elementPath))) {
2770 			return true;
2771 		}
2772 		return false;
2773 	}
2774 
2775 	/**
2776 	 * load preferences from a shareable format (VCM-wise)
2777 	 */
loadPreferences()2778 	 private IEclipsePreferences loadPreferences() {
2779 
2780 	 	IEclipsePreferences preferences = null;
2781 	 	IPath projectMetaLocation = getPluginWorkingLocation();
2782 		if (projectMetaLocation != null) {
2783 			File prefFile = projectMetaLocation.append(PREF_FILENAME).toFile();
2784 			if (prefFile.exists()) { // load preferences from file
2785 				InputStream in = null;
2786 				try {
2787 					in = new BufferedInputStream(new FileInputStream(prefFile));
2788 					preferences = Platform.getPreferencesService().readPreferences(in);
2789 				} catch (CoreException | IOException e) { // problems loading preference store - quietly ignore
2790 				} finally {
2791 					if (in != null) {
2792 						try {
2793 							in.close();
2794 						} catch (IOException e) { // ignore problems with close
2795 						}
2796 					}
2797 				}
2798 				// one shot read, delete old preferences
2799 				prefFile.delete();
2800 				return preferences;
2801 			}
2802 		}
2803 		return null;
2804 	 }
2805 
2806 	/**
2807 	 * @see IJavaProject#newEvaluationContext()
2808 	 */
2809 	@Override
newEvaluationContext()2810 	public IEvaluationContext newEvaluationContext() {
2811 		EvaluationContext context = new EvaluationContext();
2812 		context.setLineSeparator(Util.getLineSeparator(null/*no existing source*/, this));
2813 		return new EvaluationContextWrapper(context, this);
2814 	}
2815 
newNameLookup(ICompilationUnit[] workingCopies)2816 	public NameLookup newNameLookup(ICompilationUnit[] workingCopies) throws JavaModelException {
2817 		return newNameLookup(workingCopies, false);
2818 	}
2819 	/*
2820 	 * Returns a new name lookup. This name lookup first looks in the given working copies.
2821 	 */
newNameLookup(ICompilationUnit[] workingCopies, boolean excludeTestCode)2822 	public NameLookup newNameLookup(ICompilationUnit[] workingCopies, boolean excludeTestCode) throws JavaModelException {
2823 		return getJavaProjectElementInfo().newNameLookup(this, workingCopies, excludeTestCode);
2824 	}
2825 
newNameLookup(WorkingCopyOwner owner)2826 	public NameLookup newNameLookup(WorkingCopyOwner owner) throws JavaModelException {
2827 		return newNameLookup(owner, false);
2828 	}
2829 	/*
2830 	 * Returns a new name lookup. This name lookup first looks in the working copies of the given owner.
2831 	 */
newNameLookup(WorkingCopyOwner owner, boolean excludeTestCode)2832 	public NameLookup newNameLookup(WorkingCopyOwner owner, boolean excludeTestCode) throws JavaModelException {
2833 		JavaModelManager manager = JavaModelManager.getJavaModelManager();
2834 		ICompilationUnit[] workingCopies = owner == null ? null : manager.getWorkingCopies(owner, true/*add primary WCs*/);
2835 		return newNameLookup(workingCopies);
2836 	}
2837 
newSearchableNameEnvironment(ICompilationUnit[] workingCopies)2838 	public SearchableEnvironment newSearchableNameEnvironment(ICompilationUnit[] workingCopies) throws JavaModelException {
2839 		return newSearchableNameEnvironment(workingCopies, false);
2840 	}
2841 	/*
2842 	 * Returns a new search name environment for this project. This name environment first looks in the given working copies.
2843 	 */
newSearchableNameEnvironment(ICompilationUnit[] workingCopies, boolean excludeTestCode)2844 	public SearchableEnvironment newSearchableNameEnvironment(ICompilationUnit[] workingCopies, boolean excludeTestCode) throws JavaModelException {
2845 		return new SearchableEnvironment(this, workingCopies, excludeTestCode);
2846 	}
2847 
2848 	/*
2849 	 * Returns a new search name environment for this project. This name environment first looks in the working copies
2850 	 * of the given owner.
2851 	 */
newSearchableNameEnvironment(WorkingCopyOwner owner)2852 	public SearchableEnvironment newSearchableNameEnvironment(WorkingCopyOwner owner) throws JavaModelException {
2853 		return newSearchableNameEnvironment(owner, false);
2854 	}
newSearchableNameEnvironment(WorkingCopyOwner owner, boolean excludeTestCode)2855 	public SearchableEnvironment newSearchableNameEnvironment(WorkingCopyOwner owner, boolean excludeTestCode) throws JavaModelException {
2856 		return new SearchableEnvironment(this, owner, excludeTestCode);
2857 	}
2858 
2859 	/*
2860 	 * Returns a PerProjectInfo that doesn't register classpath change
2861 	 * and that should be used as a temporary info.
2862 	 */
newTemporaryInfo()2863 	public PerProjectInfo newTemporaryInfo() {
2864 		return
2865 			new PerProjectInfo(this.project.getProject()) {
2866 				@Override
2867 				protected ClasspathChange addClasspathChange() {
2868 					return null;
2869 				}
2870 		};
2871 	}
2872 
2873 	/**
2874 	 * @see IJavaProject
2875 	 */
2876 	@Override
2877 	public ITypeHierarchy newTypeHierarchy(
2878 		IRegion region,
2879 		IProgressMonitor monitor)
2880 		throws JavaModelException {
2881 
2882 		return newTypeHierarchy(region, DefaultWorkingCopyOwner.PRIMARY, monitor);
2883 	}
2884 
2885 	/**
2886 	 * @see IJavaProject
2887 	 */
2888 	@Override
2889 	public ITypeHierarchy newTypeHierarchy(
2890 		IRegion region,
2891 		WorkingCopyOwner owner,
2892 		IProgressMonitor monitor)
2893 		throws JavaModelException {
2894 
2895 		if (region == null) {
2896 			throw new IllegalArgumentException(Messages.hierarchy_nullRegion);
2897 		}
2898 		ICompilationUnit[] workingCopies = JavaModelManager.getJavaModelManager().getWorkingCopies(owner, true/*add primary working copies*/);
2899 		CreateTypeHierarchyOperation op =
2900 			new CreateTypeHierarchyOperation(region, workingCopies, null, true);
2901 		op.runOperation(monitor);
2902 		return op.getResult();
2903 	}
2904 
2905 	/**
2906 	 * @see IJavaProject
2907 	 */
2908 	@Override
2909 	public ITypeHierarchy newTypeHierarchy(
2910 		IType type,
2911 		IRegion region,
2912 		IProgressMonitor monitor)
2913 		throws JavaModelException {
2914 
2915 		return newTypeHierarchy(type, region, DefaultWorkingCopyOwner.PRIMARY, monitor);
2916 	}
2917 
2918 	/**
2919 	 * @see IJavaProject
2920 	 */
2921 	@Override
2922 	public ITypeHierarchy newTypeHierarchy(
2923 		IType type,
2924 		IRegion region,
2925 		WorkingCopyOwner owner,
2926 		IProgressMonitor monitor)
2927 		throws JavaModelException {
2928 
2929 		if (type == null) {
2930 			throw new IllegalArgumentException(Messages.hierarchy_nullFocusType);
2931 		}
2932 		if (region == null) {
2933 			throw new IllegalArgumentException(Messages.hierarchy_nullRegion);
2934 		}
2935 		ICompilationUnit[] workingCopies = JavaModelManager.getJavaModelManager().getWorkingCopies(owner, true/*add primary working copies*/);
2936 		CreateTypeHierarchyOperation op =
2937 			new CreateTypeHierarchyOperation(region, workingCopies, type, true/*compute subtypes*/);
2938 		op.runOperation(monitor);
2939 		return op.getResult();
2940 	}
2941 	public String[] projectPrerequisites(IClasspathEntry[] resolvedClasspath)
2942 		throws JavaModelException {
2943 
2944 		ArrayList prerequisites = new ArrayList();
2945 		for (int i = 0, length = resolvedClasspath.length; i < length; i++) {
2946 			IClasspathEntry entry = resolvedClasspath[i];
2947 			if (entry.getEntryKind() == IClasspathEntry.CPE_PROJECT) {
2948 				prerequisites.add(entry.getPath().lastSegment());
2949 			}
2950 		}
2951 		int size = prerequisites.size();
2952 		if (size == 0) {
2953 			return NO_PREREQUISITES;
2954 		} else {
2955 			String[] result = new String[size];
2956 			prerequisites.toArray(result);
2957 			return result;
2958 		}
2959 	}
2960 	/**
2961 	 * Reads the classpath file entries of this project's .classpath file.
2962 	 * Returns a two-dimensional array, where the number of elements in the row is fixed to 2.
2963 	 * The first element is an array of raw classpath entries, which includes the output entry,
2964 	 * and the second element is an array of referenced entries that may have been stored
2965 	 * by the client earlier.
2966 	 * See {@link IJavaProject#getReferencedClasspathEntries()} for more details.
2967 	 * As a side effect, unknown elements are stored in the given map (if not null)
2968 	 * Throws exceptions if the file cannot be accessed or is malformed.
2969 	 */
2970 	public IClasspathEntry[][] readFileEntriesWithException(Map unknownElements) throws CoreException, IOException, ClasspathEntry.AssertionFailedException {
2971 		IFile rscFile = this.project.getFile(JavaProject.CLASSPATH_FILENAME);
2972 		byte[] bytes;
2973 		if (rscFile.exists()) {
2974 			bytes = Util.getResourceContentsAsByteArray(rscFile);
2975 		} else {
2976 			// when a project is imported, we get a first delta for the addition of the .project, but the .classpath is not accessible
2977 			// so default to using java.io.File
2978 			// see https://bugs.eclipse.org/bugs/show_bug.cgi?id=96258
2979 			URI location = rscFile.getLocationURI();
2980 			if (location == null)
2981 				throw new IOException("Cannot obtain a location URI for " + rscFile); //$NON-NLS-1$
2982 			File file = Util.toLocalFile(location, null/*no progress monitor available*/);
2983 			if (file == null)
2984 				throw new IOException("Unable to fetch file from " + location); //$NON-NLS-1$
2985 			try {
2986 				bytes = org.eclipse.jdt.internal.compiler.util.Util.getFileByteContent(file);
2987 			} catch (IOException e) {
2988 				if (!file.exists())
2989 					return new IClasspathEntry[][]{defaultClasspath(), ClasspathEntry.NO_ENTRIES};
2990 				throw e;
2991 			}
2992 		}
2993 		if (hasUTF8BOM(bytes)) { // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=240034
2994 			int length = bytes.length-IContentDescription.BOM_UTF_8.length;
2995 			System.arraycopy(bytes, IContentDescription.BOM_UTF_8.length, bytes = new byte[length], 0, length);
2996 		}
2997 		String xmlClasspath;
2998 		try {
2999 			xmlClasspath = new String(bytes, org.eclipse.jdt.internal.compiler.util.Util.UTF_8); // .classpath always encoded with UTF-8
3000 		} catch (UnsupportedEncodingException e) {
3001 			Util.log(e, "Could not read .classpath with UTF-8 encoding"); //$NON-NLS-1$
3002 			// fallback to default
3003 			xmlClasspath = new String(bytes);
3004 		}
3005 		return decodeClasspath(xmlClasspath, unknownElements);
3006 	}
3007 
3008 	/*
3009 	 * Reads the classpath file entries of this project's .classpath file.
3010 	 * This includes the output entry.
3011 	 * As a side effect, unknown elements are stored in the given map (if not null)
3012 	 */
3013 	private IClasspathEntry[][] readFileEntries(Map unkwownElements) {
3014 		try {
3015 			return readFileEntriesWithException(unkwownElements);
3016 		} catch (CoreException | IOException | ClasspathEntry.AssertionFailedException e) {
3017 			Util.log(e, "Exception while reading " + getPath().append(JavaProject.CLASSPATH_FILENAME)); //$NON-NLS-1$
3018 			return new IClasspathEntry[][]{JavaProject.INVALID_CLASSPATH, ClasspathEntry.NO_ENTRIES};
3019 		}
3020 	}
3021 
3022 	/**
3023 	 * @see IJavaProject
3024 	 */
3025 	@Override
3026 	public IPath readOutputLocation() {
3027 		// Read classpath file without creating markers nor logging problems
3028 		IClasspathEntry[][] classpath = readFileEntries(null/*not interested in unknown elements*/);
3029 		if (classpath[0] == JavaProject.INVALID_CLASSPATH)
3030 			return defaultOutputLocation();
3031 
3032 		// extract the output location
3033 		IPath outputLocation = null;
3034 		if (classpath[0].length > 0) {
3035 			IClasspathEntry entry = classpath[0][classpath[0].length - 1];
3036 			if (entry.getContentKind() == ClasspathEntry.K_OUTPUT) {
3037 				outputLocation = entry.getPath();
3038 			}
3039 		}
3040 		return outputLocation;
3041 	}
3042 
3043 	/**
3044 	 * @see IJavaProject
3045 	 */
3046 	@Override
3047 	public IClasspathEntry[] readRawClasspath() {
3048 		// Read classpath file without creating markers nor logging problems
3049 		IClasspathEntry[][] classpath = readFileEntries(null/*not interested in unknown elements*/);
3050 		if (classpath[0] == JavaProject.INVALID_CLASSPATH)
3051 			return defaultClasspath();
3052 
3053 		// discard the output location
3054 		if (classpath[0].length > 0) {
3055 			IClasspathEntry entry = classpath[0][classpath[0].length - 1];
3056 			if (entry.getContentKind() == ClasspathEntry.K_OUTPUT) {
3057 				IClasspathEntry[] copy = new IClasspathEntry[classpath[0].length - 1];
3058 				System.arraycopy(classpath[0], 0, copy, 0, copy.length);
3059 				classpath[0] = copy;
3060 			}
3061 		}
3062 		return classpath[0];
3063 	}
3064 
3065 	/**
3066 	 * Removes the given builder from the build spec for the given project.
3067 	 */
3068 	protected void removeFromBuildSpec(String builderID) throws CoreException {
3069 
3070 		IProjectDescription description = this.project.getDescription();
3071 		ICommand[] commands = description.getBuildSpec();
3072 		for (int i = 0; i < commands.length; ++i) {
3073 			if (commands[i].getBuilderName().equals(builderID)) {
3074 				ICommand[] newCommands = new ICommand[commands.length - 1];
3075 				System.arraycopy(commands, 0, newCommands, 0, i);
3076 				System.arraycopy(commands, i + 1, newCommands, i, commands.length - i - 1);
3077 				description.setBuildSpec(newCommands);
3078 				this.project.setDescription(description, null);
3079 				return;
3080 			}
3081 		}
3082 	}
3083 
3084 	/*
3085 	 * Resets this project's caches
3086 	 */
3087 	public void resetCaches() {
3088 		JavaProjectElementInfo info = (JavaProjectElementInfo) JavaModelManager.getJavaModelManager().peekAtInfo(this);
3089 		if (info != null){
3090 			info.resetCaches();
3091 		}
3092 	}
3093 
3094 	public ClasspathChange resetResolvedClasspath() {
3095 		try {
3096 			return getPerProjectInfo().resetResolvedClasspath();
3097 		} catch (JavaModelException e) {
3098 			// project doesn't exist
3099 			return null;
3100 		}
3101 	}
3102 
3103 	/*
3104 	 * Resolve the given raw classpath.
3105 	 */
3106 	public IClasspathEntry[] resolveClasspath(IClasspathEntry[] rawClasspath) throws JavaModelException {
3107 		return resolveClasspath(rawClasspath, false/*don't use previous session*/, true/*resolve chained libraries*/).resolvedClasspath;
3108 	}
3109 
3110 	static class ResolvedClasspath {
3111 		IClasspathEntry[] resolvedClasspath;
3112 		IJavaModelStatus unresolvedEntryStatus = JavaModelStatus.VERIFIED_OK;
3113 		HashMap rawReverseMap = new HashMap();
3114 		Map rootPathToResolvedEntries = new HashMap();
3115 		IClasspathEntry[] referencedEntries = null;
3116 	}
3117 
3118 	public ResolvedClasspath resolveClasspath(IClasspathEntry[] rawClasspath, boolean usePreviousSession, boolean resolveChainedLibraries) throws JavaModelException {
3119 		return resolveClasspath(rawClasspath, null, usePreviousSession, resolveChainedLibraries);
3120 	}
3121 
3122 	public ResolvedClasspath resolveClasspath(IClasspathEntry[] rawClasspath, IClasspathEntry[] referencedEntries, boolean usePreviousSession, boolean resolveChainedLibraries) throws JavaModelException {
3123 		JavaModelManager manager = JavaModelManager.getJavaModelManager();
3124 		ExternalFoldersManager externalFoldersManager = JavaModelManager.getExternalManager();
3125 		ResolvedClasspath result = new ResolvedClasspath();
3126 		Map knownDrives = new HashMap();
3127 
3128 		Map referencedEntriesMap = new HashMap();
3129 		Set<IPath> rawLibrariesPath = new LinkedHashSet<>();
3130 		LinkedHashSet resolvedEntries = new LinkedHashSet();
3131 
3132 		if(resolveChainedLibraries) {
3133 			for (int index = 0; index < rawClasspath.length; index++) {
3134 				IClasspathEntry currentEntry = rawClasspath[index];
3135 				if (currentEntry.getEntryKind() == IClasspathEntry.CPE_LIBRARY) {
3136 					rawLibrariesPath.add(ClasspathEntry.resolveDotDot(getProject().getLocation(), currentEntry.getPath()));
3137 				}
3138 			}
3139 			if (referencedEntries != null) {
3140 				// The Set is required to keep the order intact while the referencedEntriesMap (Map)
3141 				// is used to map the referenced entries with path
3142 				LinkedHashSet referencedEntriesSet = new LinkedHashSet();
3143 				for (int index = 0; index < referencedEntries.length; index++) {
3144 					IPath path = referencedEntries[index].getPath();
3145 					if (!rawLibrariesPath.contains(path) && referencedEntriesMap.get(path) == null) {
3146 						referencedEntriesMap.put(path, referencedEntries[index]);
3147 						referencedEntriesSet.add(referencedEntries[index]);
3148 					}
3149 				}
3150 				if (referencedEntriesSet.size() > 0) {
3151 					result.referencedEntries = new IClasspathEntry[referencedEntriesSet.size()];
3152 					referencedEntriesSet.toArray(result.referencedEntries);
3153 				}
3154 			}
3155 		}
3156 
3157 		int length = rawClasspath.length;
3158 		for (int i = 0; i < length; i++) {
3159 
3160 			IClasspathEntry rawEntry = rawClasspath[i];
3161 			IClasspathEntry resolvedEntry = rawEntry;
3162 
3163 			switch (rawEntry.getEntryKind()){
3164 
3165 				case IClasspathEntry.CPE_VARIABLE :
3166 					try {
3167 						resolvedEntry = manager.resolveVariableEntry(rawEntry, usePreviousSession);
3168 					} catch (ClasspathEntry.AssertionFailedException e) {
3169 						// Catch the assertion failure and set status instead
3170 						// see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=55992
3171 						result.unresolvedEntryStatus = new JavaModelStatus(IJavaModelStatusConstants.INVALID_PATH, e.getMessage());
3172 						break;
3173 					}
3174 					if (resolvedEntry == null) {
3175 						result.unresolvedEntryStatus = new JavaModelStatus(IJavaModelStatusConstants.CP_VARIABLE_PATH_UNBOUND, this, rawEntry.getPath());
3176 					} else {
3177 						// If the entry is already present in the rawReversetMap, it means the entry and the chained libraries
3178 						// have already been processed. So, skip it.
3179 						if (resolveChainedLibraries && resolvedEntry.getEntryKind() == IClasspathEntry.CPE_LIBRARY
3180 													&& result.rawReverseMap.get(resolvedEntry.getPath()) == null) {
3181 							// resolve Class-Path: in manifest
3182 							ClasspathEntry[] extraEntries = ((ClasspathEntry) resolvedEntry).resolvedChainedLibraries();
3183 							for (int j = 0, length2 = extraEntries.length; j < length2; j++) {
3184 								if (!rawLibrariesPath.contains(extraEntries[j].getPath())) {
3185 									// https://bugs.eclipse.org/bugs/show_bug.cgi?id=305037
3186 									// referenced entries for variable entries could also be persisted with extra attributes, so addAsChainedEntry = true
3187 									addToResult(rawEntry, extraEntries[j], result, resolvedEntries, externalFoldersManager, referencedEntriesMap, true, knownDrives);
3188 								}
3189 							}
3190 						}
3191 						addToResult(rawEntry, resolvedEntry, result, resolvedEntries, externalFoldersManager, referencedEntriesMap, false, knownDrives);
3192 					}
3193 					break;
3194 
3195 				case IClasspathEntry.CPE_CONTAINER :
3196 					IClasspathContainer container = usePreviousSession ? manager.getPreviousSessionContainer(rawEntry.getPath(), this) : JavaCore.getClasspathContainer(rawEntry.getPath(), this);
3197 					if (container == null){
3198 						result.unresolvedEntryStatus = new JavaModelStatus(IJavaModelStatusConstants.CP_CONTAINER_PATH_UNBOUND, this, rawEntry.getPath());
3199 						break;
3200 					}
3201 
3202 					IClasspathEntry[] containerEntries = container.getClasspathEntries();
3203 					if (containerEntries == null) {
3204 						if (JavaModelManager.CP_RESOLVE_VERBOSE || JavaModelManager.CP_RESOLVE_VERBOSE_FAILURE) {
3205 							JavaModelManager.getJavaModelManager().verbose_missbehaving_container_null_entries(this, rawEntry.getPath());
3206 						}
3207 						break;
3208 					}
3209 
3210 					// container was bound
3211 					for (int j = 0, containerLength = containerEntries.length; j < containerLength; j++){
3212 						ClasspathEntry cEntry = (ClasspathEntry) containerEntries[j];
3213 						if (cEntry == null) {
3214 							if (JavaModelManager.CP_RESOLVE_VERBOSE || JavaModelManager.CP_RESOLVE_VERBOSE_FAILURE) {
3215 								JavaModelManager.getJavaModelManager().verbose_missbehaving_container(this, rawEntry.getPath(), containerEntries);
3216 							}
3217 							break;
3218 						}
3219 						// if container is exported or restricted, then its nested entries must in turn be exported  (21749) and/or propagate restrictions
3220 						cEntry = cEntry.combineWith((ClasspathEntry) rawEntry);
3221 
3222 						if (cEntry.getEntryKind() == IClasspathEntry.CPE_LIBRARY) {
3223 							// resolve ".." in library path
3224 							cEntry = cEntry.resolvedDotDot(getProject().getLocation());
3225 							// https://bugs.eclipse.org/bugs/show_bug.cgi?id=313965
3226 							// Do not resolve if the system attribute is set to false
3227 							if (resolveChainedLibraries
3228 									&& JavaModelManager.getJavaModelManager().resolveReferencedLibrariesForContainers
3229 									&& result.rawReverseMap.get(cEntry.getPath()) == null) {
3230 								// resolve Class-Path: in manifest
3231 								ClasspathEntry[] extraEntries = cEntry.resolvedChainedLibraries();
3232 								for (int k = 0, length2 = extraEntries.length; k < length2; k++) {
3233 									if (!rawLibrariesPath.contains(extraEntries[k].getPath())) {
3234 										addToResult(rawEntry, extraEntries[k], result, resolvedEntries, externalFoldersManager, referencedEntriesMap, false, knownDrives);
3235 									}
3236 								}
3237 							}
3238 						}
3239 						addToResult(rawEntry, cEntry, result, resolvedEntries, externalFoldersManager, referencedEntriesMap, false, knownDrives);
3240 					}
3241 					break;
3242 
3243 				case IClasspathEntry.CPE_LIBRARY:
3244 					// resolve ".." in library path
3245 					resolvedEntry = ((ClasspathEntry) rawEntry).resolvedDotDot(getProject().getLocation());
3246 
3247 					if (resolveChainedLibraries && result.rawReverseMap.get(resolvedEntry.getPath()) == null) {
3248 						// resolve Class-Path: in manifest
3249 						ClasspathEntry[] extraEntries = ((ClasspathEntry) resolvedEntry).resolvedChainedLibraries();
3250 						for (int k = 0, length2 = extraEntries.length; k < length2; k++) {
3251 							if (!rawLibrariesPath.contains(extraEntries[k].getPath())) {
3252 								addToResult(rawEntry, extraEntries[k], result, resolvedEntries, externalFoldersManager, referencedEntriesMap, true, knownDrives);
3253 							}
3254 						}
3255 					}
3256 
3257 					addToResult(rawEntry, resolvedEntry, result, resolvedEntries, externalFoldersManager, referencedEntriesMap, false, knownDrives);
3258 					break;
3259 				default :
3260 					addToResult(rawEntry, resolvedEntry, result, resolvedEntries, externalFoldersManager, referencedEntriesMap, false, knownDrives);
3261 					break;
3262 			}
3263 		}
3264 		result.resolvedClasspath = new IClasspathEntry[resolvedEntries.size()];
3265 		resolvedEntries.toArray(result.resolvedClasspath);
3266 		return result;
3267 	}
3268 
3269 	private void addToResult(IClasspathEntry rawEntry, IClasspathEntry resolvedEntry, ResolvedClasspath result,
3270 			LinkedHashSet resolvedEntries, ExternalFoldersManager externalFoldersManager,
3271 			Map oldChainedEntriesMap, boolean addAsChainedEntry, Map knownDrives) {
3272 
3273 		IPath resolvedPath;
3274 		// If it's already been resolved, do not add to resolvedEntries
3275 		if (result.rawReverseMap.get(resolvedPath = resolvedEntry.getPath()) == null) {
3276 			result.rawReverseMap.put(resolvedPath, rawEntry);
3277 			result.rootPathToResolvedEntries.put(resolvedPath, resolvedEntry);
3278 			resolvedEntries.add(resolvedEntry);
3279 			if (addAsChainedEntry) {
3280 				IClasspathEntry chainedEntry = null;
3281 				chainedEntry = (ClasspathEntry) oldChainedEntriesMap.get(resolvedPath);
3282 				if (chainedEntry != null) {
3283 					// This is required to keep the attributes if any added by the user in
3284 					// the previous session such as source attachment path etc.
3285 					copyFromOldChainedEntry((ClasspathEntry) resolvedEntry, (ClasspathEntry) chainedEntry);
3286 				}
3287 			}
3288 		}
3289 		if (resolvedEntry.getEntryKind() == IClasspathEntry.CPE_LIBRARY && ExternalFoldersManager.isExternalFolderPath(resolvedPath)) {
3290 			externalFoldersManager.addFolder(resolvedPath, true/*scheduleForCreation*/); // no-op if not an external folder or if already registered
3291 		}
3292 		// https://bugs.eclipse.org/bugs/show_bug.cgi?id=336046
3293 		// The source attachment path could be external too and in which case, must be added.
3294 		IPath sourcePath = resolvedEntry.getSourceAttachmentPath();
3295 		if (sourcePath != null && driveExists(sourcePath, knownDrives) && ExternalFoldersManager.isExternalFolderPath(sourcePath)) {
3296 			externalFoldersManager.addFolder(sourcePath, true);
3297 		}
3298 	}
3299 
3300 	private void copyFromOldChainedEntry(ClasspathEntry resolvedEntry, ClasspathEntry chainedEntry) {
3301 		IPath path = chainedEntry.getSourceAttachmentPath();
3302 		if ( path != null) {
3303 			resolvedEntry.sourceAttachmentPath = path;
3304 		}
3305 		path = chainedEntry.getSourceAttachmentRootPath();
3306 		if (path != null) {
3307 			resolvedEntry.sourceAttachmentRootPath = path;
3308 		}
3309 		IClasspathAttribute[] attributes = chainedEntry.getExtraAttributes();
3310 		if (attributes != null) {
3311 			resolvedEntry.extraAttributes = attributes;
3312 		}
3313 	}
3314 
3315 	/*
3316 	 * File#exists() takes lot of time for an unmapped drive. Hence, cache the info.
3317 	 * https://bugs.eclipse.org/bugs/show_bug.cgi?id=338649
3318 	 */
3319 	private boolean driveExists(IPath sourcePath, Map knownDrives) {
3320 		String drive = sourcePath.getDevice();
3321 		if (drive == null) return true;
3322 		Boolean good = (Boolean)knownDrives.get(drive);
3323 		if (good == null) {
3324 			if (new File(drive).exists()) {
3325 				knownDrives.put(drive, Boolean.TRUE);
3326 				return true;
3327 			} else {
3328 				knownDrives.put(drive, Boolean.FALSE);
3329 				return false;
3330 			}
3331 		}
3332 		return good.booleanValue();
3333 	}
3334 
3335 	/*
3336 	 * Resolve the given perProjectInfo's raw classpath and store the resolved classpath in the perProjectInfo.
3337 	 */
3338 	public void resolveClasspath(PerProjectInfo perProjectInfo, boolean usePreviousSession, boolean addClasspathChange) throws JavaModelException {
3339 		if (CP_RESOLUTION_BP_LISTENERS != null)
3340 			breakpoint(1, this);
3341 		JavaModelManager manager = JavaModelManager.getJavaModelManager();
3342 		boolean isClasspathBeingResolved = manager.isClasspathBeingResolved(this);
3343 		try {
3344 			if (!isClasspathBeingResolved) {
3345 				manager.setClasspathBeingResolved(this, true);
3346 			}
3347 
3348 			// get raw info inside a synchronized block to ensure that it is consistent
3349 			IClasspathEntry[][] classpath = new IClasspathEntry[2][];
3350 			int timeStamp;
3351 			synchronized (perProjectInfo) {
3352 				classpath[0] = perProjectInfo.rawClasspath;
3353 				classpath[1] = perProjectInfo.referencedEntries;
3354 				// Checking null only for rawClasspath enough
3355 				if (classpath[0] == null)
3356 					classpath = perProjectInfo.readAndCacheClasspath(this);
3357 				timeStamp = perProjectInfo.rawTimeStamp;
3358 			}
3359 
3360 			ResolvedClasspath result = resolveClasspath(classpath[0], classpath[1], usePreviousSession, true/*resolve chained libraries*/);
3361 
3362 			if (CP_RESOLUTION_BP_LISTENERS != null)
3363 				breakpoint(2, this);
3364 
3365 			// store resolved info along with the raw info to ensure consistency
3366 			perProjectInfo.setResolvedClasspath(result.resolvedClasspath, result.referencedEntries, result.rawReverseMap, result.rootPathToResolvedEntries, usePreviousSession ? PerProjectInfo.NEED_RESOLUTION : result.unresolvedEntryStatus, timeStamp, addClasspathChange);
3367 		} finally {
3368 			if (!isClasspathBeingResolved) {
3369 				manager.setClasspathBeingResolved(this, false);
3370 			}
3371 			if (CP_RESOLUTION_BP_LISTENERS != null)
3372 				breakpoint(3, this);
3373 		}
3374 	}
3375 
3376 	/**
3377 	 * Answers an ID which is used to distinguish project/entries during package
3378 	 * fragment root computations
3379 	 * @return String
3380 	 */
3381 	public String rootID(){
3382 		return "[PRJ]"+this.project.getFullPath(); //$NON-NLS-1$
3383 	}
3384 
3385 	/**
3386 	 * Writes the classpath in a sharable format (VCM-wise) only when necessary, that is, if  it is semantically different
3387 	 * from the existing one in file. Will never write an identical one.
3388 	 *
3389 	 * @param newClasspath IClasspathEntry[]
3390 	 * @param newOutputLocation IPath
3391 	 * @return boolean Return whether the .classpath file was modified.
3392 	 * @throws JavaModelException
3393 	 */
3394 	public boolean writeFileEntries(IClasspathEntry[] newClasspath, IClasspathEntry[] referencedEntries, IPath newOutputLocation) throws JavaModelException {
3395 
3396 		if (!this.project.isAccessible()) return false;
3397 
3398 		Map unknownElements = new HashMap();
3399 		IClasspathEntry[][] fileEntries = readFileEntries(unknownElements);
3400 		if (fileEntries[0] != JavaProject.INVALID_CLASSPATH &&
3401 				areClasspathsEqual(newClasspath, newOutputLocation, fileEntries[0])
3402 				&& (referencedEntries == null || areClasspathsEqual(referencedEntries, fileEntries[1])) ) {
3403 			// no need to save it, it is the same
3404 			return false;
3405 		}
3406 
3407 		// actual file saving
3408 		try {
3409 			setSharedProperty(JavaProject.CLASSPATH_FILENAME, encodeClasspath(newClasspath, referencedEntries, newOutputLocation, true, unknownElements));
3410 			return true;
3411 		} catch (CoreException e) {
3412 			throw new JavaModelException(e);
3413 		}
3414 	}
3415 	public boolean writeFileEntries(IClasspathEntry[] newClasspath, IPath newOutputLocation) throws JavaModelException {
3416 		return writeFileEntries(newClasspath, ClasspathEntry.NO_ENTRIES, newOutputLocation);
3417 	}
3418 
3419 	/**
3420 	 * Update the Java command in the build spec (replace existing one if present,
3421 	 * add one first if none).
3422 	 */
3423 	private void setJavaCommand(
3424 		IProjectDescription description,
3425 		ICommand newCommand)
3426 		throws CoreException {
3427 
3428 		ICommand[] oldBuildSpec = description.getBuildSpec();
3429 		int oldJavaCommandIndex = getJavaCommandIndex(oldBuildSpec);
3430 		ICommand[] newCommands;
3431 
3432 		if (oldJavaCommandIndex == -1) {
3433 			// Add a Java build spec before other builders (1FWJK7I)
3434 			newCommands = new ICommand[oldBuildSpec.length + 1];
3435 			System.arraycopy(oldBuildSpec, 0, newCommands, 1, oldBuildSpec.length);
3436 			newCommands[0] = newCommand;
3437 		} else {
3438 		    oldBuildSpec[oldJavaCommandIndex] = newCommand;
3439 			newCommands = oldBuildSpec;
3440 		}
3441 
3442 		// Commit the spec change into the project
3443 		description.setBuildSpec(newCommands);
3444 		this.project.setDescription(description, null);
3445 	}
3446 
3447 	/**
3448 	 * @see org.eclipse.jdt.core.IJavaProject#setOption(java.lang.String, java.lang.String)
3449 	 */
3450 	@Override
3451 	public void setOption(String optionName, String optionValue) {
3452 		// Store option value
3453 		IEclipsePreferences projectPreferences = getEclipsePreferences();
3454 		boolean modified = JavaModelManager.getJavaModelManager().storePreference(optionName, optionValue, projectPreferences, null);
3455 
3456 		// Write changes
3457 		if (modified) {
3458 			try {
3459 				projectPreferences.flush();
3460 			} catch (BackingStoreException e) {
3461 				// problem with pref store - quietly ignore
3462 			}
3463 		}
3464 	}
3465 
3466 	/**
3467 	 * @see org.eclipse.jdt.core.IJavaProject#setOptions(Map)
3468 	 */
3469 	@Override
3470 	public void setOptions(Map<String, String> newOptions) {
3471 
3472 		IEclipsePreferences projectPreferences = getEclipsePreferences();
3473 		if (projectPreferences == null) return;
3474 		try {
3475 			if (newOptions == null){
3476 				projectPreferences.clear();
3477 			} else {
3478 				Iterator<Map.Entry<String, String>> entries = newOptions.entrySet().iterator();
3479 				JavaModelManager javaModelManager = JavaModelManager.getJavaModelManager();
3480 				while (entries.hasNext()){
3481 					Map.Entry<String, String> entry = entries.next();
3482 					String key = entry.getKey();
3483 					String value = entry.getValue();
3484 					javaModelManager.storePreference(key, value, projectPreferences, newOptions);
3485 				}
3486 
3487 				// reset to default all options not in new map
3488 				// @see https://bugs.eclipse.org/bugs/show_bug.cgi?id=26255
3489 				// @see https://bugs.eclipse.org/bugs/show_bug.cgi?id=49691
3490 				String[] pNames = projectPreferences.keys();
3491 				int ln = pNames.length;
3492 				for (int i=0; i<ln; i++) {
3493 					String key = pNames[i];
3494 					if (!newOptions.containsKey(key)) {
3495 						projectPreferences.remove(key); // old preferences => remove from preferences table
3496 					}
3497 				}
3498 			}
3499 
3500 			// persist options
3501 			projectPreferences.flush();
3502 
3503 			// flush cache immediately
3504 			try {
3505 				getPerProjectInfo().options = null;
3506 			} catch (JavaModelException e) {
3507 				// do nothing
3508 			}
3509 		} catch (BackingStoreException e) {
3510 			// problem with pref store - quietly ignore
3511 		}
3512 	}
3513 	/**
3514 	 * @see IJavaProject
3515 	 */
3516 	@Override
3517 	public void setOutputLocation(IPath path, IProgressMonitor monitor) throws JavaModelException {
3518 		if (path == null) {
3519 			throw new IllegalArgumentException(Messages.path_nullPath);
3520 		}
3521 		if (path.equals(getOutputLocation())) {
3522 			return;
3523 		}
3524 		setRawClasspath(getRawClasspath(), path, monitor);
3525 	}
3526 
3527 	/**
3528 	 * Sets the underlying kernel project of this Java project,
3529 	 * and fills in its parent and name.
3530 	 * Called by IProject.getNature().
3531 	 *
3532 	 * @see IProjectNature#setProject(IProject)
3533 	 */
3534 	@Override
3535 	public void setProject(IProject project) {
3536 
3537 		this.project = project;
3538 		this.parent = JavaModelManager.getJavaModelManager().getJavaModel();
3539 	}
3540 
3541 	/**
3542 	 * @see IJavaProject#setRawClasspath(IClasspathEntry[],boolean,IProgressMonitor)
3543 	 */
3544 	@Override
3545 	public void setRawClasspath(
3546 		IClasspathEntry[] entries,
3547 		boolean canModifyResources,
3548 		IProgressMonitor monitor)
3549 		throws JavaModelException {
3550 
3551 		setRawClasspath(
3552 			entries,
3553 			getOutputLocation()/*don't change output*/,
3554 			canModifyResources,
3555 			monitor);
3556 	}
3557 
3558 	/**
3559 	 * @see IJavaProject#setRawClasspath(IClasspathEntry[],IPath,boolean,IProgressMonitor)
3560 	 */
3561 	@Override
3562 	public void setRawClasspath(
3563 			IClasspathEntry[] newRawClasspath,
3564 			IPath newOutputLocation,
3565 			boolean canModifyResources,
3566 			IProgressMonitor monitor)
3567 			throws JavaModelException {
3568 		setRawClasspath(newRawClasspath, null, newOutputLocation, canModifyResources, monitor);
3569 	}
3570 
3571 	/**
3572 	 * @see IJavaProject#setRawClasspath(IClasspathEntry[],IPath,IProgressMonitor)
3573 	 */
3574 	@Override
3575 	public void setRawClasspath(
3576 		IClasspathEntry[] entries,
3577 		IPath outputLocation,
3578 		IProgressMonitor monitor)
3579 		throws JavaModelException {
3580 
3581 		setRawClasspath(
3582 			entries,
3583 			outputLocation,
3584 			true/*can change resource (as per API contract)*/,
3585 			monitor);
3586 	}
3587 
3588 	@Override
3589 	public void setRawClasspath(IClasspathEntry[] entries, IClasspathEntry[] referencedEntries, IPath outputLocation,
3590 			IProgressMonitor monitor) throws JavaModelException {
3591 		setRawClasspath(entries, referencedEntries, outputLocation, true, monitor);
3592 	}
3593 
3594 	protected void setRawClasspath(IClasspathEntry[] newRawClasspath, IClasspathEntry[] referencedEntries, IPath newOutputLocation,
3595 			boolean canModifyResources,	IProgressMonitor monitor) throws JavaModelException {
3596 
3597 		try {
3598 			if (newRawClasspath == null) //are we already with the default classpath
3599 				newRawClasspath = defaultClasspath();
3600 
3601 			SetClasspathOperation op =
3602 				new SetClasspathOperation(
3603 					this,
3604 					newRawClasspath,
3605 					referencedEntries,
3606 					newOutputLocation,
3607 					canModifyResources);
3608 			op.runOperation(monitor);
3609 		} catch (JavaModelException e) {
3610 			JavaModelManager.getJavaModelManager().getDeltaProcessor().flush();
3611 			throw e;
3612 		}
3613 	}
3614 
3615 	/**
3616 	 * @see IJavaProject
3617 	 */
3618 	@Override
3619 	public void setRawClasspath(
3620 		IClasspathEntry[] entries,
3621 		IProgressMonitor monitor)
3622 		throws JavaModelException {
3623 
3624 		setRawClasspath(
3625 			entries,
3626 			getOutputLocation()/*don't change output*/,
3627 			true/*can change resource (as per API contract)*/,
3628 			monitor);
3629 	}
3630 
3631 	/**
3632 	 * Record a shared persistent property onto a project.
3633 	 * Note that it is orthogonal to IResource persistent properties, and client code has to decide
3634 	 * which form of storage to use appropriately. Shared properties produce real resource files which
3635 	 * can be shared through a VCM onto a server. Persistent properties are not shareable.
3636 	 * <p>
3637 	 * Shared properties end up in resource files, and thus cannot be modified during
3638 	 * delta notifications (a CoreException would then be thrown).
3639 	 *
3640 	 * @param key String
3641 	 * @param value String
3642 	 * @see JavaProject#getSharedProperty(String key)
3643 	 * @throws CoreException
3644 	 */
3645 	public void setSharedProperty(String key, String value) throws CoreException {
3646 
3647 		IFile rscFile = this.project.getFile(key);
3648 		byte[] bytes = null;
3649 		try {
3650 			bytes = value.getBytes(org.eclipse.jdt.internal.compiler.util.Util.UTF_8); // .classpath always encoded with UTF-8
3651 		} catch (UnsupportedEncodingException e) {
3652 			Util.log(e, "Could not write .classpath with UTF-8 encoding "); //$NON-NLS-1$
3653 			// fallback to default
3654 			bytes = value.getBytes();
3655 		}
3656 		InputStream inputStream = new ByteArrayInputStream(bytes);
3657 		// update the resource content
3658 		if (rscFile.exists()) {
3659 			if (rscFile.isReadOnly()) {
3660 				// provide opportunity to checkout read-only .classpath file (23984)
3661 				ResourcesPlugin.getWorkspace().validateEdit(new IFile[]{rscFile}, IWorkspace.VALIDATE_PROMPT);
3662 			}
3663 			rscFile.setContents(inputStream, IResource.FORCE, null);
3664 		} else {
3665 			rscFile.create(inputStream, IResource.FORCE, null);
3666 		}
3667 	}
3668 
3669 	/** internal structure for detected build path cycles. */
3670 	static class CycleInfo {
3671 
3672 		private List<IPath> pathToCycle;
3673 		public final List<IPath> cycle;
3674 
3675 		public CycleInfo(List<IPath> pathToCycle, List<IPath> cycle) {
3676 			this.pathToCycle = new ArrayList<>(pathToCycle);
3677 			this.cycle = new ArrayList<>(cycle);
3678 		}
3679 
3680 		public static Optional<CycleInfo> findCycleContaining(Collection<List<CycleInfo>> infos, IPath path) {
3681 			return infos.stream().flatMap(l -> l.stream()).filter(c -> c.cycle.contains(path)).findAny();
3682 		}
3683 
3684 		public static void add(IPath project, List<IPath> prefix, List<IPath> cycle, Map<IPath, List<CycleInfo>> cyclesPerProject) {
3685 			List<CycleInfo> list = cyclesPerProject.get(project);
3686 			if (list == null) {
3687 				cyclesPerProject.put(project, list = new ArrayList<>());
3688 			} else {
3689 				for (CycleInfo cycleInfo: list) {
3690 					if (cycleInfo.cycle.equals(cycle)) {
3691 						// same cycle: use the shorter prefix:
3692 						if (cycleInfo.pathToCycle.size() > prefix.size()) {
3693 							cycleInfo.pathToCycle.clear();
3694 							cycleInfo.pathToCycle.addAll(prefix);
3695 						}
3696 						return;
3697 					}
3698 				}
3699 			}
3700 			list.add(new CycleInfo(prefix, cycle));
3701 		}
3702 
3703 		public String pathToCycleAsString() {
3704 			return this.pathToCycle.stream().map(IPath::lastSegment).collect(Collectors.joining(", ")); //$NON-NLS-1$
3705 		}
3706 
3707 		public String cycleAsString() {
3708 			return this.cycle.stream().map(IPath::lastSegment).collect(Collectors.joining(", ")); //$NON-NLS-1$
3709 		}
3710 	}
3711 	/**
3712 	 * If a cycle is detected, then cycleParticipants contains all the paths of projects involved in this cycle (directly and indirectly),
3713 	 * no cycle if the set is empty (and started empty)
3714 	 * @param prereqChain ArrayList
3715 	 * @param cycleParticipants HashSet
3716 	 * @param workspaceRoot IWorkspaceRoot
3717 	 * @param traversed HashSet
3718 	 * @param preferredClasspaths Map
3719 	 */
3720 	public void updateCycleParticipants(
3721 			List<IPath> prereqChain,
3722 			LinkedHashSet cycleParticipants,
3723 			Map<IPath,List<CycleInfo>> cyclesPerProject,
3724 			IWorkspaceRoot workspaceRoot,
3725 			HashSet traversed,
3726 			Map preferredClasspaths){
3727 
3728 		IPath path = getPath();
3729 		prereqChain.add(path);
3730 		traversed.add(path);
3731 		try {
3732 			IClasspathEntry[] classpath = null;
3733 			if (preferredClasspaths != null) classpath = (IClasspathEntry[])preferredClasspaths.get(this);
3734 			if (classpath == null) classpath = getResolvedClasspath();
3735 			for (int i = 0, length = classpath.length; i < length; i++) {
3736 				IClasspathEntry entry = classpath[i];
3737 
3738 				if (entry.getEntryKind() == IClasspathEntry.CPE_PROJECT){
3739 					IPath prereqProjectPath = entry.getPath();
3740 					int prereqIndex = prereqChain.indexOf(prereqProjectPath);
3741 					if (prereqIndex > -1) {
3742 						// record a new cycle:
3743 						List<IPath> cycle = prereqChain.subList(prereqIndex, prereqChain.size());
3744 						// empty-prefix CycleInfo for all members of the cycle:
3745 						List<IPath> prefix = Collections.emptyList();
3746 						for (IPath prjInCycle : cycle) {
3747 							CycleInfo.add(prjInCycle, prefix, cycle, cyclesPerProject);
3748 						}
3749 						// also record with all members of the prereqChain with transitive dependency on the cycle:
3750 						for (int j = 0; j < prereqIndex; j++) {
3751 							CycleInfo.add(prereqChain.get(j), prereqChain.subList(j, prereqIndex), cycle, cyclesPerProject);
3752 						}
3753 					} else if (cycleParticipants.contains(prereqProjectPath)) {
3754 						// record existing cycle as dependency of each project in prereqChain:
3755 						Optional<CycleInfo> cycle = CycleInfo.findCycleContaining(cyclesPerProject.values(), prereqProjectPath);
3756 						if (cycle.isPresent()) {
3757 							List<IPath> theCycle = cycle.get().cycle;
3758 							for (int j = 0; j < prereqChain.size(); j++) {
3759 								IPath prereq = prereqChain.get(j);
3760 								List<IPath> prereqSubList = prereqChain.subList(j, prereqChain.size());
3761 								int joinIndex1 = theCycle.indexOf(prereq);
3762 								if (joinIndex1 != -1) {
3763 									// prereqSubList -> prereqProjectPath + theCycle create a new cycle
3764 									List<IPath> newCycle = new ArrayList<>(prereqSubList);
3765 									int joinIndex2 = theCycle.indexOf(prereqProjectPath); // always != -1 since that's how we found 'cycle'
3766 									while (joinIndex2 != joinIndex1) {
3767 										newCycle.add(theCycle.get(joinIndex2++));
3768 										if (joinIndex2 == theCycle.size())
3769 											joinIndex2 = 0; // it's a cycle :)
3770 									}
3771 									for (IPath newMember : newCycle) {
3772 										CycleInfo.add(newMember, Collections.emptyList(), newCycle, cyclesPerProject);
3773 									}
3774 									break; // the rest of prereqChain is already included via newCycle
3775 								} else {
3776 									CycleInfo.add(prereq, prereqSubList, theCycle, cyclesPerProject);
3777 								}
3778 							}
3779 						}
3780 						prereqIndex = 0;
3781 					} else {
3782 						if (!traversed.contains(prereqProjectPath)) {
3783 							IResource member = workspaceRoot.findMember(prereqProjectPath);
3784 							if (member != null && member.getType() == IResource.PROJECT){
3785 								JavaProject javaProject = (JavaProject)JavaCore.create((IProject)member);
3786 								javaProject.updateCycleParticipants(prereqChain, cycleParticipants, cyclesPerProject, workspaceRoot, traversed, preferredClasspaths);
3787 							}
3788 						}
3789 						continue;
3790 					}
3791 					// fall through from both positive branches above
3792 					for (int index = prereqIndex, size = prereqChain.size(); index < size; index++) {
3793 						cycleParticipants.add(prereqChain.get(index));
3794 					}
3795 				}
3796 			}
3797 		} catch(JavaModelException e){
3798 			// project doesn't exist: ignore
3799 		}
3800 		prereqChain.remove(path);
3801 	}
3802 
3803 	/*
3804 	 * Update eclipse preferences from old preferences.
3805 	 */
3806 	 private void updatePreferences(IEclipsePreferences preferences) {
3807 
3808 	 	IEclipsePreferences oldPreferences = loadPreferences();
3809 	 	if (oldPreferences != null) {
3810 			try {
3811 		 		String[] propertyNames = oldPreferences.childrenNames();
3812 				for (int i = 0; i < propertyNames.length; i++){
3813 					String propertyName = propertyNames[i];
3814 				    String propertyValue = oldPreferences.get(propertyName, ""); //$NON-NLS-1$
3815 				    if (!"".equals(propertyValue)) { //$NON-NLS-1$
3816 					    preferences.put(propertyName, propertyValue);
3817 				    }
3818 				}
3819 				// save immediately new preferences
3820 				preferences.flush();
3821 			} catch (BackingStoreException e) {
3822 				// fails silently
3823 			}
3824 		}
3825 	 }
3826 
3827 	@Override
3828 	protected IStatus validateExistence(IResource underlyingResource) {
3829 		// check whether the java project can be opened
3830 		try {
3831 			if (!((IProject) underlyingResource).hasNature(JavaCore.NATURE_ID))
3832 				return newDoesNotExistStatus();
3833 		} catch (CoreException e) {
3834 			return newDoesNotExistStatus();
3835 		}
3836 		return JavaModelStatus.VERIFIED_OK;
3837 	}
3838 
3839 	@Override
3840 	public IModuleDescription getModuleDescription() throws JavaModelException {
3841 		JavaProjectElementInfo info = (JavaProjectElementInfo) getElementInfo();
3842 		IModuleDescription module = info.getModule();
3843 		if (module != null)
3844 			return module;
3845 		for(IClasspathEntry entry : getRawClasspath()) {
3846 			List<String> patchedModules = getPatchedModules(entry);
3847 			if (patchedModules.size() == 1) { // > 1 is malformed, 0 means not affecting this project
3848 				String mainModule = patchedModules.get(0);
3849 				switch (entry.getEntryKind()) {
3850 					case IClasspathEntry.CPE_PROJECT:
3851 						IJavaProject referencedProject = getJavaModel().getJavaProject(entry.getPath().toString());
3852 						module = referencedProject.getModuleDescription();
3853 						if (module != null && module.getElementName().equals(mainModule))
3854 							return module;
3855 						break;
3856 					case IClasspathEntry.CPE_LIBRARY:
3857 					case IClasspathEntry.CPE_CONTAINER:
3858 						for (IPackageFragmentRoot root : findPackageFragmentRoots(entry)) {
3859 							module = root.getModuleDescription();
3860 							if (module != null && module.getElementName().equals(mainModule))
3861 								return module;
3862 						}
3863 				}
3864 			}
3865 		}
3866 		return null;
3867 	}
3868 
3869 	@Override
3870 	public IModuleDescription getOwnModuleDescription() throws JavaModelException {
3871 		JavaProjectElementInfo info = (JavaProjectElementInfo) getElementInfo();
3872 		return info.getModule();
3873 	}
3874 
3875 	public List<String> getPatchedModules(IClasspathEntry cpEntry) {
3876 		String patchModules = ClasspathEntry.getExtraAttribute(cpEntry, IClasspathAttribute.PATCH_MODULE);
3877 		if (patchModules != null) {
3878 			List<String> result = new ArrayList<>();
3879 			IPath prjPath = getPath();
3880 			for (String patchModule : patchModules.split("::")) { //$NON-NLS-1$
3881 				int equalsIdx = patchModule.indexOf('=');
3882 				if (equalsIdx != -1) {
3883 					if (equalsIdx < patchModule.length()-1) { // otherwise malformed?
3884 						String locations = patchModule.substring(equalsIdx + 1);
3885 						for (String location : locations.split(File.pathSeparator)) {
3886 							if (prjPath.isPrefixOf(new Path(location))) {
3887 								result.add(patchModule.substring(0, equalsIdx));
3888 								break;
3889 							}
3890 						}
3891 					}
3892 				} else {
3893 					result.add(patchModule); // old format not specifying a location
3894 				}
3895 			}
3896 			return result;
3897 		}
3898 		return Collections.emptyList();
3899 	}
3900 
3901 	public IModuleDescription getAutomaticModuleDescription() throws JavaModelException {
3902 		boolean nameFromManifest = true;
3903 		char[] moduleName = AutomaticModuleNaming.determineAutomaticModuleNameFromManifest(getManifest());
3904 		if (moduleName == null) {
3905 			nameFromManifest = false;
3906 			moduleName = AutomaticModuleNaming.determineAutomaticModuleNameFromFileName(getElementName(), true, false);
3907 		}
3908 		return new AbstractModule.AutoModule(this, String.valueOf(moduleName), nameFromManifest);
3909 	}
3910 
3911 	public void setModuleDescription(IModuleDescription module) throws JavaModelException {
3912 		JavaProjectElementInfo info = (JavaProjectElementInfo) getElementInfo();
3913 		IModuleDescription current = info.getModule();
3914 		if (current != null) {
3915 			IPackageFragmentRoot root = (IPackageFragmentRoot) current.getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT);
3916 			IPackageFragmentRoot newRoot = (IPackageFragmentRoot) module.getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT);
3917 			if (!root.equals(newRoot))
3918 				throw new JavaModelException(new Status(IStatus.ERROR, JavaCore.PLUGIN_ID,
3919 						Messages.bind(Messages.classpath_duplicateEntryPath, TypeConstants.MODULE_INFO_FILE_NAME_STRING, getElementName())));
3920 		}
3921 		info.setModule(module);
3922 	}
3923 
3924 	private boolean isUnNamedModule() throws JavaModelException {
3925 		JavaProjectElementInfo info = (JavaProjectElementInfo) getElementInfo();
3926 		IModuleDescription module = info.getModule();
3927 		if (module != null)
3928 			return false;
3929 		for(IClasspathEntry entry : getRawClasspath()) {
3930 			if (!getPatchedModules(entry).isEmpty())
3931 				return false;
3932 		}
3933 		return true;
3934 	}
3935 
3936 	public Manifest getManifest() {
3937 		IFile file = getProject().getFile(new Path(TypeConstants.META_INF_MANIFEST_MF));
3938 		if (file.exists()) {
3939 			try (InputStream contents = file.getContents()) {
3940 				return new Manifest(contents);
3941 			} catch (IOException | CoreException e) {
3942 				// unusable manifest
3943 			}
3944 		}
3945 		return null;
3946 	}
3947 
3948 	@Override
3949 	public Set<String> determineModulesOfProjectsWithNonEmptyClasspath() throws JavaModelException {
3950 		return ModuleUpdater.determineModulesOfProjectsWithNonEmptyClasspath(this, getExpandedClasspath());
3951 	}
3952 }
3953