| /******************************************************************************* |
| * Copyright (c) 2000, 2017 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jdt.ui; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.List; |
| |
| import org.eclipse.core.runtime.CoreException; |
| |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IFolder; |
| import org.eclipse.core.resources.IResource; |
| |
| import org.eclipse.jface.viewers.ITreeContentProvider; |
| import org.eclipse.jface.viewers.Viewer; |
| |
| import org.eclipse.jdt.core.IClassFile; |
| import org.eclipse.jdt.core.ICompilationUnit; |
| import org.eclipse.jdt.core.IJarEntryResource; |
| import org.eclipse.jdt.core.IJavaElement; |
| import org.eclipse.jdt.core.IJavaElementDelta; |
| import org.eclipse.jdt.core.IJavaModel; |
| import org.eclipse.jdt.core.IJavaProject; |
| import org.eclipse.jdt.core.IPackageFragment; |
| import org.eclipse.jdt.core.IPackageFragmentRoot; |
| import org.eclipse.jdt.core.IParent; |
| import org.eclipse.jdt.core.ISourceReference; |
| import org.eclipse.jdt.core.ITypeRoot; |
| import org.eclipse.jdt.core.JavaCore; |
| import org.eclipse.jdt.core.JavaModelException; |
| |
| import org.eclipse.jdt.internal.corext.util.JavaModelUtil; |
| |
| /** |
| * A base content provider for Java elements. It provides access to the |
| * Java element hierarchy without listening to changes in the Java model. |
| * If updating the presentation on Java model change is required than |
| * clients have to subclass, listen to Java model changes and have to update |
| * the UI using corresponding methods provided by the JFace viewers or their |
| * own UI presentation. |
| * <p> |
| * The following Java element hierarchy is surfaced by this content provider: |
| * <pre> |
| Java model (<code>IJavaModel</code>) |
| Java project (<code>IJavaProject</code>) |
| package fragment root (<code>IPackageFragmentRoot</code>) |
| package fragment (<code>IPackageFragment</code>) |
| compilation unit (<code>ICompilationUnit</code>) |
| binary class file (<code>IClassFile</code>) |
| * </pre> |
| * <p> |
| * Note that when the entire Java project is declared to be package fragment root, |
| * the corresponding package fragment root element that normally appears between the |
| * Java project and the package fragments is automatically filtered out. |
| * </p> |
| * |
| * @since 2.0 |
| */ |
| public class StandardJavaElementContentProvider implements ITreeContentProvider, IWorkingCopyProvider { |
| |
| protected static final Object[] NO_CHILDREN= new Object[0]; |
| protected boolean fProvideMembers; |
| protected boolean fProvideWorkingCopy; |
| |
| /** |
| * Creates a new content provider. The content provider does not |
| * provide members of compilation units or class files. |
| */ |
| public StandardJavaElementContentProvider() { |
| this(false); |
| } |
| |
| /** |
| * @param provideMembers if <code>true</code> members below compilation units |
| * @param provideWorkingCopy if <code>true</code> working copies are provided |
| * @deprecated Use {@link #StandardJavaElementContentProvider(boolean)} instead. |
| * Since 3.0 compilation unit children are always provided as working copies. The Java Model |
| * does not support the 'original' mode anymore. |
| */ |
| @Deprecated |
| public StandardJavaElementContentProvider(boolean provideMembers, boolean provideWorkingCopy) { |
| this(provideMembers); |
| } |
| |
| |
| /** |
| * Creates a new <code>StandardJavaElementContentProvider</code>. |
| * |
| * @param provideMembers if <code>true</code> members below compilation units |
| * and class files are provided. |
| */ |
| public StandardJavaElementContentProvider(boolean provideMembers) { |
| fProvideMembers= provideMembers; |
| fProvideWorkingCopy= provideMembers; |
| } |
| |
| /** |
| * Returns whether members are provided when asking |
| * for a compilation units or class file for its children. |
| * |
| * @return <code>true</code> if the content provider provides members; |
| * otherwise <code>false</code> is returned |
| */ |
| public boolean getProvideMembers() { |
| return fProvideMembers; |
| } |
| |
| /** |
| * Sets whether the content provider is supposed to return members |
| * when asking a compilation unit or class file for its children. |
| * |
| * @param b if <code>true</code> then members are provided. |
| * If <code>false</code> compilation units and class files are the |
| * leaves provided by this content provider. |
| */ |
| public void setProvideMembers(boolean b) { |
| //hello |
| fProvideMembers= b; |
| } |
| |
| /** |
| * @return returns <code>true</code> if working copies are provided |
| * @deprecated Since 3.0 compilation unit children are always provided as working copies. The Java model |
| * does not support the 'original' mode anymore. |
| */ |
| @Deprecated |
| public boolean getProvideWorkingCopy() { |
| return fProvideWorkingCopy; |
| } |
| |
| /** |
| * @param b specifies if working copies should be provided |
| * @deprecated Since 3.0 compilation unit children are always provided from the working copy. The Java model |
| * offers a unified world and does not support the 'original' mode anymore. |
| */ |
| @Deprecated |
| public void setProvideWorkingCopy(boolean b) { |
| fProvideWorkingCopy= b; |
| } |
| |
| @Override |
| public boolean providesWorkingCopies() { |
| return getProvideWorkingCopy(); |
| } |
| |
| @Override |
| public Object[] getElements(Object parent) { |
| return getChildren(parent); |
| } |
| |
| @Override |
| public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { |
| } |
| |
| @Override |
| public void dispose() { |
| } |
| |
| @Override |
| public Object[] getChildren(Object element) { |
| if (!exists(element)) |
| return NO_CHILDREN; |
| |
| try { |
| if (element instanceof IJavaModel) |
| return getJavaProjects((IJavaModel)element); |
| |
| if (element instanceof IJavaProject) |
| return getPackageFragmentRoots((IJavaProject)element); |
| |
| if (element instanceof IPackageFragmentRoot) |
| return getPackageFragmentRootContent((IPackageFragmentRoot)element); |
| |
| if (element instanceof IPackageFragment) |
| return getPackageContent((IPackageFragment)element); |
| |
| if (element instanceof IFolder) |
| return getFolderContent((IFolder)element); |
| |
| if (element instanceof IJarEntryResource) { |
| return ((IJarEntryResource) element).getChildren(); |
| } |
| |
| if (getProvideMembers() && element instanceof ISourceReference && element instanceof IParent) { |
| return ((IParent)element).getChildren(); |
| } |
| } catch (CoreException e) { |
| return NO_CHILDREN; |
| } |
| return NO_CHILDREN; |
| } |
| |
| @Override |
| public boolean hasChildren(Object element) { |
| if (getProvideMembers()) { |
| // assume CUs and class files are never empty |
| if (element instanceof ICompilationUnit || |
| element instanceof IClassFile) { |
| return true; |
| } |
| } else { |
| // don't allow to drill down into a compilation unit or class file |
| if (element instanceof ICompilationUnit || |
| element instanceof IClassFile || |
| element instanceof IFile) |
| return false; |
| } |
| |
| if (element instanceof IJavaProject) { |
| IJavaProject jp= (IJavaProject)element; |
| if (!jp.getProject().isOpen()) { |
| return false; |
| } |
| } |
| |
| if (element instanceof IParent) { |
| try { |
| // when we have Java children return true, else we fetch all the children |
| if (((IParent)element).hasChildren()) |
| return true; |
| } catch(JavaModelException e) { |
| return true; |
| } |
| } |
| Object[] children= getChildren(element); |
| return (children != null) && children.length > 0; |
| } |
| |
| @Override |
| public Object getParent(Object element) { |
| if (!exists(element)) |
| return null; |
| return internalGetParent(element); |
| } |
| |
| /** |
| * Evaluates all children of a given {@link IPackageFragmentRoot}. Clients can override this method. |
| * @param root The root to evaluate the children for. |
| * @return The children of the root |
| * @exception JavaModelException if the package fragment root does not exist or if an |
| * exception occurs while accessing its corresponding resource |
| * |
| * @since 3.3 |
| */ |
| protected Object[] getPackageFragmentRootContent(IPackageFragmentRoot root) throws JavaModelException { |
| IJavaElement[] fragments= root.getChildren(); |
| if (JavaModelUtil.is9OrHigher(root.getJavaProject())) { |
| ITypeRoot moduleRoot= getModuleRoot(root); |
| if (moduleRoot != null) { |
| IJavaElement defaultPackage= moduleRoot.getParent(); |
| if (!JavaModelUtil.containsOrdinaryCompilationUnit((IPackageFragment) defaultPackage)) { |
| // replace "empty" default package with module-info |
| for (int i= 0; i < fragments.length; i++) { |
| if (fragments[i].equals(defaultPackage)) { |
| fragments= Arrays.copyOf(fragments, fragments.length); |
| fragments[i]= moduleRoot; |
| break; |
| } |
| } |
| } else { |
| // add module-info |
| int length= fragments.length; |
| fragments= Arrays.copyOf(fragments, length+1); |
| fragments[length]= moduleRoot; |
| } |
| } |
| } |
| if (isProjectPackageFragmentRoot(root)) { |
| return fragments; |
| } |
| Object[] nonJavaResources= root.getNonJavaResources(); |
| if (nonJavaResources == null) |
| return fragments; |
| return concatenate(fragments, nonJavaResources); |
| } |
| |
| ITypeRoot getModuleRoot(IPackageFragmentRoot root) throws JavaModelException { |
| IJavaElement module= root.getModuleDescription(); |
| if (module == null) { |
| // may still need to show an empty module-info.java, which otherwise would remain invisible |
| IPackageFragment dfltPack= root.getPackageFragment(""); //$NON-NLS-1$ |
| if (dfltPack.exists()) { |
| for (IJavaElement child : dfltPack.getChildren()) { |
| if (child instanceof ITypeRoot |
| && JavaModelUtil.isModuleInfo((ITypeRoot) child) |
| && ((ITypeRoot) child).getChildren().length == 0) { |
| return (ITypeRoot) child; |
| } |
| } |
| } |
| return null; |
| } else { |
| return (ITypeRoot) module.getParent(); |
| } |
| } |
| |
| /** |
| * Evaluates all children of a given {@link IJavaProject}. Clients can override this method. |
| * @param project The Java project to evaluate the children for. |
| * @return The children of the project. Typically these are package fragment roots but can also be other elements. |
| * @exception JavaModelException if the Java project does not exist or if an |
| * exception occurs while accessing its corresponding resource |
| */ |
| protected Object[] getPackageFragmentRoots(IJavaProject project) throws JavaModelException { |
| if (!project.getProject().isOpen()) |
| return NO_CHILDREN; |
| |
| IPackageFragmentRoot[] roots= project.getPackageFragmentRoots(); |
| List<Object> list= new ArrayList<>(roots.length); |
| // filter out package fragments that correspond to projects and |
| // replace them with the package fragments directly |
| for (int i= 0; i < roots.length; i++) { |
| IPackageFragmentRoot root= roots[i]; |
| if (isProjectPackageFragmentRoot(root)) { |
| Object[] fragments= getPackageFragmentRootContent(root); |
| for (int j= 0; j < fragments.length; j++) { |
| list.add(fragments[j]); |
| } |
| } else { |
| list.add(root); |
| } |
| } |
| Object[] resources= project.getNonJavaResources(); |
| for (int i= 0; i < resources.length; i++) { |
| list.add(resources[i]); |
| } |
| return list.toArray(); |
| } |
| |
| /** |
| * Evaluates all Java projects of a given {@link IJavaModel}. Clients can override this method. |
| * |
| * @param jm the Java model |
| * @return the projects |
| * @throws JavaModelException thrown if accessing the model failed |
| */ |
| protected Object[] getJavaProjects(IJavaModel jm) throws JavaModelException { |
| return jm.getJavaProjects(); |
| } |
| |
| /** |
| * Evaluates all children of a given {@link IPackageFragment}. Clients can override this method. |
| * @param fragment The fragment to evaluate the children for. |
| * @return The children of the given package fragment. |
| * @exception JavaModelException if the package fragment does not exist or if an |
| * exception occurs while accessing its corresponding resource |
| * |
| * @since 3.3 |
| */ |
| protected Object[] getPackageContent(IPackageFragment fragment) throws JavaModelException { |
| if (fragment.getKind() == IPackageFragmentRoot.K_SOURCE) { |
| ITypeRoot[] compilationUnits= filterModuleInfo(fragment, fragment.getCompilationUnits()); |
| return concatenate(compilationUnits, fragment.getNonJavaResources()); |
| } |
| ITypeRoot[] classFiles= fragment.getOrdinaryClassFiles(); |
| return concatenate(classFiles, fragment.getNonJavaResources()); |
| } |
| |
| private ITypeRoot[] filterModuleInfo(IPackageFragment fragment, ITypeRoot[] units) { |
| if (fragment.isDefaultPackage() && JavaModelUtil.is9OrHigher(fragment.getJavaProject())) { |
| int count= 0; |
| ITypeRoot[] newUnits= new ITypeRoot[units.length]; |
| for (int i= 0; i < units.length; i++) { |
| if (!JavaModelUtil.isModuleInfo(units[i])) |
| newUnits[count++]= units[i]; |
| } |
| if (count < units.length) |
| return Arrays.copyOf(newUnits, count); |
| } |
| return units; |
| } |
| |
| /** |
| * Evaluates all children of a given {@link IFolder}. Clients can override this method. |
| * @param folder The folder to evaluate the children for. |
| * @return The children of the given folder. |
| * @exception CoreException if the folder does not exist. |
| * |
| * @since 3.3 |
| */ |
| protected Object[] getFolderContent(IFolder folder) throws CoreException { |
| IResource[] members= folder.members(); |
| IJavaProject javaProject= JavaCore.create(folder.getProject()); |
| if (javaProject == null || !javaProject.exists()) |
| return members; |
| boolean isFolderOnClasspath = javaProject.isOnClasspath(folder); |
| List<IResource> nonJavaResources= new ArrayList<>(); |
| // Can be on classpath but as a member of non-java resource folder |
| for (int i= 0; i < members.length; i++) { |
| IResource member= members[i]; |
| // A resource can also be a java element |
| // in the case of exclusion and inclusion filters. |
| // We therefore exclude Java elements from the list |
| // of non-Java resources. |
| if (isFolderOnClasspath) { |
| if (javaProject.findPackageFragmentRoot(member.getFullPath()) == null) { |
| nonJavaResources.add(member); |
| } |
| } else if (!javaProject.isOnClasspath(member)) { |
| nonJavaResources.add(member); |
| } else { |
| IJavaElement element= JavaCore.create(member, javaProject); |
| if (element instanceof IPackageFragmentRoot |
| && javaProject.equals(element.getJavaProject()) |
| && ((IPackageFragmentRoot)element).getKind() != IPackageFragmentRoot.K_SOURCE) { |
| // don't skip libs and class folders on the classpath of their project |
| nonJavaResources.add(member); |
| } |
| } |
| } |
| return nonJavaResources.toArray(); |
| } |
| |
| /** |
| * Tests if the a Java element delta contains a class path change |
| * |
| * @param delta the Java element delta |
| * @return returns <code>true</code> if the delta contains a class path change |
| */ |
| protected boolean isClassPathChange(IJavaElementDelta delta) { |
| |
| // need to test the flags only for package fragment roots |
| if (delta.getElement().getElementType() != IJavaElement.PACKAGE_FRAGMENT_ROOT) |
| return false; |
| |
| int flags= delta.getFlags(); |
| return (delta.getKind() == IJavaElementDelta.CHANGED && |
| ((flags & IJavaElementDelta.F_ADDED_TO_CLASSPATH) != 0) || |
| ((flags & IJavaElementDelta.F_REMOVED_FROM_CLASSPATH) != 0) || |
| ((flags & IJavaElementDelta.F_REORDER) != 0)); |
| } |
| |
| /** |
| * Note: This method is for internal use only. Clients should not call this method. |
| * |
| * @param root the package fragment root |
| * @return returns the element representing the root. |
| * |
| * @noreference This method is not intended to be referenced by clients. |
| */ |
| protected Object skipProjectPackageFragmentRoot(IPackageFragmentRoot root) { |
| if (isProjectPackageFragmentRoot(root)) |
| return root.getParent(); |
| return root; |
| } |
| |
| /** |
| * Tests if the given element is a empty package fragment. |
| * |
| * @param element the element to test |
| * @return returns <code>true</code> if the package fragment is empty |
| * @throws JavaModelException thrown if accessing the element failed |
| */ |
| protected boolean isPackageFragmentEmpty(IJavaElement element) throws JavaModelException { |
| if (element instanceof IPackageFragment) { |
| IPackageFragment fragment= (IPackageFragment)element; |
| if (fragment.exists() && !(fragment.hasChildren() || fragment.getNonJavaResources().length > 0) && fragment.hasSubpackages()) |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Tests if the package fragment root is located on the project. |
| * |
| * @param root the package fragment root |
| * @return returns <code>true</code> if the package fragment root is the located on the project |
| */ |
| protected boolean isProjectPackageFragmentRoot(IPackageFragmentRoot root) { |
| IJavaProject javaProject= root.getJavaProject(); |
| return javaProject != null && javaProject.getPath().equals(root.getPath()); |
| } |
| |
| /** |
| * Note: This method is for internal use only. Clients should not call this method. |
| * |
| * @param element the element to test |
| * @return returns <code>true</code> if the element exists |
| * |
| * @noreference This method is not intended to be referenced by clients. |
| */ |
| protected boolean exists(Object element) { |
| if (element == null) { |
| return false; |
| } |
| if (element instanceof IResource) { |
| return ((IResource)element).exists(); |
| } |
| if (element instanceof IJavaElement) { |
| return ((IJavaElement)element).exists(); |
| } |
| return true; |
| } |
| |
| /** |
| * Note: This method is for internal use only. Clients should not call this method. |
| * |
| * @param element the element |
| * @return the parent of the element |
| * |
| * @noreference This method is not intended to be referenced by clients. |
| */ |
| protected Object internalGetParent(Object element) { |
| |
| // try to map resources to the containing package fragment |
| if (element instanceof IResource) { |
| IResource parent= ((IResource)element).getParent(); |
| IJavaElement jParent= JavaCore.create(parent); |
| // http://bugs.eclipse.org/bugs/show_bug.cgi?id=31374 |
| if (jParent != null && jParent.exists()) |
| return jParent; |
| return parent; |
| } else if (element instanceof IJavaElement) { |
| IJavaElement parent= ((IJavaElement) element).getParent(); |
| // for package fragments that are contained in a project package fragment |
| // we have to skip the package fragment root as the parent. |
| if (element instanceof IPackageFragment) { |
| return skipProjectPackageFragmentRoot((IPackageFragmentRoot) parent); |
| } |
| if (element instanceof ITypeRoot && JavaModelUtil.isModuleInfo((ITypeRoot) element) |
| && parent instanceof IPackageFragment && ((IPackageFragment) parent).isDefaultPackage()) { |
| return parent.getParent(); // module-info in default package: skip the default package |
| } |
| return parent; |
| } else if (element instanceof IJarEntryResource) { |
| return ((IJarEntryResource) element).getParent(); |
| } |
| return null; |
| } |
| |
| /** |
| * Utility method to concatenate two arrays. |
| * |
| * @param a1 the first array |
| * @param a2 the second array |
| * @return the concatenated array |
| */ |
| protected static Object[] concatenate(Object[] a1, Object[] a2) { |
| int a1Len= a1.length; |
| int a2Len= a2.length; |
| if (a1Len == 0) return a2; |
| if (a2Len == 0) return a1; |
| Object[] res= new Object[a1Len + a2Len]; |
| System.arraycopy(a1, 0, res, 0, a1Len); |
| System.arraycopy(a2, 0, res, a1Len, a2Len); |
| return res; |
| } |
| |
| |
| } |