| /******************************************************************************* |
| * Copyright (c) 2000, 2018 IBM Corporation and others. |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0. |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| *******************************************************************************/ |
| package org.eclipse.dltk.internal.ui.navigator; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import org.eclipse.core.resources.IFolder; |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.dltk.core.DLTKCore; |
| import org.eclipse.dltk.core.ElementChangedEvent; |
| import org.eclipse.dltk.core.IModelElement; |
| import org.eclipse.dltk.core.IModelElementDelta; |
| import org.eclipse.dltk.core.IProjectFragment; |
| import org.eclipse.dltk.core.IScriptFolder; |
| import org.eclipse.dltk.core.IScriptProject; |
| import org.eclipse.dltk.core.ModelException; |
| import org.eclipse.dltk.ui.DLTKUIPlugin; |
| import org.eclipse.dltk.ui.PreferenceConstants; |
| import org.eclipse.jface.preference.IPreferenceStore; |
| import org.eclipse.jface.util.IPropertyChangeListener; |
| import org.eclipse.jface.util.PropertyChangeEvent; |
| import org.eclipse.jface.viewers.TreeViewer; |
| import org.eclipse.jface.viewers.Viewer; |
| import org.eclipse.swt.widgets.Control; |
| import org.eclipse.swt.widgets.Display; |
| |
| /** |
| * Content provider which provides package fragments for hierarchical Package |
| * Explorer layout. |
| * |
| */ |
| public class ProjectFragmentProvider implements IPropertyChangeListener { |
| |
| private TreeViewer fViewer; |
| private boolean fFoldPackages; |
| private IPreferenceStore fStore; |
| |
| public ProjectFragmentProvider(IPreferenceStore store) { |
| fStore = store; |
| fFoldPackages = arePackagesFoldedInHierarchicalLayout(); |
| fStore.addPropertyChangeListener(this); |
| } |
| |
| public Object[] getChildren(Object parentElement) { |
| try { |
| if (parentElement instanceof IFolder) { |
| IResource[] resources = ((IFolder) parentElement).members(); |
| return filter(getFolders(resources)).toArray(); |
| } else if (parentElement instanceof IModelElement) { |
| IModelElement iModelElement = (IModelElement) parentElement; |
| int type = iModelElement.getElementType(); |
| |
| switch (type) { |
| case IModelElement.SCRIPT_PROJECT: { |
| IScriptProject project = (IScriptProject) iModelElement; |
| |
| IProjectFragment root = project |
| .findProjectFragment(project.getPath()); |
| if (root != null) { |
| List<IModelElement> children = getTopLevelChildren( |
| root); |
| return filter(children).toArray(); |
| } |
| break; |
| } |
| case IModelElement.PROJECT_FRAGMENT: { |
| IProjectFragment root = (IProjectFragment) parentElement; |
| if (root.exists()) { |
| return filter(getTopLevelChildren(root)).toArray(); |
| } |
| break; |
| } |
| case IModelElement.SCRIPT_FOLDER: { |
| IScriptFolder scriptFolder = (IScriptFolder) parentElement; |
| if (!scriptFolder.isRootFolder()) { |
| IProjectFragment root = (IProjectFragment) scriptFolder |
| .getParent(); |
| List<IScriptFolder> children = getPackageChildren(root, |
| scriptFolder); |
| return filter(children).toArray(); |
| } |
| break; |
| } |
| default: |
| // do nothing |
| } |
| } |
| } catch (CoreException e) { |
| DLTKUIPlugin.log(e); |
| } |
| return new Object[0]; |
| } |
| |
| private List filter(List children) throws ModelException { |
| if (fFoldPackages) { |
| int size = children.size(); |
| for (int i = 0; i < size; i++) { |
| Object curr = children.get(i); |
| if (curr instanceof IScriptFolder) { |
| IScriptFolder fragment = (IScriptFolder) curr; |
| if (!fragment.isRootFolder() && isEmpty(fragment)) { |
| IScriptFolder collapsed = getCollapsed(fragment); |
| if (collapsed != null) { |
| children.set(i, collapsed); // replace with |
| // collapsed |
| } |
| } |
| } |
| } |
| } |
| return children; |
| } |
| |
| private IScriptFolder getCollapsed(IScriptFolder pack) |
| throws ModelException { |
| IModelElement[] children = ((IProjectFragment) pack.getParent()) |
| .getChildren(); |
| IScriptFolder child = getSinglePackageChild(pack, children); |
| while (child != null && isEmpty(child)) { |
| IScriptFolder collapsed = getSinglePackageChild(child, children); |
| if (collapsed == null) { |
| return child; |
| } |
| child = collapsed; |
| } |
| return child; |
| } |
| |
| private boolean isEmpty(IScriptFolder fragment) throws ModelException { |
| return !fragment.containsScriptResources() |
| && fragment.getForeignResources().length == 0; |
| } |
| |
| private static IScriptFolder getSinglePackageChild(IScriptFolder fragment, |
| IModelElement[] children) { |
| String prefix = fragment.getElementName() |
| + IScriptFolder.PACKAGE_DELIMITER; |
| int prefixLen = prefix.length(); |
| IScriptFolder found = null; |
| for (int i = 0; i < children.length; i++) { |
| IModelElement element = children[i]; |
| String name = element.getElementName(); |
| if (name.startsWith(prefix) && name.length() > prefixLen |
| && name.indexOf(IScriptFolder.PACKAGE_DELIMITER, |
| prefixLen) == -1) { |
| if (found == null) { |
| found = (IScriptFolder) element; |
| } else { |
| return null; |
| } |
| } |
| } |
| return found; |
| } |
| |
| private static List<IScriptFolder> getPackageChildren( |
| IProjectFragment parent, IScriptFolder fragment) |
| throws ModelException { |
| IModelElement[] children = parent.getChildren(); |
| ArrayList<IScriptFolder> list = new ArrayList<>(children.length); |
| String prefix = fragment.getElementName() |
| + IScriptFolder.PACKAGE_DELIMITER; |
| int prefixLen = prefix.length(); |
| for (int i = 0; i < children.length; i++) { |
| IModelElement element = children[i]; |
| if (element instanceof IScriptFolder) { // see bug 134256 |
| String name = element.getElementName(); |
| if (name.startsWith(prefix) && name.length() > prefixLen |
| && name.indexOf(IScriptFolder.PACKAGE_DELIMETER_STR, |
| prefixLen) == -1) { |
| list.add((IScriptFolder) element); |
| } |
| } |
| } |
| return list; |
| } |
| |
| private static List<IModelElement> getTopLevelChildren( |
| IProjectFragment root) throws ModelException { |
| IModelElement[] elements = root.getChildren(); |
| ArrayList<IModelElement> topLevelElements = new ArrayList<>( |
| elements.length); |
| for (int i = 0; i < elements.length; i++) { |
| IModelElement iModelElement = elements[i]; |
| // if the name of the ScriptFolder is the top level package it will |
| // contain no "." separators |
| if (iModelElement instanceof IScriptFolder |
| && iModelElement.getElementName() |
| .indexOf(IScriptFolder.PACKAGE_DELIMITER) == -1) { |
| IScriptFolder folder = (IScriptFolder) iModelElement; |
| if (!folder.isRootFolder()) { |
| topLevelElements.add(iModelElement); |
| } else { |
| IModelElement[] children = folder.getChildren(); |
| for (int j = 0; j < children.length; j++) { |
| topLevelElements.add(children[j]); |
| } |
| } |
| } |
| } |
| return topLevelElements; |
| } |
| |
| private List<IFolder> getFolders(IResource[] resources) |
| throws ModelException { |
| List<IFolder> list = new ArrayList<>(resources.length); |
| for (int i = 0; i < resources.length; i++) { |
| IResource resource = resources[i]; |
| if (resource instanceof IFolder) { |
| IFolder folder = (IFolder) resource; |
| // IModelElement element= DLTKCore.create(folder); |
| // if (element instanceof IScriptFolder) { |
| IProject project = folder.getProject(); |
| IScriptProject scriptProject = DLTKCore.create(project); |
| if (scriptProject != null) { |
| if (scriptProject.isOnBuildpath(folder)) |
| list.add(folder); |
| } |
| // } |
| } |
| } |
| return list; |
| } |
| |
| public Object getParent(Object element) { |
| |
| if (element instanceof IScriptFolder) { |
| IScriptFolder frag = (IScriptFolder) element; |
| // @Changed: a fix, before: if(frag.exists() && isEmpty(frag)) |
| |
| return filterParent(getActualParent(frag)); |
| } |
| return null; |
| } |
| |
| private Object getActualParent(IScriptFolder fragment) { |
| try { |
| |
| if (fragment.exists()) { |
| IModelElement parent = fragment.getParent(); |
| |
| if ((parent instanceof IProjectFragment) && parent.exists()) { |
| IProjectFragment root = (IProjectFragment) parent; |
| if (root.isArchive()) { |
| return findNextLevelParentByElementName(fragment); |
| } |
| |
| IResource resource = fragment.getUnderlyingResource(); |
| if ((resource != null) && (resource instanceof IFolder)) { |
| IFolder folder = (IFolder) resource; |
| IResource res = folder.getParent(); |
| |
| IModelElement el = DLTKCore.create(res); |
| if (el != null) { |
| return el; |
| } |
| return res; |
| } |
| return parent; |
| } |
| } |
| |
| } catch (ModelException e) { |
| DLTKUIPlugin.log(e); |
| } |
| return null; |
| } |
| |
| private Object filterParent(Object parent) { |
| if (fFoldPackages && (parent != null)) { |
| try { |
| if (parent instanceof IScriptFolder) { |
| IScriptFolder fragment = (IScriptFolder) parent; |
| if (isEmpty(fragment) && hasSingleChild(fragment)) { |
| return filterParent(getActualParent(fragment)); |
| } |
| } |
| } catch (ModelException e) { |
| DLTKUIPlugin.log(e); |
| } |
| } |
| return parent; |
| } |
| |
| private boolean hasSingleChild(IScriptFolder fragment) { |
| return getChildren(fragment).length == 1; |
| } |
| |
| private Object findNextLevelParentByElementName(IScriptFolder child) { |
| String name = child.getElementName(); |
| |
| int index = name.lastIndexOf(IScriptFolder.PACKAGE_DELIMITER); |
| if (index != -1) { |
| String realParentName = name.substring(0, index); |
| IScriptFolder element = ((IProjectFragment) child.getParent()) |
| .getScriptFolder(realParentName); |
| if (element.exists()) { |
| return element; |
| } |
| } |
| return child.getParent(); |
| } |
| |
| /* |
| * @see org.eclipse.jface.viewers.ITreeContentProvider#hasChildren(Object) |
| */ |
| public boolean hasChildren(Object element) { |
| |
| if (element instanceof IScriptFolder) { |
| IScriptFolder fragment = (IScriptFolder) element; |
| if (fragment.isRootFolder()) |
| return false; |
| } |
| return getChildren(element).length > 0; |
| } |
| |
| /* |
| * @see |
| * org.eclipse.jface.viewers.IStructuredContentProvider#getElements(Object) |
| */ |
| public Object[] getElements(Object inputElement) { |
| return getChildren(inputElement); |
| } |
| |
| /* |
| * @see org.eclipse.jface.viewers.IContentProvider#dispose() |
| */ |
| public void dispose() { |
| fStore.removePropertyChangeListener(this); |
| } |
| |
| /** |
| * Called when the view is closed and opened. |
| * |
| * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(Viewer, |
| * Object, Object) |
| */ |
| public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { |
| fViewer = (TreeViewer) viewer; |
| } |
| |
| public void elementChanged(ElementChangedEvent event) { |
| processDelta(event.getDelta()); |
| } |
| |
| public void processDelta(IModelElementDelta delta) { |
| |
| int kind = delta.getKind(); |
| final IModelElement element = delta.getElement(); |
| |
| if (element instanceof IScriptFolder) { |
| |
| if (kind == IModelElementDelta.REMOVED) { |
| |
| postRunnable(() -> { |
| Control ctrl = fViewer.getControl(); |
| if (ctrl != null && !ctrl.isDisposed()) { |
| if (!fFoldPackages) |
| fViewer.remove(element); |
| else |
| refreshGrandParent(element); |
| } |
| }); |
| return; |
| |
| } else if (kind == IModelElementDelta.ADDED) { |
| |
| final Object parent = getParent(element); |
| if (parent != null) { |
| postRunnable(() -> { |
| Control ctrl = fViewer.getControl(); |
| if (ctrl != null && !ctrl.isDisposed()) { |
| if (!fFoldPackages) |
| fViewer.add(parent, element); |
| else |
| refreshGrandParent(element); |
| } |
| }); |
| } |
| return; |
| } |
| } |
| } |
| |
| // XXX: needs to be revisited - might be a performance issue |
| private void refreshGrandParent(final IModelElement element) { |
| if (element instanceof IScriptFolder) { |
| Object gp = getGrandParent((IScriptFolder) element); |
| if (gp instanceof IModelElement) { |
| IModelElement el = (IModelElement) gp; |
| if (el.exists()) |
| fViewer.refresh(gp); |
| } else if (gp instanceof IFolder) { |
| IFolder folder = (IFolder) gp; |
| if (folder.exists()) |
| fViewer.refresh(folder); |
| } |
| } |
| } |
| |
| private Object getGrandParent(IScriptFolder element) { |
| |
| Object parent = findNextLevelParentByElementName(element); |
| if (parent instanceof IProjectFragment) { |
| IProjectFragment root = (IProjectFragment) parent; |
| if (isRootProject(root)) { |
| return root.getScriptProject(); |
| } |
| return root; |
| } |
| |
| Object grandParent = getParent(parent); |
| if (grandParent == null) { |
| return parent; |
| } |
| return grandParent; |
| } |
| |
| private boolean isRootProject(IProjectFragment root) { |
| if (IProjectFragment.DEFAULT_PACKAGE_ROOT.equals(root.getElementName())) |
| return true; |
| return false; |
| } |
| |
| private void postRunnable(final Runnable r) { |
| Control ctrl = fViewer.getControl(); |
| if (ctrl != null && !ctrl.isDisposed()) { |
| |
| Display currentDisplay = Display.getCurrent(); |
| if (currentDisplay != null |
| && currentDisplay.equals(ctrl.getDisplay())) |
| ctrl.getDisplay().syncExec(r); |
| else |
| ctrl.getDisplay().asyncExec(r); |
| } |
| } |
| |
| /* |
| * @see |
| * org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse |
| * .jface.util.PropertyChangeEvent) |
| */ |
| @Override |
| public void propertyChange(PropertyChangeEvent event) { |
| if (arePackagesFoldedInHierarchicalLayout() != fFoldPackages) { |
| fFoldPackages = arePackagesFoldedInHierarchicalLayout(); |
| if (fViewer != null && !fViewer.getControl().isDisposed()) { |
| fViewer.getControl().setRedraw(false); |
| Object[] expandedObjects = fViewer.getExpandedElements(); |
| fViewer.refresh(); |
| fViewer.setExpandedElements(expandedObjects); |
| fViewer.getControl().setRedraw(true); |
| } |
| } |
| } |
| |
| private boolean arePackagesFoldedInHierarchicalLayout() { |
| return fStore.getBoolean( |
| PreferenceConstants.APPEARANCE_FOLD_PACKAGES_IN_PACKAGE_EXPLORER); |
| } |
| } |