| /******************************************************************************* |
| * Copyright (c) 2000, 2015 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.internal.ui.browsing; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Iterator; |
| import java.util.List; |
| |
| import org.eclipse.swt.widgets.Control; |
| import org.eclipse.swt.widgets.Display; |
| |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IAdaptable; |
| |
| import org.eclipse.core.resources.IFolder; |
| import org.eclipse.core.resources.IResource; |
| |
| import org.eclipse.jface.viewers.ITreeContentProvider; |
| import org.eclipse.jface.viewers.StructuredViewer; |
| import org.eclipse.jface.viewers.TreeViewer; |
| |
| import org.eclipse.jdt.core.ICompilationUnit; |
| import org.eclipse.jdt.core.IJavaElement; |
| import org.eclipse.jdt.core.IJavaElementDelta; |
| import org.eclipse.jdt.core.IJavaProject; |
| import org.eclipse.jdt.core.IPackageFragment; |
| import org.eclipse.jdt.core.IPackageFragmentRoot; |
| import org.eclipse.jdt.core.JavaCore; |
| import org.eclipse.jdt.core.JavaModelException; |
| |
| import org.eclipse.jdt.internal.ui.JavaPlugin; |
| |
| /** |
| * Tree content provider for the hierarchical layout in the packages view. |
| * <p> |
| * XXX: The standard Java browsing part content provider needs and calls |
| * the browsing part/view. This class currently doesn't need to do so |
| * but might be required to later. |
| * </p> |
| */ |
| class PackagesViewHierarchicalContentProvider extends LogicalPackagesProvider implements ITreeContentProvider { |
| |
| public PackagesViewHierarchicalContentProvider(StructuredViewer viewer){ |
| super(viewer); |
| } |
| |
| /* |
| * @see org.eclipse.jface.viewers.ITreeContentProvider#getChildren(Object) |
| */ |
| @Override |
| public Object[] getChildren(Object parentElement) { |
| try { |
| if (parentElement instanceof IJavaElement) { |
| IJavaElement iJavaElement= (IJavaElement) parentElement; |
| int type= iJavaElement.getElementType(); |
| |
| switch (type) { |
| case IJavaElement.JAVA_PROJECT : |
| { |
| |
| //create new element mapping |
| fMapToLogicalPackage.clear(); |
| fMapToPackageFragments.clear(); |
| IJavaProject project= (IJavaProject) parentElement; |
| |
| List<IPackageFragment> list= new ArrayList<>(); |
| for (IPackageFragment fragment : getTopLevelChildrenByElementName(project.getPackageFragments())) { |
| IJavaElement el= fragment.getParent(); |
| if (el instanceof IPackageFragmentRoot) { |
| IPackageFragmentRoot root= (IPackageFragmentRoot) el; |
| if (!root.isArchive() || !root.isExternal()) |
| list.add(fragment); |
| } |
| } |
| |
| List<Object> folders= new ArrayList<>(); |
| for (IPackageFragmentRoot root : project.getPackageFragmentRoots()) { |
| IResource resource= root.getUnderlyingResource(); |
| if (resource != null && resource instanceof IFolder) { |
| folders.addAll(getFolders(((IFolder)resource).members())); |
| } |
| } |
| |
| Object[] logicalPackages= combineSamePackagesIntoLogialPackages(list.toArray(new IPackageFragment[list.size()])); |
| if (folders.size() > 0) { |
| if (logicalPackages.length > 0) |
| folders.addAll(Arrays.asList(logicalPackages)); |
| return folders.toArray(); |
| } else { |
| return logicalPackages; |
| } |
| } |
| |
| case IJavaElement.PACKAGE_FRAGMENT_ROOT : |
| { |
| IPackageFragmentRoot root= (IPackageFragmentRoot) parentElement; |
| |
| //create new element mapping |
| fMapToLogicalPackage.clear(); |
| fMapToPackageFragments.clear(); |
| IResource resource= root.getUnderlyingResource(); |
| if (root.isArchive() || root.isExternal()) { |
| IJavaElement[] els= root.getChildren(); |
| IPackageFragment[] fragments= getTopLevelChildrenByElementName(els); |
| addFragmentsToMap(fragments); |
| return fragments; |
| |
| } else if (resource != null && resource instanceof IFolder) { |
| List<IAdaptable> children= getFoldersAndElements(((IFolder)resource).members()); |
| |
| IPackageFragment defaultPackage= root.getPackageFragment(""); //$NON-NLS-1$ |
| if(defaultPackage.exists()) |
| children.add(defaultPackage); |
| |
| addFragmentsToMap(children); |
| return children.toArray(); |
| } else { |
| return NO_CHILDREN; |
| } |
| } |
| |
| case IJavaElement.PACKAGE_FRAGMENT : |
| { |
| IPackageFragment packageFragment= (IPackageFragment) parentElement; |
| if (packageFragment.isDefaultPackage()) |
| return NO_CHILDREN; |
| |
| IPackageFragmentRoot parent= (IPackageFragmentRoot) packageFragment.getParent(); |
| IPackageFragment[] fragments= findNextLevelChildrenByElementName(parent, packageFragment); |
| |
| addFragmentsToMap(fragments); |
| |
| Object[] nonJavaResources= packageFragment.getNonJavaResources(); |
| if (nonJavaResources.length == 0) { |
| return fragments; |
| } |
| ArrayList<Object> combined= new ArrayList<>(); |
| combined.addAll(Arrays.asList(fragments)); |
| for (Object curr : nonJavaResources) { |
| if (curr instanceof IFolder) { |
| combined.add(curr); |
| } |
| } |
| return combined.toArray(); |
| } |
| } |
| |
| //@Improve: rewrite using concatenate |
| } else if (parentElement instanceof LogicalPackage) { |
| |
| List<IPackageFragment> children= new ArrayList<>(); |
| LogicalPackage logicalPackage= (LogicalPackage) parentElement; |
| for (IPackageFragment fragment : logicalPackage.getFragments()) { |
| IPackageFragment[] objects= findNextLevelChildrenByElementName((IPackageFragmentRoot) fragment.getParent(), fragment); |
| children.addAll(Arrays.asList(objects)); |
| } |
| return combineSamePackagesIntoLogialPackages(children.toArray(new IPackageFragment[children.size()])); |
| } else if (parentElement instanceof IFolder) { |
| IFolder folder= (IFolder)parentElement; |
| IResource[] resources= folder.members(); |
| List<IAdaptable> children = getFoldersAndElements(resources); |
| addFragmentsToMap(children); |
| return children.toArray(); |
| } |
| |
| } catch (CoreException e) { |
| return NO_CHILDREN; |
| } |
| return NO_CHILDREN; |
| } |
| |
| private void addFragmentsToMap(List<IAdaptable> elements) { |
| List<IPackageFragment> packageFragments= new ArrayList<>(); |
| for (IAdaptable iAdaptable : elements) { |
| Object elem= iAdaptable; |
| if (elem instanceof IPackageFragment) |
| packageFragments.add((IPackageFragment) elem); |
| } |
| addFragmentsToMap(packageFragments.toArray(new IPackageFragment[packageFragments.size()])); |
| } |
| |
| private List<IAdaptable> getFoldersAndElements(IResource[] resources) { |
| List<IAdaptable> list= new ArrayList<>(); |
| for (IResource resource : resources) { |
| if (resource instanceof IFolder) { |
| IFolder folder= (IFolder) resource; |
| IJavaElement element= JavaCore.create(folder); |
| |
| if (element instanceof IPackageFragment) { |
| list.add(element); |
| } else { |
| list.add(folder); |
| } |
| } |
| } |
| return list; |
| } |
| |
| private List<IFolder> getFolders(IResource[] resources) { |
| List<IFolder> list= new ArrayList<>(); |
| for (IResource resource : resources) { |
| if (resource instanceof IFolder) { |
| IFolder folder= (IFolder) resource; |
| IJavaElement element= JavaCore.create(folder); |
| |
| if (element == null) { |
| list.add(folder); |
| } |
| } |
| } |
| return list; |
| } |
| |
| private IPackageFragment[] findNextLevelChildrenByElementName(IPackageFragmentRoot parent, IPackageFragment fragment) { |
| List<IPackageFragment> list= new ArrayList<>(); |
| try { |
| |
| String fragmentname= fragment.getElementName(); |
| for (IJavaElement element : parent.getChildren()) { |
| if (element instanceof IPackageFragment) { |
| IPackageFragment frag= (IPackageFragment) element; |
| |
| String name= element.getElementName(); |
| if (name.length() > fragmentname.length() && name.charAt(fragmentname.length()) == '.' && frag.exists() && !IPackageFragment.DEFAULT_PACKAGE_NAME.equals(fragmentname) && name.startsWith(fragmentname) && !name.equals(fragmentname)) { |
| String tail= name.substring(fragmentname.length() + 1); |
| if (!IPackageFragment.DEFAULT_PACKAGE_NAME.equals(tail) && tail.indexOf('.') == -1) { |
| list.add(frag); |
| } |
| } |
| } |
| } |
| |
| } catch (JavaModelException e) { |
| JavaPlugin.log(e); |
| } |
| return list.toArray(new IPackageFragment[list.size()]); |
| } |
| |
| private IPackageFragment[] getTopLevelChildrenByElementName(IJavaElement[] elements){ |
| List<IJavaElement> topLevelElements= new ArrayList<>(); |
| for (IJavaElement iJavaElement : elements) { |
| //if the name of the PackageFragment is the top level package it will contain no "." separators |
| if (iJavaElement instanceof IPackageFragment && iJavaElement.getElementName().indexOf('.')==-1){ |
| topLevelElements.add(iJavaElement); |
| } |
| } |
| return topLevelElements.toArray(new IPackageFragment[topLevelElements.size()]); |
| } |
| |
| /* |
| * @see org.eclipse.jface.viewers.ITreeContentProvider#getParent(Object) |
| */ |
| @Override |
| public Object getParent(Object element) { |
| |
| if (element instanceof IPackageFragment) { |
| IPackageFragment fragment= (IPackageFragment) element; |
| if(!fragment.exists()) |
| return null; |
| Object parent= getHierarchicalParent(fragment); |
| if(parent instanceof IPackageFragment) { |
| IPackageFragment pkgFragment= (IPackageFragment)parent; |
| LogicalPackage logicalPkg= findLogicalPackage(pkgFragment); |
| if (logicalPkg != null) |
| return logicalPkg; |
| else { |
| LogicalPackage lp= createLogicalPackage(pkgFragment); |
| if(lp == null) |
| return pkgFragment; |
| else return lp; |
| } |
| } |
| return parent; |
| |
| } else if(element instanceof LogicalPackage){ |
| LogicalPackage el= (LogicalPackage) element; |
| IPackageFragment fragment= el.getFragments()[0]; |
| Object parent= getHierarchicalParent(fragment); |
| |
| if(parent instanceof IPackageFragment){ |
| IPackageFragment pkgFragment= (IPackageFragment) parent; |
| LogicalPackage logicalPkg= findLogicalPackage(pkgFragment); |
| if (logicalPkg != null) |
| return logicalPkg; |
| else { |
| LogicalPackage lp= createLogicalPackage(pkgFragment); |
| if(lp == null) |
| return pkgFragment; |
| else return lp; |
| } |
| } else |
| return fragment.getJavaProject(); |
| } else if (element instanceof IFolder) { |
| IFolder folder = (IFolder) element; |
| IResource res = folder.getParent(); |
| |
| IJavaElement el = JavaCore.create(res); |
| if (el != null) { |
| return el; |
| } else { |
| return res; |
| } |
| } |
| return null; |
| } |
| |
| /* |
| * Check if the given IPackageFragment should be the member of a |
| * LogicalPackage and if so creates the LogicalPackage and adds it to the |
| * map. |
| */ |
| private LogicalPackage createLogicalPackage(IPackageFragment pkgFragment) { |
| if(!fInputIsProject) |
| return null; |
| |
| List<IPackageFragment> fragments= new ArrayList<>(); |
| try { |
| for (IPackageFragmentRoot root : pkgFragment.getJavaProject().getPackageFragmentRoots()) { |
| IPackageFragment fragment= root.getPackageFragment(pkgFragment.getElementName()); |
| if(fragment.exists() && !fragment.equals(pkgFragment)) |
| fragments.add(fragment); |
| } |
| if(!fragments.isEmpty()) { |
| LogicalPackage logicalPackage= new LogicalPackage(pkgFragment); |
| fMapToLogicalPackage.put(getKey(pkgFragment), logicalPackage); |
| Iterator<IPackageFragment> iter= fragments.iterator(); |
| while(iter.hasNext()){ |
| IPackageFragment f= iter.next(); |
| if(logicalPackage.belongs(f)){ |
| logicalPackage.add(f); |
| fMapToLogicalPackage.put(getKey(f), logicalPackage); |
| } |
| } |
| |
| return logicalPackage; |
| } |
| |
| } catch (JavaModelException e) { |
| JavaPlugin.log(e); |
| } |
| |
| return null; |
| } |
| |
| private Object getHierarchicalParent(IPackageFragment fragment) { |
| IJavaElement parent= fragment.getParent(); |
| |
| if ((parent instanceof IPackageFragmentRoot) && parent.exists()) { |
| IPackageFragmentRoot root= (IPackageFragmentRoot) parent; |
| if (root.isArchive() || root.isExternal() || !fragment.exists()) { |
| return findNextLevelParentByElementName(fragment); |
| } else { |
| IResource resource= fragment.getResource(); |
| if ((resource != null) && (resource instanceof IFolder)) { |
| IFolder folder= (IFolder) resource; |
| IResource res= folder.getParent(); |
| |
| IJavaElement el= JavaCore.create(res); |
| if (el != null) { |
| return el; |
| } else { |
| return res; |
| } |
| } |
| } |
| } |
| return parent; |
| } |
| |
| private Object findNextLevelParentByElementName(IPackageFragment child) { |
| String name= child.getElementName(); |
| |
| int index= name.lastIndexOf('.'); |
| if (index != -1) { |
| String realParentName= name.substring(0, index); |
| IPackageFragment element= ((IPackageFragmentRoot) child.getParent()).getPackageFragment(realParentName); |
| if (element.exists()) { |
| return element; |
| } |
| } |
| return child.getParent(); |
| } |
| |
| |
| /* |
| * @see org.eclipse.jface.viewers.ITreeContentProvider#hasChildren(Object) |
| */ |
| @Override |
| public boolean hasChildren(Object element) { |
| |
| if (element instanceof IPackageFragment) { |
| IPackageFragment fragment= (IPackageFragment) element; |
| if(fragment.isDefaultPackage() || !fragment.exists()) |
| return false; |
| } |
| return getChildren(element).length > 0; |
| } |
| |
| /* |
| * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(Object) |
| */ |
| @Override |
| public Object[] getElements(Object inputElement) { |
| return getChildren(inputElement); |
| } |
| |
| @Override |
| protected void processDelta(IJavaElementDelta delta) throws JavaModelException { |
| |
| int kind = delta.getKind(); |
| final IJavaElement element = delta.getElement(); |
| |
| if (isClassPathChange(delta)) { |
| Object input= fViewer.getInput(); |
| if (input != null) { |
| if (fInputIsProject && input.equals(element.getJavaProject())) { |
| postRefresh(input); |
| return; |
| } else if (!fInputIsProject && input.equals(element)) { |
| if (element.exists()) |
| postRefresh(input); |
| else |
| postRemove(input); |
| return; |
| } |
| } |
| } |
| |
| if (kind == IJavaElementDelta.REMOVED) { |
| Object input= fViewer.getInput(); |
| if (input != null && input.equals(element)) { |
| postRemove(input); |
| return; |
| } |
| } |
| |
| if (element instanceof IPackageFragment) { |
| final IPackageFragment frag = (IPackageFragment) element; |
| |
| //if fragment was in LogicalPackage refresh, |
| //otherwise just remove |
| switch (kind) { |
| case IJavaElementDelta.REMOVED: |
| removeElement(frag); |
| return; |
| case IJavaElementDelta.ADDED: |
| Object parent= getParent(frag); |
| addElement(frag, parent); |
| return; |
| case IJavaElementDelta.CHANGED: |
| //just refresh |
| LogicalPackage logicalPkg= findLogicalPackage(frag); |
| //in case changed object is filtered out |
| if (logicalPkg != null) |
| postRefresh(findElementToRefresh(logicalPkg)); |
| else |
| postRefresh(findElementToRefresh(frag)); |
| return; |
| default: |
| break; |
| } |
| } |
| |
| processAffectedChildren(delta); |
| } |
| |
| private Object findElementToRefresh(Object object) { |
| Object toBeRefreshed= object; |
| if (fViewer.testFindItem(object) == null) { |
| Object parent= getParent(object); |
| if(parent instanceof IPackageFragmentRoot && fInputIsProject) |
| parent= ((IPackageFragmentRoot)parent).getJavaProject(); |
| |
| if(parent != null) |
| toBeRefreshed= parent; |
| } |
| return toBeRefreshed; |
| } |
| |
| private void processAffectedChildren(IJavaElementDelta delta) throws JavaModelException { |
| for (IJavaElementDelta child : delta.getAffectedChildren()) { |
| if (!(child instanceof ICompilationUnit)) { |
| processDelta(child); |
| } |
| } |
| } |
| |
| private void postAdd(final Object child, final Object parent) { |
| postRunnable(() -> { |
| Control ctrl = fViewer.getControl(); |
| if (ctrl != null && !ctrl.isDisposed()) { |
| ((TreeViewer)fViewer).add(parent, child); |
| } |
| }); |
| } |
| |
| private void postRemove(final Object object) { |
| postRunnable(() -> { |
| Control ctrl = fViewer.getControl(); |
| if (ctrl != null && !ctrl.isDisposed()) { |
| ((TreeViewer)fViewer).remove(object); |
| } |
| }); |
| } |
| |
| private void postRefresh(final Object object) { |
| postRunnable(() -> { |
| Control ctrl= fViewer.getControl(); |
| if (ctrl != null && !ctrl.isDisposed()) { |
| ((TreeViewer) fViewer).refresh(object); |
| } |
| }); |
| } |
| |
| private void postRunnable(final Runnable r) { |
| Control ctrl= fViewer.getControl(); |
| if (ctrl != null && !ctrl.isDisposed()) { |
| // fBrowsingPart.setProcessSelectionEvents(false); |
| try { |
| Display currentDisplay= Display.getCurrent(); |
| if (currentDisplay != null && currentDisplay.equals(ctrl.getDisplay())) |
| ctrl.getDisplay().syncExec(r); |
| else |
| ctrl.getDisplay().asyncExec(r); |
| } finally { |
| // fBrowsingPart.setProcessSelectionEvents(true); |
| } |
| } |
| } |
| |
| private void addElement(IPackageFragment frag, Object parent) { |
| |
| String key= getKey(frag); |
| LogicalPackage lp= fMapToLogicalPackage.get(key); |
| |
| //if fragment must be added to an existing LogicalPackage |
| if (lp != null && lp.belongs(frag)){ |
| lp.add(frag); |
| return; |
| } |
| |
| //if a new LogicalPackage must be created |
| IPackageFragment iPackageFragment= fMapToPackageFragments.get(key); |
| if (iPackageFragment!= null && !iPackageFragment.equals(frag)){ |
| lp= new LogicalPackage(iPackageFragment); |
| lp.add(frag); |
| //add new LogicalPackage to LogicalPackages map |
| fMapToLogicalPackage.put(key, lp); |
| |
| //determine who to refresh |
| if (parent instanceof IPackageFragmentRoot){ |
| IPackageFragmentRoot root= (IPackageFragmentRoot) parent; |
| if (fInputIsProject){ |
| postRefresh(root.getJavaProject()); |
| } else { |
| postRefresh(root); |
| } |
| } else { |
| //@Improve: Should this be replaced by a refresh? |
| postAdd(lp, parent); |
| postRemove(iPackageFragment); |
| } |
| |
| } |
| //if this is a new Package Fragment |
| else { |
| fMapToPackageFragments.put(key, frag); |
| |
| //determine who to refresh |
| if (parent instanceof IPackageFragmentRoot) { |
| IPackageFragmentRoot root= (IPackageFragmentRoot) parent; |
| if (fInputIsProject) { |
| postAdd(frag, root.getJavaProject()); |
| } else |
| postAdd(frag, root); |
| } else { |
| postAdd(frag, parent); |
| } |
| } |
| } |
| |
| private void removeElement(IPackageFragment frag) { |
| |
| String key= getKey(frag); |
| LogicalPackage lp= fMapToLogicalPackage.get(key); |
| |
| if(lp != null){ |
| lp.remove(frag); |
| //if the LogicalPackage needs to revert back to a PackageFragment |
| //remove it from the LogicalPackages map and add the PackageFragment |
| //to the PackageFragment map |
| if (lp.getFragments().length == 1) { |
| IPackageFragment fragment= lp.getFragments()[0]; |
| fMapToPackageFragments.put(key, fragment); |
| fMapToLogicalPackage.remove(key); |
| |
| //remove the LogicalPackage from viewer |
| postRemove(lp); |
| |
| Object parent= getParent(fragment); |
| if (parent instanceof IPackageFragmentRoot) { |
| parent= ((IPackageFragmentRoot)parent).getJavaProject(); |
| } |
| postAdd(fragment, parent); |
| } |
| |
| } else { |
| //remove the fragment from the fragment map and viewer |
| IPackageFragment fragment= fMapToPackageFragments.get(key); |
| if (fragment!= null && fragment.equals(frag)) { |
| fMapToPackageFragments.remove(key); |
| postRemove(frag); |
| } |
| } |
| } |
| } |