blob: d75cb430b2a002d1a7d512d940fc80e7784c9324 [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
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.dltk.ui.browsing;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.dltk.core.DLTKCore;
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.ISourceModule;
import org.eclipse.dltk.core.ModelException;
import org.eclipse.dltk.ui.DLTKUIPlugin;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.StructuredViewer;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
/**
* 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);
}
@Override
public Object[] getChildren(Object parentElement) {
try {
if (parentElement instanceof IModelElement) {
IModelElement iJavaElement = (IModelElement) parentElement;
int type = iJavaElement.getElementType();
switch (type) {
case IModelElement.SCRIPT_PROJECT: {
// create new element mapping
fMapToLogicalPackage.clear();
fMapToPackageFragments.clear();
IScriptProject project = (IScriptProject) parentElement;
IScriptFolder[] topLevelChildren = getTopLevelChildrenByElementName(
project.getScriptFolders());
List<IScriptFolder> list = new ArrayList<>();
for (int i = 0; i < topLevelChildren.length; i++) {
IScriptFolder fragment = topLevelChildren[i];
IModelElement el = fragment.getParent();
if (el instanceof IProjectFragment) {
IProjectFragment root = (IProjectFragment) el;
if (!root.isArchive() || !root.isExternal())
list.add(fragment);
}
}
IProjectFragment[] packageFragmentRoots = project
.getProjectFragments();
List folders = new ArrayList();
for (int i = 0; i < packageFragmentRoots.length; i++) {
IProjectFragment root = packageFragmentRoots[i];
IResource resource = root.getUnderlyingResource();
if (resource != null && resource instanceof IFolder) {
folders.addAll(
getFolders(((IFolder) resource).members()));
}
}
Object[] logicalPackages = combineSamePackagesIntoLogialPackages(
list.toArray(new IScriptFolder[list.size()]));
if (folders.size() > 0) {
if (logicalPackages.length > 0)
folders.addAll(Arrays.asList(logicalPackages));
return folders.toArray();
}
return logicalPackages;
}
case IModelElement.PROJECT_FRAGMENT: {
IProjectFragment root = (IProjectFragment) parentElement;
// create new element mapping
fMapToLogicalPackage.clear();
fMapToPackageFragments.clear();
IResource resource = root.getUnderlyingResource();
if (root.isArchive()) {
IScriptFolder[] fragments = new IScriptFolder[0];
IModelElement[] els = root.getChildren();
fragments = getTopLevelChildrenByElementName(els);
addFragmentsToMap(fragments);
return fragments;
} else if (resource != null
&& resource instanceof IFolder) {
List children = getFoldersAndElements(
((IFolder) resource).members());
IScriptFolder defaultPackage = root.getScriptFolder(""); //$NON-NLS-1$
if (defaultPackage.exists())
children.add(defaultPackage);
addFragmentsToMap(children);
return children.toArray();
} else {
return NO_CHILDREN;
}
}
case IModelElement.SCRIPT_FOLDER: {
IScriptFolder packageFragment = (IScriptFolder) parentElement;
if (packageFragment.isRootFolder())
return NO_CHILDREN;
IProjectFragment parent = (IProjectFragment) packageFragment
.getParent();
IScriptFolder[] fragments = findNextLevelChildrenByElementName(
parent, packageFragment);
addFragmentsToMap(fragments);
Object[] nonJavaResources = packageFragment
.getForeignResources();
if (nonJavaResources.length == 0) {
return fragments;
}
ArrayList combined = new ArrayList();
combined.addAll(Arrays.asList(fragments));
for (int i = 0; i < nonJavaResources.length; i++) {
Object curr = nonJavaResources[i];
if (curr instanceof IFolder) {
combined.add(curr);
}
}
return combined.toArray();
}
}
// @Improve: rewrite using concatenate
} else if (parentElement instanceof LogicalPackage) {
List<IScriptFolder> children = new ArrayList<>();
LogicalPackage logicalPackage = (LogicalPackage) parentElement;
IScriptFolder[] elements = logicalPackage.getScriptFolders();
for (int i = 0; i < elements.length; i++) {
IScriptFolder fragment = elements[i];
IScriptFolder[] objects = findNextLevelChildrenByElementName(
(IProjectFragment) fragment.getParent(), fragment);
children.addAll(Arrays.asList(objects));
}
return combineSamePackagesIntoLogialPackages(
children.toArray(new IScriptFolder[children.size()]));
} else if (parentElement instanceof IFolder) {
IFolder folder = (IFolder) parentElement;
IResource[] resources = folder.members();
List children = getFoldersAndElements(resources);
addFragmentsToMap(children);
return children.toArray();
}
} catch (Exception e) {
return NO_CHILDREN;
}
return NO_CHILDREN;
}
private void addFragmentsToMap(List elements) {
List<IScriptFolder> packageFragments = new ArrayList<>();
for (Iterator iter = elements.iterator(); iter.hasNext();) {
Object elem = iter.next();
if (elem instanceof IScriptFolder)
packageFragments.add((IScriptFolder) elem);
}
addFragmentsToMap(packageFragments
.toArray(new IScriptFolder[packageFragments.size()]));
}
private List getFoldersAndElements(IResource[] resources)
throws CoreException {
List list = new ArrayList();
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) {
list.add(element);
} else {
list.add(folder);
}
}
}
return list;
}
private List getFolders(IResource[] resources) throws CoreException {
List list = new ArrayList();
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 == null) {
list.add(folder);
}
}
}
return list;
}
private IScriptFolder[] findNextLevelChildrenByElementName(
IProjectFragment parent, IScriptFolder fragment) {
List<IScriptFolder> list = new ArrayList<>();
try {
IModelElement[] children = parent.getChildren();
String fragmentname = fragment.getElementName();
for (int i = 0; i < children.length; i++) {
IModelElement element = children[i];
if (element instanceof IScriptFolder) {
IScriptFolder frag = (IScriptFolder) element;
String name = element.getElementName();
if (name.length() > fragmentname.length()
&& name.charAt(fragmentname.length()) == '.'
&& frag.exists()
&& !IScriptFolder.DEFAULT_FOLDER_NAME
.equals(fragmentname)
&& name.startsWith(fragmentname)
&& !name.equals(fragmentname)) {
String tail = name.substring(fragmentname.length() + 1);
if (!IScriptFolder.DEFAULT_FOLDER_NAME.equals(tail)
&& tail.indexOf('.') == -1) {
list.add(frag);
}
}
}
}
} catch (ModelException e) {
DLTKUIPlugin.log(e);
}
return list.toArray(new IScriptFolder[list.size()]);
}
private IScriptFolder[] getTopLevelChildrenByElementName(
IModelElement[] elements) {
List<IScriptFolder> topLevelElements = new ArrayList<>();
for (int i = 0; i < elements.length; i++) {
IModelElement iJavaElement = elements[i];
// if the name of the PackageFragment is the top level package it
// will contain no "." separators
if (iJavaElement instanceof IScriptFolder
&& iJavaElement.getElementName().indexOf('.') == -1) {
topLevelElements.add((IScriptFolder) iJavaElement);
}
}
return topLevelElements
.toArray(new IScriptFolder[topLevelElements.size()]);
}
/*
* @see org.eclipse.jface.viewers.ITreeContentProvider#getParent(Object)
*/
@Override
public Object getParent(Object element) {
try {
if (element instanceof IScriptFolder) {
IScriptFolder fragment = (IScriptFolder) element;
if (!fragment.exists())
return null;
Object parent = getHierarchicalParent(fragment);
if (parent instanceof IScriptFolder) {
IScriptFolder pkgFragment = (IScriptFolder) parent;
LogicalPackage logicalPkg = findLogicalPackage(pkgFragment);
if (logicalPkg != null) {
return logicalPkg;
}
LogicalPackage lp = createLogicalPackage(pkgFragment);
if (lp == null) {
return pkgFragment;
}
return lp;
}
return parent;
} else if (element instanceof LogicalPackage) {
LogicalPackage el = (LogicalPackage) element;
IScriptFolder fragment = el.getScriptFolders()[0];
Object parent = getHierarchicalParent(fragment);
if (parent instanceof IScriptFolder) {
IScriptFolder pkgFragment = (IScriptFolder) parent;
LogicalPackage logicalPkg = findLogicalPackage(pkgFragment);
if (logicalPkg != null) {
return logicalPkg;
}
LogicalPackage lp = createLogicalPackage(pkgFragment);
if (lp == null) {
return pkgFragment;
}
return lp;
}
return fragment.getScriptProject();
} else if (element instanceof IFolder) {
IFolder folder = (IFolder) element;
IResource res = folder.getParent();
IModelElement el = DLTKCore.create(res);
if (el != null) {
return el;
}
return res;
}
} catch (Exception e) {
DLTKUIPlugin.log(e);
}
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(IScriptFolder pkgFragment) {
if (!fInputIsProject)
return null;
List fragments = new ArrayList();
try {
IProjectFragment[] roots = pkgFragment.getScriptProject()
.getProjectFragments();
for (int i = 0; i < roots.length; i++) {
IProjectFragment root = roots[i];
IScriptFolder fragment = root
.getScriptFolder(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 iter = fragments.iterator();
while (iter.hasNext()) {
IScriptFolder f = (IScriptFolder) iter.next();
if (logicalPackage.belongs(f)) {
logicalPackage.add(f);
fMapToLogicalPackage.put(getKey(f), logicalPackage);
}
}
return logicalPackage;
}
} catch (Exception e) {
DLTKUIPlugin.log(e);
}
return null;
}
private Object getHierarchicalParent(IScriptFolder fragment)
throws Exception {
IModelElement parent = fragment.getParent();
if ((parent instanceof IProjectFragment) && parent.exists()) {
IProjectFragment root = (IProjectFragment) parent;
if (root.isArchive() || !fragment.exists()) {
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;
}
private Object findNextLevelParentByElementName(IScriptFolder child) {
String name = child.getElementName();
int index = name.lastIndexOf('.');
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)
*/
@Override
public boolean hasChildren(Object element) {
if (element instanceof IScriptFolder) {
IScriptFolder fragment = (IScriptFolder) element;
if (fragment.isRootFolder() || !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(IModelElementDelta delta) {
int kind = delta.getKind();
final IModelElement element = delta.getElement();
if (isClassPathChange(delta)) {
Object input = fViewer.getInput();
if (input != null) {
if (fInputIsProject
&& input.equals(element.getScriptProject())) {
postRefresh(input);
return;
} else if (!fInputIsProject && input.equals(element)) {
if (element.exists())
postRefresh(input);
else
postRemove(input);
return;
}
}
}
if (kind == IModelElementDelta.REMOVED) {
Object input = fViewer.getInput();
if (input != null && input.equals(element)) {
postRemove(input);
return;
}
}
if (element instanceof IScriptFolder) {
final IScriptFolder frag = (IScriptFolder) element;
// if fragment was in LogicalPackage refresh,
// otherwise just remove
if (kind == IModelElementDelta.REMOVED) {
removeElement(frag);
return;
} else if (kind == IModelElementDelta.ADDED) {
Object parent = getParent(frag);
addElement(frag, parent);
return;
} else if (kind == IModelElementDelta.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;
}
}
try {
processAffectedChildren(delta);
} catch (Exception e) {
if (DLTKCore.DEBUG) {
e.printStackTrace();
}
}
}
private Object findElementToRefresh(Object object) {
Object toBeRefreshed = object;
if (fViewer.testFindItem(object) == null) {
Object parent = getParent(object);
if (parent instanceof IProjectFragment && fInputIsProject)
parent = ((IProjectFragment) parent).getScriptProject();
if (parent != null)
toBeRefreshed = parent;
}
return toBeRefreshed;
}
private void processAffectedChildren(IModelElementDelta delta)
throws Exception {
IModelElementDelta[] affectedChildren = delta.getAffectedChildren();
for (int i = 0; i < affectedChildren.length; i++) {
if (!(affectedChildren[i] instanceof ISourceModule)) {
processDelta(affectedChildren[i]);
}
}
}
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(IScriptFolder frag, Object parent) {
String key = getKey(frag);
LogicalPackage lp = (LogicalPackage) 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
IScriptFolder iPackageFragment = (IScriptFolder) 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 IProjectFragment) {
IProjectFragment root = (IProjectFragment) parent;
if (fInputIsProject) {
postRefresh(root.getScriptProject());
} 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 IProjectFragment) {
IProjectFragment root = (IProjectFragment) parent;
if (fInputIsProject) {
postAdd(frag, root.getScriptProject());
} else
postAdd(frag, root);
} else {
postAdd(frag, parent);
}
}
}
private void removeElement(IScriptFolder frag) {
String key = getKey(frag);
LogicalPackage lp = (LogicalPackage) 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.getScriptFolders().length == 1) {
IScriptFolder fragment = lp.getScriptFolders()[0];
fMapToPackageFragments.put(key, fragment);
fMapToLogicalPackage.remove(key);
// remove the LogicalPackage from viewer
postRemove(lp);
Object parent = getParent(fragment);
if (parent instanceof IProjectFragment) {
parent = ((IProjectFragment) parent).getScriptProject();
}
postAdd(fragment, parent);
}
} else {
// remove the fragment from the fragment map and viewer
IScriptFolder fragment = (IScriptFolder) fMapToPackageFragments
.get(key);
if (fragment != null && fragment.equals(frag)) {
fMapToPackageFragments.remove(key);
postRemove(frag);
}
}
}
}