1 /*******************************************************************************
2 * Copyright (c) 2000, 2019 IBM Corporation and others.
3 *
4 * This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License 2.0
6 * which accompanies this distribution, and is available at
7 * https://www.eclipse.org/legal/epl-2.0/
8 *
9 * SPDX-License-Identifier: EPL-2.0
10 *
11 * Contributors:
12 * IBM Corporation - initial API and implementation
13 *******************************************************************************/
14 package org.eclipse.jdt.internal.core;
15
16 import java.util.ArrayList;
17 import java.util.Enumeration;
18 import java.util.Map;
19 import java.util.jar.Manifest;
20
21 import org.eclipse.core.resources.*;
22 import org.eclipse.core.runtime.*;
23 import org.eclipse.jdt.core.*;
24 import org.eclipse.jdt.core.compiler.CharOperation;
25 import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
26 import org.eclipse.jdt.internal.compiler.env.AutomaticModuleNaming;
27 import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
28 import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
29 import org.eclipse.jdt.internal.core.util.MementoTokenizer;
30 import org.eclipse.jdt.internal.core.util.Messages;
31 import org.eclipse.jdt.internal.core.util.Util;
32
33 /**
34 * @see IPackageFragmentRoot
35 */
36 @SuppressWarnings({"rawtypes", "unchecked"})
37 public class PackageFragmentRoot extends Openable implements IPackageFragmentRoot {
38
39 /**
40 * The delimiter between the source path and root path in the
41 * attachment server property.
42 */
43 protected final static char ATTACHMENT_PROPERTY_DELIMITER= '*';
44 /*
45 * No source attachment property
46 */
47 public final static String NO_SOURCE_ATTACHMENT = ""; //$NON-NLS-1$
48
49 /**
50 * The resource associated with this root (null for external jar)
51 */
52 protected IResource resource;
53
54 /**
55 * Constructs a package fragment root which is the root of the java package
56 * directory hierarchy.
57 */
PackageFragmentRoot(IResource resource, JavaProject project)58 protected PackageFragmentRoot(IResource resource, JavaProject project) {
59 super(project);
60 this.resource = resource;
61 }
62
63 /**
64 * @see IPackageFragmentRoot
65 */
66 @Override
attachSource(IPath sourcePath, IPath rootPath, IProgressMonitor monitor)67 public void attachSource(IPath sourcePath, IPath rootPath, IProgressMonitor monitor) throws JavaModelException {
68 try {
69 verifyAttachSource(sourcePath);
70 if (monitor != null) {
71 monitor.beginTask(Messages.element_attachingSource, 2);
72 }
73 SourceMapper oldMapper= getSourceMapper();
74 boolean rootNeedsToBeClosed= false;
75
76 if (sourcePath == null) {
77 //source being detached
78 rootNeedsToBeClosed= true;
79 setSourceMapper(null);
80 /* Disable deltas (see 1GDTUSD)
81 // fire a delta to notify the UI about the source detachement.
82 JavaModelManager manager = (JavaModelManager) JavaModelManager.getJavaModelManager();
83 JavaModel model = (JavaModel) getJavaModel();
84 JavaElementDelta attachedSourceDelta = new JavaElementDelta(model);
85 attachedSourceDelta .sourceDetached(this); // this would be a PackageFragmentRoot
86 manager.registerResourceDelta(attachedSourceDelta );
87 manager.fire(); // maybe you want to fire the change later. Let us know about it.
88 */
89 } else {
90 /*
91 // fire a delta to notify the UI about the source attachment.
92 JavaModelManager manager = (JavaModelManager) JavaModelManager.getJavaModelManager();
93 JavaModel model = (JavaModel) getJavaModel();
94 JavaElementDelta attachedSourceDelta = new JavaElementDelta(model);
95 attachedSourceDelta .sourceAttached(this); // this would be a PackageFragmentRoot
96 manager.registerResourceDelta(attachedSourceDelta );
97 manager.fire(); // maybe you want to fire the change later. Let us know about it.
98 */
99
100 //check if different from the current attachment
101 IPath storedSourcePath= getSourceAttachmentPath();
102 IPath storedRootPath= getSourceAttachmentRootPath();
103 if (monitor != null) {
104 monitor.worked(1);
105 }
106 if (storedSourcePath != null) {
107 if (!(storedSourcePath.equals(sourcePath) && (rootPath != null && rootPath.equals(storedRootPath)) || storedRootPath == null)) {
108 rootNeedsToBeClosed= true;
109 }
110 }
111 // check if source path is valid
112 Object target = JavaModel.getTarget(sourcePath, false);
113 if (target == null) {
114 throw new JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.INVALID_PATH, sourcePath));
115 }
116 SourceMapper mapper = createSourceMapper(sourcePath, rootPath);
117 if (rootPath == null && mapper.rootPath != null) {
118 // as a side effect of calling the SourceMapper constructor, the root path was computed
119 rootPath = new Path(mapper.rootPath);
120 }
121 setSourceMapper(mapper);
122 }
123 if (sourcePath == null) {
124 Util.setSourceAttachmentProperty(getPath(), null); //remove the property
125 } else {
126 //set the property to the path of the mapped source
127 Util.setSourceAttachmentProperty(
128 getPath(),
129 sourcePath.toString()
130 + (rootPath == null ? "" : (ATTACHMENT_PROPERTY_DELIMITER + rootPath.toString()))); //$NON-NLS-1$
131 }
132 if (rootNeedsToBeClosed) {
133 if (oldMapper != null) {
134 oldMapper.close();
135 }
136 BufferManager manager= BufferManager.getDefaultBufferManager();
137 Enumeration openBuffers= manager.getOpenBuffers();
138 while (openBuffers.hasMoreElements()) {
139 IBuffer buffer= (IBuffer) openBuffers.nextElement();
140 IOpenable possibleMember= buffer.getOwner();
141 if (isAncestorOf((IJavaElement) possibleMember)) {
142 buffer.close();
143 }
144 }
145 if (monitor != null) {
146 monitor.worked(1);
147 }
148 }
149 } catch (JavaModelException e) {
150 Util.setSourceAttachmentProperty(getPath(), null); // loose info - will be recomputed
151 throw e;
152 } finally {
153 if (monitor != null) {
154 monitor.done();
155 }
156 }
157 }
158
159 /**
160 * @see Openable
161 */
162 @Override
buildStructure(OpenableElementInfo info, IProgressMonitor pm, Map newElements, IResource underlyingResource)163 protected boolean buildStructure(OpenableElementInfo info, IProgressMonitor pm, Map newElements, IResource underlyingResource) throws JavaModelException {
164 ((PackageFragmentRootInfo) info).setRootKind(determineKind(underlyingResource));
165 return computeChildren(info, underlyingResource);
166 }
167
createSourceMapper(IPath sourcePath, IPath rootPath)168 SourceMapper createSourceMapper(IPath sourcePath, IPath rootPath) throws JavaModelException {
169 IClasspathEntry entry = ((JavaProject) getParent()).getClasspathEntryFor(getPath());
170 String encoding = (entry== null) ? null : ((ClasspathEntry) entry).getSourceAttachmentEncoding();
171 SourceMapper mapper = new SourceMapper(
172 sourcePath,
173 rootPath == null ? null : rootPath.toOSString(),
174 getJavaProject().getOptions(true),// cannot use workspace options if external jar is 1.5 jar and workspace options are 1.4 options
175 encoding);
176
177 return mapper;
178 }
179
180 @Override
delete( int updateResourceFlags, int updateModelFlags, IProgressMonitor monitor)181 public void delete(
182 int updateResourceFlags,
183 int updateModelFlags,
184 IProgressMonitor monitor)
185 throws JavaModelException {
186
187 DeletePackageFragmentRootOperation op = new DeletePackageFragmentRootOperation(this, updateResourceFlags, updateModelFlags);
188 op.runOperation(monitor);
189 }
190
191 /**
192 * Compute the package fragment children of this package fragment root.
193 *
194 * @exception JavaModelException The resource associated with this package fragment root does not exist
195 */
computeChildren(OpenableElementInfo info, IResource underlyingResource)196 protected boolean computeChildren(OpenableElementInfo info, IResource underlyingResource) throws JavaModelException {
197 // Note the children are not opened (so not added to newElements) for a regular package fragment root
198 // However they are opened for a Jar package fragment root (see JarPackageFragmentRoot#computeChildren)
199 try {
200 // the underlying resource may be a folder or a project (in the case that the project folder
201 // is actually the package fragment root)
202 if (underlyingResource.getType() == IResource.FOLDER || underlyingResource.getType() == IResource.PROJECT) {
203 ArrayList vChildren = new ArrayList(5);
204 IContainer rootFolder = (IContainer) underlyingResource;
205 char[][] inclusionPatterns = fullInclusionPatternChars();
206 char[][] exclusionPatterns = fullExclusionPatternChars();
207 computeFolderChildren(rootFolder, !Util.isExcluded(rootFolder, inclusionPatterns, exclusionPatterns), CharOperation.NO_STRINGS, vChildren, inclusionPatterns, exclusionPatterns);
208 // char[] suffix = getKind() == K_SOURCE ? SuffixConstants.SUFFIX_java : SuffixConstants.SUFFIX_class;
209 // char[] moduleInfoName = CharOperation.concat(TypeConstants.MODULE_INFO_NAME, suffix);
210 // IResource module = rootFolder.findMember(String.valueOf(moduleInfoName), true);
211 // if (module != null && module.exists()) {
212 // vChildren.add(new ClassFile(getPackageFragment(CharOperation.NO_STRINGS), String.valueOf(TypeConstants.MODULE_INFO_NAME)));
213 // }
214 if (!vChildren.isEmpty()) {
215 IJavaElement[] children = new IJavaElement[vChildren.size()];
216 vChildren.toArray(children);
217 info.setChildren(children);
218 } else {
219 info.setChildren(JavaElement.NO_ELEMENTS);
220 }
221 }
222 } catch (JavaModelException e) {
223 //problem resolving children; structure remains unknown
224 info.setChildren(new IJavaElement[]{});
225 throw e;
226 }
227 return true;
228 }
229
230 /**
231 * Starting at this folder, create package fragments and add the fragments that are not excluded
232 * to the collection of children.
233 *
234 * @exception JavaModelException The resource associated with this package fragment does not exist
235 */
computeFolderChildren(IContainer folder, boolean isIncluded, String[] pkgName, ArrayList vChildren, char[][] inclusionPatterns, char[][] exclusionPatterns)236 protected void computeFolderChildren(IContainer folder, boolean isIncluded, String[] pkgName, ArrayList vChildren, char[][] inclusionPatterns, char[][] exclusionPatterns) throws JavaModelException {
237
238 if (isIncluded) {
239 IPackageFragment pkg = getPackageFragment(pkgName);
240 vChildren.add(pkg);
241 }
242 try {
243 IResource[] members = folder.members();
244 boolean hasIncluded = isIncluded;
245 int length = members.length;
246 if (length > 0) {
247 // if package fragment root refers to folder in another IProject, then
248 // folder.getProject() is different than getJavaProject().getProject()
249 // use the other java project's options to verify the name
250 IJavaProject otherJavaProject = JavaCore.create(folder.getProject());
251 String sourceLevel = otherJavaProject.getOption(JavaCore.COMPILER_SOURCE, true);
252 String complianceLevel = otherJavaProject.getOption(JavaCore.COMPILER_COMPLIANCE, true);
253 JavaProject javaProject = (JavaProject) getJavaProject();
254 JavaModelManager manager = JavaModelManager.getJavaModelManager();
255 for (int i = 0; i < length; i++) {
256 IResource member = members[i];
257 String memberName = member.getName();
258
259 switch(member.getType()) {
260
261 case IResource.FOLDER:
262 // recurse into sub folders even even parent not included as a sub folder could be included
263 // (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=65637)
264 if (Util.isValidFolderNameForPackage(memberName, sourceLevel, complianceLevel)) {
265 // eliminate binary output only if nested inside direct subfolders
266 if (javaProject.contains(member)) {
267 String[] newNames = Util.arrayConcat(pkgName, manager.intern(memberName));
268 boolean isMemberIncluded = !Util.isExcluded(member, inclusionPatterns, exclusionPatterns);
269 computeFolderChildren((IFolder) member, isMemberIncluded, newNames, vChildren, inclusionPatterns, exclusionPatterns);
270 }
271 }
272 break;
273 case IResource.FILE:
274 // inclusion filter may only include files, in which case we still want to include the immediate parent package (lazily)
275 if (!hasIncluded
276 && Util.isValidCompilationUnitName(memberName, sourceLevel, complianceLevel)
277 && !Util.isExcluded(member, inclusionPatterns, exclusionPatterns)) {
278 hasIncluded = true;
279 IPackageFragment pkg = getPackageFragment(pkgName);
280 vChildren.add(pkg);
281 }
282 break;
283 }
284 }
285 }
286 } catch(IllegalArgumentException e){
287 throw new JavaModelException(e, IJavaModelStatusConstants.ELEMENT_DOES_NOT_EXIST); // could be thrown by ElementTree when path is not found
288 } catch (CoreException e) {
289 throw new JavaModelException(e);
290 }
291 }
292
293 @Override
copy( IPath destination, int updateResourceFlags, int updateModelFlags, IClasspathEntry sibling, IProgressMonitor monitor)294 public void copy(
295 IPath destination,
296 int updateResourceFlags,
297 int updateModelFlags,
298 IClasspathEntry sibling,
299 IProgressMonitor monitor)
300 throws JavaModelException {
301
302 CopyPackageFragmentRootOperation op =
303 new CopyPackageFragmentRootOperation(this, destination, updateResourceFlags, updateModelFlags, sibling);
304 op.runOperation(monitor);
305 }
306
307 /**
308 * Returns a new element info for this element.
309 */
310 @Override
createElementInfo()311 protected Object createElementInfo() {
312 return new PackageFragmentRootInfo();
313 }
314
315 /**
316 * @see IPackageFragmentRoot
317 */
318 @Override
createPackageFragment(String pkgName, boolean force, IProgressMonitor monitor)319 public IPackageFragment createPackageFragment(String pkgName, boolean force, IProgressMonitor monitor) throws JavaModelException {
320 CreatePackageFragmentOperation op = new CreatePackageFragmentOperation(this, pkgName, force);
321 op.runOperation(monitor);
322 return getPackageFragment(op.pkgName);
323 }
324
325 /**
326 * Returns the root's kind - K_SOURCE or K_BINARY, defaults
327 * to K_SOURCE if it is not on the classpath.
328 *
329 * @exception JavaModelException if the project and root do
330 * not exist.
331 */
determineKind(IResource underlyingResource)332 protected int determineKind(IResource underlyingResource) throws JavaModelException {
333 IClasspathEntry entry = ((JavaProject)getJavaProject()).getClasspathEntryFor(underlyingResource.getFullPath());
334 if (entry != null) {
335 return entry.getContentKind();
336 }
337 return IPackageFragmentRoot.K_SOURCE;
338 }
339
340 /**
341 * Compares two objects for equality;
342 * for <code>PackageFragmentRoot</code>s, equality is having the
343 * same parent, same resources, and occurrence count.
344 *
345 */
346 @Override
equals(Object o)347 public boolean equals(Object o) {
348 if (this == o)
349 return true;
350 if (!(o instanceof PackageFragmentRoot))
351 return false;
352 PackageFragmentRoot other = (PackageFragmentRoot) o;
353 return resource().equals(other.resource()) &&
354 this.parent.equals(other.parent);
355 }
356
findSourceAttachmentRecommendation()357 private IClasspathEntry findSourceAttachmentRecommendation() {
358 try {
359 IPath rootPath = getPath();
360 IClasspathEntry entry;
361
362 // try on enclosing project first
363 JavaProject parentProject = (JavaProject) getJavaProject();
364 try {
365 entry = parentProject.getClasspathEntryFor(rootPath);
366 if (entry != null) {
367 Object target = JavaModel.getTarget(entry.getSourceAttachmentPath(), true);
368 if (target != null) {
369 return entry;
370 }
371 }
372 } catch(JavaModelException e){
373 // ignore
374 }
375
376 // iterate over all projects
377 IJavaModel model = getJavaModel();
378 IJavaProject[] jProjects = model.getJavaProjects();
379 for (int i = 0, max = jProjects.length; i < max; i++){
380 JavaProject jProject = (JavaProject) jProjects[i];
381 if (jProject == parentProject) continue; // already done
382 try {
383 entry = jProject.getClasspathEntryFor(rootPath);
384 if (entry != null){
385 Object target = JavaModel.getTarget(entry.getSourceAttachmentPath(), true);
386 if (target != null) {
387 return entry;
388 }
389 }
390 } catch(JavaModelException e){
391 // ignore
392 }
393 }
394 } catch(JavaModelException e){
395 // ignore
396 }
397
398 return null;
399 }
400
401 /*
402 * Returns the exclusion patterns from the classpath entry associated with this root.
403 */
fullExclusionPatternChars()404 public char[][] fullExclusionPatternChars() {
405 try {
406 if (isOpen() && getKind() != IPackageFragmentRoot.K_SOURCE) return null;
407 ClasspathEntry entry = (ClasspathEntry) getRawClasspathEntry();
408 if (entry == null) {
409 return null;
410 } else {
411 return entry.fullExclusionPatternChars();
412 }
413 } catch (JavaModelException e) {
414 return null;
415 }
416 }
417
418 /*
419 * Returns the inclusion patterns from the classpath entry associated with this root.
420 */
fullInclusionPatternChars()421 public char[][] fullInclusionPatternChars() {
422 try {
423 if (isOpen() && getKind() != IPackageFragmentRoot.K_SOURCE) return null;
424 ClasspathEntry entry = (ClasspathEntry)getRawClasspathEntry();
425 if (entry == null) {
426 return null;
427 } else {
428 return entry.fullInclusionPatternChars();
429 }
430 } catch (JavaModelException e) {
431 return null;
432 }
433 }
434 @Override
getElementName()435 public String getElementName() {
436 IResource res = resource();
437 if (res instanceof IFolder)
438 return ((IFolder) res).getName();
439 return ""; //$NON-NLS-1$
440 }
441 /**
442 * @see IJavaElement
443 */
444 @Override
getElementType()445 public int getElementType() {
446 return PACKAGE_FRAGMENT_ROOT;
447 }
448 /**
449 * @see JavaElement#getHandleMemento()
450 */
451 @Override
getHandleMementoDelimiter()452 protected char getHandleMementoDelimiter() {
453 return JavaElement.JEM_PACKAGEFRAGMENTROOT;
454 }
455 /*
456 * @see JavaElement
457 */
458 @Override
getHandleFromMemento(String token, MementoTokenizer memento, WorkingCopyOwner owner)459 public IJavaElement getHandleFromMemento(String token, MementoTokenizer memento, WorkingCopyOwner owner) {
460 switch (token.charAt(0)) {
461 case JEM_PACKAGEFRAGMENT:
462 String[] pkgName;
463 if (memento.hasMoreTokens()) {
464 token = memento.nextToken();
465 char firstChar = token.charAt(0);
466 if (firstChar == JEM_CLASSFILE || firstChar == JEM_MODULAR_CLASSFILE || firstChar == JEM_COMPILATIONUNIT || firstChar == JEM_COUNT) {
467 pkgName = CharOperation.NO_STRINGS;
468 } else {
469 pkgName = Util.splitOn('.', token, 0, token.length());
470 token = null;
471 }
472 } else {
473 pkgName = CharOperation.NO_STRINGS;
474 token = null;
475 }
476 JavaElement pkg = getPackageFragment(pkgName);
477 if (token == null) {
478 return pkg.getHandleFromMemento(memento, owner);
479 } else {
480 return pkg.getHandleFromMemento(token, memento, owner);
481 }
482 }
483 return null;
484 }
485 /**
486 * @see JavaElement#getHandleMemento(StringBuffer)
487 */
488 @Override
getHandleMemento(StringBuffer buff)489 protected void getHandleMemento(StringBuffer buff) {
490 IPath path;
491 IResource underlyingResource = getResource();
492 if (underlyingResource != null) {
493 // internal jar or regular root
494 if (resource().getProject().equals(getJavaProject().getProject())) {
495 path = underlyingResource.getProjectRelativePath();
496 } else {
497 path = underlyingResource.getFullPath();
498 }
499 } else {
500 // external jar
501 path = getPath();
502 }
503 ((JavaElement)getParent()).getHandleMemento(buff);
504 buff.append(getHandleMementoDelimiter());
505 escapeMementoName(buff, path.toString());
506 if (org.eclipse.jdt.internal.compiler.util.Util.isJrt(path.toOSString())) {
507 buff.append(JavaElement.JEM_MODULE);
508 escapeMementoName(buff, getElementName());
509 }
510 try {
511 IClasspathEntry entry = getJavaProject().getClasspathEntryFor(getPath());
512 if (entry != null) {
513 for (IClasspathAttribute attribute : entry.getExtraAttributes()) {
514 appendEscapedDelimiter(buff, JavaElement.JEM_PACKAGEFRAGMENTROOT);
515 escapeMementoName(buff, attribute.getName());
516 appendEscapedDelimiter(buff, JavaElement.JEM_PACKAGEFRAGMENTROOT);
517 escapeMementoName(buff, attribute.getValue());
518 appendEscapedDelimiter(buff, JavaElement.JEM_PACKAGEFRAGMENTROOT);
519 }
520 }
521 } catch (JavaModelException e) {
522 // ignore
523 }
524 }
525 /**
526 * @see IPackageFragmentRoot
527 */
528 @Override
getKind()529 public int getKind() throws JavaModelException {
530 return ((PackageFragmentRootInfo)getElementInfo()).getRootKind();
531 }
532
533 /*
534 * A version of getKind() that doesn't update the timestamp of the info in the Java model cache
535 * to speed things up
536 */
internalKind()537 int internalKind() throws JavaModelException {
538 JavaModelManager manager = JavaModelManager.getJavaModelManager();
539 PackageFragmentRootInfo info = (PackageFragmentRootInfo) manager.peekAtInfo(this);
540 if (info == null) {
541 info = (PackageFragmentRootInfo) openWhenClosed(createElementInfo(), false, null);
542 }
543 return info.getRootKind();
544 }
545
546 /**
547 * Returns an array of non-java resources contained in the receiver.
548 */
549 @Override
getNonJavaResources()550 public Object[] getNonJavaResources() throws JavaModelException {
551 return ((PackageFragmentRootInfo) getElementInfo()).getNonJavaResources(getJavaProject(), resource(), this);
552 }
553
554 /**
555 * @see IPackageFragmentRoot
556 */
557 @Override
getPackageFragment(String packageName)558 public IPackageFragment getPackageFragment(String packageName) {
559 // tolerate package names with spaces (e.g. 'x . y') (http://bugs.eclipse.org/bugs/show_bug.cgi?id=21957)
560 String[] pkgName = Util.getTrimmedSimpleNames(packageName);
561 return getPackageFragment(pkgName);
562 }
getPackageFragment(String[] pkgName)563 public PackageFragment getPackageFragment(String[] pkgName) {
564 return new PackageFragment(this, pkgName);
565 }
getPackageFragment(String[] pkgName, String mod)566 public PackageFragment getPackageFragment(String[] pkgName, String mod) {
567 return new PackageFragment(this, pkgName); // Overridden in JImageModuleFragmentBridge
568 }
569 /**
570 * Returns the package name for the given folder
571 * (which is a decendent of this root).
572 */
getPackageName(IFolder folder)573 protected String getPackageName(IFolder folder) {
574 IPath myPath= getPath();
575 IPath pkgPath= folder.getFullPath();
576 int mySegmentCount= myPath.segmentCount();
577 int pkgSegmentCount= pkgPath.segmentCount();
578 StringBuffer pkgName = new StringBuffer(IPackageFragment.DEFAULT_PACKAGE_NAME);
579 for (int i= mySegmentCount; i < pkgSegmentCount; i++) {
580 if (i > mySegmentCount) {
581 pkgName.append('.');
582 }
583 pkgName.append(pkgPath.segment(i));
584 }
585 return pkgName.toString();
586 }
587
588 /**
589 * @see IJavaElement
590 */
591 @Override
getPath()592 public IPath getPath() {
593 return internalPath();
594 }
595
internalPath()596 public IPath internalPath() {
597 return resource().getFullPath();
598 }
599 /*
600 * @see IPackageFragmentRoot
601 */
602 @Override
getRawClasspathEntry()603 public IClasspathEntry getRawClasspathEntry() throws JavaModelException {
604
605 IClasspathEntry rawEntry = null;
606 JavaProject project = (JavaProject)getJavaProject();
607 project.getResolvedClasspath(); // force the reverse rawEntry cache to be populated
608 Map rootPathToRawEntries = project.getPerProjectInfo().rootPathToRawEntries;
609 if (rootPathToRawEntries != null) {
610 rawEntry = (IClasspathEntry) rootPathToRawEntries.get(getPath());
611 }
612 if (rawEntry == null) {
613 throw new JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.ELEMENT_NOT_ON_CLASSPATH, this));
614 }
615 return rawEntry;
616 }
617 /*
618 * @see IPackageFragmentRoot
619 */
620 @Override
getResolvedClasspathEntry()621 public IClasspathEntry getResolvedClasspathEntry() throws JavaModelException {
622 IClasspathEntry resolvedEntry = null;
623 JavaProject project = (JavaProject)getJavaProject();
624 project.getResolvedClasspath(); // force the resolved entry cache to be populated
625 Map rootPathToResolvedEntries = project.getPerProjectInfo().rootPathToResolvedEntries;
626 if (rootPathToResolvedEntries != null) {
627 resolvedEntry = (IClasspathEntry) rootPathToResolvedEntries.get(getPath());
628 }
629 if (resolvedEntry == null) {
630 throw new JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.ELEMENT_NOT_ON_CLASSPATH, this));
631 }
632 return resolvedEntry;
633 }
634
635
636 @Override
resource()637 public IResource resource() {
638 if (this.resource != null) // perf improvement to avoid message send in resource()
639 return this.resource;
640 return super.resource();
641 }
642 /*
643 * @see IJavaElement
644 */
645 @Override
resource(PackageFragmentRoot root)646 public IResource resource(PackageFragmentRoot root) {
647 return this.resource;
648 }
649
650 /**
651 * @see IPackageFragmentRoot
652 */
653 @Override
getSourceAttachmentPath()654 public IPath getSourceAttachmentPath() throws JavaModelException {
655 if (getKind() != K_BINARY) return null;
656
657 // 1) look source attachment property (set iff attachSource(...) was called
658 IPath path = getPath();
659 String serverPathString= Util.getSourceAttachmentProperty(path);
660 if (serverPathString != null) {
661 int index= serverPathString.lastIndexOf(ATTACHMENT_PROPERTY_DELIMITER);
662 if (index < 0) {
663 // no root path specified
664 return new Path(serverPathString);
665 } else {
666 String serverSourcePathString= serverPathString.substring(0, index);
667 return new Path(serverSourcePathString);
668 }
669 }
670
671 // 2) look at classpath entry
672 IClasspathEntry entry = ((JavaProject) getParent()).getClasspathEntryFor(path);
673 IPath sourceAttachmentPath;
674 if (entry != null && (sourceAttachmentPath = entry.getSourceAttachmentPath()) != null)
675 return sourceAttachmentPath;
676
677 // 3) look for a recommendation
678 entry = findSourceAttachmentRecommendation();
679 if (entry != null && (sourceAttachmentPath = entry.getSourceAttachmentPath()) != null) {
680 return sourceAttachmentPath;
681 }
682
683 return null;
684 }
685
686 /**
687 * For use by <code>AttachSourceOperation</code> only.
688 * Sets the source mapper associated with this root.
689 */
setSourceMapper(SourceMapper mapper)690 public void setSourceMapper(SourceMapper mapper) throws JavaModelException {
691 ((PackageFragmentRootInfo) getElementInfo()).setSourceMapper(mapper);
692 }
693
694
695
696 /**
697 * @see IPackageFragmentRoot
698 */
699 @Override
getSourceAttachmentRootPath()700 public IPath getSourceAttachmentRootPath() throws JavaModelException {
701 if (getKind() != K_BINARY) return null;
702
703 // 1) look source attachment property (set iff attachSource(...) was called
704 IPath path = getPath();
705 String serverPathString= Util.getSourceAttachmentProperty(path);
706 if (serverPathString != null) {
707 int index = serverPathString.lastIndexOf(ATTACHMENT_PROPERTY_DELIMITER);
708 if (index == -1) return null;
709 String serverRootPathString= IPackageFragmentRoot.DEFAULT_PACKAGEROOT_PATH;
710 if (index != serverPathString.length() - 1) {
711 serverRootPathString= serverPathString.substring(index + 1);
712 }
713 return new Path(serverRootPathString);
714 }
715
716 // 2) look at classpath entry
717 IClasspathEntry entry = ((JavaProject) getParent()).getClasspathEntryFor(path);
718 IPath sourceAttachmentRootPath;
719 if (entry != null && (sourceAttachmentRootPath = entry.getSourceAttachmentRootPath()) != null)
720 return sourceAttachmentRootPath;
721
722 // 3) look for a recomendation
723 entry = findSourceAttachmentRecommendation();
724 if (entry != null && (sourceAttachmentRootPath = entry.getSourceAttachmentRootPath()) != null)
725 return sourceAttachmentRootPath;
726
727 return null;
728 }
729
730 /**
731 * @see JavaElement
732 */
733 @Override
getSourceMapper()734 public SourceMapper getSourceMapper() {
735 SourceMapper mapper;
736 try {
737 PackageFragmentRootInfo rootInfo = (PackageFragmentRootInfo) getElementInfo();
738 mapper = rootInfo.getSourceMapper();
739 if (mapper == null) {
740 // first call to this method
741 IPath sourcePath= getSourceAttachmentPath();
742 IPath rootPath= getSourceAttachmentRootPath();
743 if (sourcePath == null)
744 mapper = createSourceMapper(getPath(), rootPath); // attach root to itself
745 else
746 mapper = createSourceMapper(sourcePath, rootPath);
747 rootInfo.setSourceMapper(mapper);
748 }
749 } catch (JavaModelException e) {
750 // no source can be attached
751 mapper = null;
752 }
753 return mapper;
754 }
755
756 /**
757 * @see IJavaElement
758 */
759 @Override
getUnderlyingResource()760 public IResource getUnderlyingResource() throws JavaModelException {
761 if (!exists()) throw newNotPresentException();
762 return resource();
763 }
764
765 /**
766 * @see IParent
767 */
768 @Override
hasChildren()769 public boolean hasChildren() throws JavaModelException {
770 // a package fragment root always has the default package as a child
771 return true;
772 }
773
774 @Override
hashCode()775 public int hashCode() {
776 return resource().hashCode();
777 }
778
ignoreOptionalProblems()779 public boolean ignoreOptionalProblems() {
780 try {
781 return ((PackageFragmentRootInfo) getElementInfo()).ignoreOptionalProblems(this);
782 } catch (JavaModelException e) {
783 return false;
784 }
785 }
786
787 /**
788 * @see IPackageFragmentRoot
789 */
790 @Override
isArchive()791 public boolean isArchive() {
792 return false;
793 }
794
795 /**
796 * @see IPackageFragmentRoot
797 */
798 @Override
isExternal()799 public boolean isExternal() {
800 return false;
801 }
802
803 /*
804 * Validate whether this package fragment root is on the classpath of its project.
805 */
validateOnClasspath()806 protected IStatus validateOnClasspath() {
807
808 IPath path = getPath();
809 try {
810 // check package fragment root on classpath of its project
811 JavaProject project = (JavaProject) getJavaProject();
812 IClasspathEntry entry = project.getClasspathEntryFor(path);
813 if (entry != null) {
814 return Status.OK_STATUS;
815 }
816 } catch(JavaModelException e){
817 // could not read classpath, then assume it is outside
818 return e.getJavaModelStatus();
819 }
820 return new JavaModelStatus(IJavaModelStatusConstants.ELEMENT_NOT_ON_CLASSPATH, this);
821 }
822
823 @Override
move( IPath destination, int updateResourceFlags, int updateModelFlags, IClasspathEntry sibling, IProgressMonitor monitor)824 public void move(
825 IPath destination,
826 int updateResourceFlags,
827 int updateModelFlags,
828 IClasspathEntry sibling,
829 IProgressMonitor monitor)
830 throws JavaModelException {
831
832 MovePackageFragmentRootOperation op =
833 new MovePackageFragmentRootOperation(this, destination, updateResourceFlags, updateModelFlags, sibling);
834 op.runOperation(monitor);
835 }
836
837 /**
838 * @private Debugging purposes
839 */
840 @Override
toStringInfo(int tab, StringBuffer buffer, Object info, boolean showResolvedInfo)841 protected void toStringInfo(int tab, StringBuffer buffer, Object info, boolean showResolvedInfo) {
842 buffer.append(tabString(tab));
843 IPath path = getPath();
844 if (isExternal()) {
845 buffer.append(path.toOSString());
846 } else if (getJavaProject().getElementName().equals(path.segment(0))) {
847 if (path.segmentCount() == 1) {
848 buffer.append("<project root>"); //$NON-NLS-1$
849 } else {
850 buffer.append(path.removeFirstSegments(1).makeRelative());
851 }
852 } else {
853 buffer.append(path);
854 }
855 if (info == null) {
856 buffer.append(" (not open)"); //$NON-NLS-1$
857 }
858 }
859
860 @Override
validateExistence(IResource underlyingResource)861 protected IStatus validateExistence(IResource underlyingResource) {
862 // check whether this pkg fragment root can be opened
863 IStatus status = validateOnClasspath();
864 if (!status.isOK())
865 return status;
866 if (!resourceExists(underlyingResource))
867 return newDoesNotExistStatus();
868 return JavaModelStatus.VERIFIED_OK;
869 }
870
871 /**
872 * Possible failures: <ul>
873 * <li>ELEMENT_NOT_PRESENT - the root supplied to the operation
874 * does not exist
875 * <li>INVALID_ELEMENT_TYPES - the root is not of kind K_BINARY
876 * <li>RELATIVE_PATH - the path supplied to this operation must be
877 * an absolute path
878 * </ul>
879 */
verifyAttachSource(IPath sourcePath)880 protected void verifyAttachSource(IPath sourcePath) throws JavaModelException {
881 if (!exists()) {
882 throw newNotPresentException();
883 } else if (getKind() != K_BINARY) {
884 throw new JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.INVALID_ELEMENT_TYPES, this));
885 } else if (sourcePath != null && !sourcePath.isAbsolute()) {
886 throw new JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.RELATIVE_PATH, sourcePath));
887 }
888 }
889 /**
890 * Returns the relative path within an archive for the given class file name. In certain
891 * kind of archives, such as a JMOD file, class files are stored in a nested folder, as opposed
892 * to directly under the root. It is the responsibility of such package fragment roots to
893 * provide the custom behavior.
894 *
895 * @param classname
896 * @return the relative path for the class file within the archive
897 */
getClassFilePath(String classname)898 public String getClassFilePath(String classname) {
899 return classname;
900 }
901 @Override
getModuleDescription()902 public IModuleDescription getModuleDescription() {
903 if (isComplianceJava9OrHigher()) {
904 return getSourceModuleDescription();
905 }
906 return null;
907 }
908
getSourceModuleDescription()909 private IModuleDescription getSourceModuleDescription() {
910 try {
911 IJavaElement[] pkgs = getChildren();
912 for (int j = 0, length = pkgs.length; j < length; j++) {
913 // only look in the default package
914 if (pkgs[j].getElementName().length() == 0) {
915 OpenableElementInfo info = null;
916 if (getKind() == IPackageFragmentRoot.K_SOURCE) {
917 ICompilationUnit unit = ((PackageFragment) pkgs[j])
918 .getCompilationUnit(TypeConstants.MODULE_INFO_FILE_NAME_STRING);
919 if (unit instanceof CompilationUnit && unit.exists()) {
920 info = (CompilationUnitElementInfo) ((CompilationUnit) unit)
921 .getElementInfo();
922 if (info != null)
923 return info.getModule();
924 }
925 } else {
926 IModularClassFile classFile = ((IPackageFragment)pkgs[j]).getModularClassFile();
927 if (classFile.exists()) {
928 return classFile.getModule();
929 }
930 }
931 break;
932 }
933 }
934 } catch (JavaModelException e) {
935 Util.log(e);
936 }
937 return null;
938 }
939
getAutomaticModuleDescription()940 public IModuleDescription getAutomaticModuleDescription() throws JavaModelException {
941 return getAutomaticModuleDescription(getResolvedClasspathEntry());
942 }
943
getAutomaticModuleDescription(IClasspathEntry classpathEntry)944 IModuleDescription getAutomaticModuleDescription(IClasspathEntry classpathEntry) {
945 String elementName = getElementName();
946 Manifest manifest = null;
947 switch (classpathEntry.getEntryKind()) {
948 case IClasspathEntry.CPE_SOURCE:
949 manifest = ((JavaProject) getJavaProject()).getManifest();
950 elementName = getJavaProject().getElementName();
951 break;
952 case IClasspathEntry.CPE_LIBRARY:
953 manifest = getManifest();
954 break;
955 case IClasspathEntry.CPE_PROJECT:
956 JavaProject javaProject = (JavaProject) getJavaModel().getJavaProject(classpathEntry.getPath().lastSegment());
957 manifest = javaProject.getManifest();
958 elementName = javaProject.getElementName();
959 break;
960 }
961 boolean nameFromManifest = true;
962 char[] moduleName = AutomaticModuleNaming.determineAutomaticModuleNameFromManifest(manifest);
963 if (moduleName == null) {
964 nameFromManifest = false;
965 moduleName = AutomaticModuleNaming.determineAutomaticModuleNameFromFileName(elementName, true, isArchive());
966 }
967 return new AbstractModule.AutoModule(this, String.valueOf(moduleName), nameFromManifest);
968 }
969
970 /** @see org.eclipse.jdt.internal.compiler.env.IModulePathEntry#hasCompilationUnit(String, String) */
hasCompilationUnit(String qualifiedPackageName, String moduleName)971 public boolean hasCompilationUnit(String qualifiedPackageName, String moduleName) {
972 IPackageFragment fragment = getPackageFragment(qualifiedPackageName.replace('/', '.'));
973 try {
974 if (fragment.exists())
975 return fragment.containsJavaResources();
976 } catch (JavaModelException e) {
977 // silent
978 }
979 return false;
980 }
981 /** Convenience lookup, though currently only JarPackageFragmentRoot is searched for a manifest. */
getManifest()982 public Manifest getManifest() {
983 return null;
984 }
985
isComplianceJava9OrHigher()986 protected boolean isComplianceJava9OrHigher() {
987 IJavaProject javaProject = getJavaProject();
988 return isComplianceJava9OrHigher(javaProject);
989 }
990
isComplianceJava9OrHigher(IJavaProject javaProject)991 private static boolean isComplianceJava9OrHigher(IJavaProject javaProject) {
992 if (javaProject == null) {
993 return false;
994 }
995 return CompilerOptions.versionToJdkLevel(javaProject.getOption(JavaCore.COMPILER_COMPLIANCE, true)) >= ClassFileConstants.JDK9;
996 }
997 }
998