blob: 0f1551c030952a1fe0cef0af41df974651f65607 [file] [log] [blame]
/*******************************************************************************
* 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);
}
}