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