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