blob: b4dc8c2df37ce2fc82886bc4fa025f2564179994 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2016 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
* Lars Vogel <Lars.Vogel@vogella.com> - Bug 487943
*******************************************************************************/
package org.eclipse.pde.internal.ui.editor.build;
import java.util.*;
import org.eclipse.core.resources.*;
import org.eclipse.core.runtime.*;
import org.eclipse.jface.action.*;
import org.eclipse.jface.viewers.*;
import org.eclipse.pde.core.IModelChangedEvent;
import org.eclipse.pde.core.build.*;
import org.eclipse.pde.internal.build.IBuildPropertiesConstants;
import org.eclipse.pde.internal.core.project.PDEProject;
import org.eclipse.pde.internal.ui.PDEPlugin;
import org.eclipse.pde.internal.ui.editor.*;
import org.eclipse.pde.internal.ui.editor.context.InputContext;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.*;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.Section;
import org.eclipse.ui.model.WorkbenchLabelProvider;
public abstract class BuildContentsSection extends TableSection implements IResourceChangeListener, IResourceDeltaVisitor {
protected CheckboxTreeViewer fTreeViewer;
private boolean fDoRefresh = false;
protected IContainer fBundleRoot;
protected IBuildModel fBuildModel;
protected IResource fOriginalResource, fParentResource;
protected boolean isChecked;
public class TreeContentProvider implements ITreeContentProvider {
@Override
public Object[] getElements(Object parent) {
if (parent instanceof IContainer) {
try {
return ((IContainer) parent).members();
} catch (CoreException e) {
PDEPlugin.logException(e);
}
}
return new Object[0];
}
/**
* @see org.eclipse.jface.viewers.ITreeContentProvider#getChildren(java.lang.Object)
*/
@Override
public Object[] getChildren(Object parent) {
try {
if (parent instanceof IFolder)
return ((IFolder) parent).members();
} catch (CoreException e) {
PDEPlugin.logException(e);
}
return new Object[0];
}
public Object[] getFolderChildren(Object parent) {
IResource[] members = null;
try {
if (!(parent instanceof IFolder))
return new Object[0];
members = ((IFolder) parent).members();
ArrayList<IResource> results = new ArrayList<>();
for (IResource member : members) {
if ((member.getType() == IResource.FOLDER)) {
results.add(member);
}
}
return results.toArray();
} catch (CoreException e) {
PDEPlugin.logException(e);
}
return new Object[0];
}
/**
* @see org.eclipse.jface.viewers.ITreeContentProvider#getParent(java.lang.Object)
*/
@Override
public Object getParent(Object element) {
if (element != null && element instanceof IResource) {
return ((IResource) element).getParent();
}
return null;
}
/**
* @see org.eclipse.jface.viewers.ITreeContentProvider#hasChildren(java.lang.Object)
*/
@Override
public boolean hasChildren(Object element) {
if (element instanceof IFolder)
return getChildren(element).length > 0;
return false;
}
}
@Override
protected void createViewerPartControl(Composite parent, int style, int span, FormToolkit toolkit) {
MenuManager popupMenuManager = new MenuManager();
IMenuListener listener = mng -> fillContextMenu(mng);
popupMenuManager.addMenuListener(listener);
popupMenuManager.setRemoveAllWhenShown(true);
Control control = fTreeViewer.getControl();
Menu menu = popupMenuManager.createContextMenu(control);
control.setMenu(menu);
}
@Override
protected void fillContextMenu(IMenuManager manager) {
manager.add(getPage().getPDEEditor().getContributor().getRevertAction());
getPage().getPDEEditor().getContributor().contextMenuAboutToShow(manager, false);
}
private IBuildModel getBuildModel() {
InputContext context = getPage().getPDEEditor().getContextManager().findContext(BuildInputContext.CONTEXT_ID);
return (IBuildModel) context.getModel();
}
public BuildContentsSection(PDEFormPage page, Composite parent) {
super(page, parent, Section.DESCRIPTION, new String[0]);
PDEPlugin.getWorkspace().addResourceChangeListener(this);
}
@Override
public void createClient(final Section section, FormToolkit toolkit) {
Composite container = createClientContainer(section, 2, toolkit);
fBuildModel = getBuildModel();
if (fBuildModel.getUnderlyingResource() != null)
fBundleRoot = PDEProject.getBundleRoot(fBuildModel.getUnderlyingResource().getProject());
fTreeViewer = new CheckboxTreeViewer(toolkit.createTree(container, SWT.CHECK));
fTreeViewer.setContentProvider(new TreeContentProvider());
fTreeViewer.setLabelProvider(new WorkbenchLabelProvider());
fTreeViewer.setAutoExpandLevel(0);
fTreeViewer.addCheckStateListener(event -> {
final Object element = event.getElement();
BusyIndicator.showWhile(section.getDisplay(), () -> {
if (element instanceof IFile) {
IFile file = (IFile) event.getElement();
handleCheckStateChanged(file, event.getChecked());
} else if (element instanceof IFolder) {
IFolder folder = (IFolder) event.getElement();
handleCheckStateChanged(folder, event.getChecked());
}
});
});
GridData gd = new GridData(GridData.FILL_BOTH);
gd.heightHint = 100;
gd.widthHint = 100;
fTreeViewer.getTree().setLayoutData(gd);
initialize();
toolkit.paintBordersFor(container);
createViewerPartControl(container, SWT.FULL_SELECTION, 2, toolkit);
section.setLayout(FormLayoutFactory.createClearGridLayout(false, 1));
section.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
section.setClient(container);
}
public void enableSection(boolean enable) {
fTreeViewer.getTree().setEnabled(enable);
}
protected void handleCheckStateChanged(IResource resource, boolean checked) {
fOriginalResource = resource;
isChecked = checked;
boolean wasTopParentChecked = fTreeViewer.getChecked(fOriginalResource.getParent());
if (!isChecked) {
resource = handleAllUnselected(resource, resource.getName());
}
fParentResource = resource;
handleBuildCheckStateChange(wasTopParentChecked);
}
protected IResource handleAllUnselected(IResource resource, String name) {
IResource parent = resource.getParent();
if (parent.equals(fBundleRoot)) {
return resource;
}
try {
boolean uncheck = true;
IResource[] members = ((IFolder) parent).members();
for (int i = 0; i < members.length; i++) {
if (fTreeViewer.getChecked(members[i]) && !members[i].getName().equals(name))
uncheck = false;
}
if (uncheck) {
return handleAllUnselected(parent, parent.getName());
}
return resource;
} catch (CoreException e) {
PDEPlugin.logException(e);
return null;
}
}
protected void setChildrenGrayed(IResource folder, boolean isGray) {
fTreeViewer.setGrayed(folder, isGray);
if (((TreeContentProvider) fTreeViewer.getContentProvider()).hasChildren(folder)) {
Object[] members = ((TreeContentProvider) fTreeViewer.getContentProvider()).getFolderChildren(folder);
for (Object member : members) {
setChildrenGrayed((IFolder) member, isGray);
}
}
}
protected void setParentsChecked(IResource resource) {
if (resource.getParent() != resource.getProject()) {
fTreeViewer.setChecked(resource.getParent(), true);
setParentsChecked(resource.getParent());
}
}
/**
* removes all child resources of the specified folder from build entries
*
* @param folder -
* current folder being modified in tree
*
* note: does not remove folder itself
*/
protected abstract void deleteFolderChildrenFromEntries(IFolder folder);
protected void initializeCheckState() {
uncheckAll();
}
protected void initializeCheckState(final IBuildEntry includes, final IBuildEntry excludes) {
fTreeViewer.getTree().getDisplay().asyncExec(() -> BusyIndicator.showWhile(Display.getCurrent(), () -> {
if (fTreeViewer.getTree().isDisposed())
return;
Vector<String> fileExt = new Vector<>();
String[] inclTokens, exclTokens = new String[0];
if (fBundleRoot == null || includes == null)
return;
inclTokens = includes.getTokens();
if (excludes != null)
exclTokens = excludes.getTokens();
Set<String> temp = new TreeSet<>();
for (String inclToken : inclTokens)
temp.add(inclToken);
for (String exclToken : exclTokens)
temp.add(exclToken);
Iterator<String> iter = temp.iterator();
while (iter.hasNext()) {
String resource = iter.next().toString();
boolean isIncluded = includes.contains(resource);
if (resource.isEmpty()) {
// ignore - empty line
} else if (resource.equals(".") || resource.equals("./") || resource.equals(".\\")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
// ignore - should be root directory
} else if (resource.lastIndexOf(IPath.SEPARATOR) == resource.length() - 1) {
IFolder folder = fBundleRoot.getFolder(new Path(resource));
if (!folder.exists())
continue;
fTreeViewer.setSubtreeChecked(folder, isIncluded);
fTreeViewer.setParentsGrayed(folder, true);
if (isIncluded) {
setParentsChecked(folder);
fTreeViewer.setGrayed(folder, false);
}
} else if (resource.startsWith("*.")) { //$NON-NLS-1$
if (isIncluded)
fileExt.add(resource.substring(2));
} else {
IFile file = fBundleRoot.getFile(new Path(resource));
if (!file.exists())
continue;
fTreeViewer.setChecked(file, isIncluded);
fTreeViewer.setParentsGrayed(file, true);
if (isIncluded) {
fTreeViewer.setGrayed(file, false);
setParentsChecked(file);
}
}
}
if (fileExt.size() == 0)
return;
try {
IResource[] members = fBundleRoot.members();
for (int i = 0; i < members.length; i++) {
if (!(members[i] instanceof IFolder) && (fileExt.contains(members[i].getFileExtension()))) {
fTreeViewer.setChecked(members[i], includes.contains("*." //$NON-NLS-1$
+ members[i].getFileExtension()));
}
}
} catch (CoreException e) {
PDEPlugin.logException(e);
}
}));
}
protected abstract void handleBuildCheckStateChange(boolean wasTopParentChecked);
protected void handleCheck(IBuildEntry includes, IBuildEntry excludes, String resourceName, IResource resource, boolean wasTopParentChecked, String PROPERTY_INCLUDES) {
try {
if (includes == null) {
includes = fBuildModel.getFactory().createEntry(PROPERTY_INCLUDES);
IBuild build = fBuildModel.getBuild();
build.add(includes);
}
if ((!wasTopParentChecked && !includes.contains(resourceName)) || isValidIncludeEntry(includes, excludes, resource, resourceName)) {
includes.addToken(resourceName);
}
if (excludes != null && excludes.contains(resourceName))
excludes.removeToken(resourceName);
} catch (CoreException e) {
PDEPlugin.logException(e);
}
}
protected boolean isValidIncludeEntry(IBuildEntry includes, IBuildEntry excludes, IResource resource, String resourceName) {
if (excludes == null)
return true;
IPath resPath = resource.getProjectRelativePath();
while (resPath.segmentCount() > 1) {
resPath = resPath.removeLastSegments(1);
if (includes.contains(resPath.toString() + IPath.SEPARATOR))
return false;
else if (excludes.contains(resPath.toString() + IPath.SEPARATOR))
return true;
}
return !excludes.contains(resourceName);
}
protected void handleUncheck(IBuildEntry includes, IBuildEntry excludes, String resourceName, IResource resource, String PROPERTY_EXCLUDES) {
try {
if (fTreeViewer.getChecked(resource.getParent())) {
if (excludes == null) {
excludes = fBuildModel.getFactory().createEntry(PROPERTY_EXCLUDES);
IBuild build = fBuildModel.getBuild();
build.add(excludes);
}
if (!excludes.contains(resourceName) && (includes != null ? !includes.contains(resourceName) : true))
excludes.addToken(resourceName);
}
if (includes != null) {
if (includes.contains(resourceName))
includes.removeToken(resourceName);
if (includes.contains("*." + resource.getFileExtension())) { //$NON-NLS-1$
IResource[] members = fBundleRoot.members();
for (int i = 0; i < members.length; i++) {
if (!(members[i] instanceof IFolder) && !members[i].getName().equals(resource.getName()) && (resource.getFileExtension().equals(members[i].getFileExtension()))) {
includes.addToken(members[i].getName());
}
IBuildEntry[] libraries = BuildUtil.getBuildLibraries(fBuildModel.getBuild().getBuildEntries());
if (resource.getFileExtension().equals("jar") //$NON-NLS-1$
&& libraries.length != 0) {
for (IBuildEntry library : libraries) {
String libName = library.getName().substring(7);
IPath path = fBundleRoot.getFile(new Path(libName)).getProjectRelativePath().makeRelativeTo(fBundleRoot.getProjectRelativePath());
if (path.segmentCount() == 1 && !includes.contains(libName) && !libName.equals(resource.getName()))
includes.addToken(libName);
}
}
}
includes.removeToken("*." + resource.getFileExtension()); //$NON-NLS-1$
}
}
} catch (CoreException e) {
PDEPlugin.logException(e);
}
}
protected String getResourceFolderName(String resourceName) {
return resourceName + IPath.SEPARATOR;
}
/**
* @param resource -
* file/folder being modified in tree
* @param resourceName -
* name file/folder
* @return relative path of folder if resource is folder, otherwise, return
* resourceName
*/
protected String handleResourceFolder(IResource resource, String resourceName) {
if (resource instanceof IFolder) {
deleteFolderChildrenFromEntries((IFolder) resource);
return getResourceFolderName(resourceName);
}
return resourceName;
}
public void initialize() {
if (fTreeViewer.getInput() == null) {
fTreeViewer.setUseHashlookup(true);
fTreeViewer.setInput(fBundleRoot);
}
fBuildModel.addModelChangedListener(this);
}
@Override
public void dispose() {
fBuildModel.removeModelChangedListener(this);
PDEPlugin.getWorkspace().removeResourceChangeListener(this);
super.dispose();
}
protected void deleteEmptyEntries() {
IBuild build = fBuildModel.getBuild();
IBuildEntry[] entries = {build.getEntry(IBuildPropertiesConstants.PROPERTY_BIN_EXCLUDES), build.getEntry(IBuildPropertiesConstants.PROPERTY_BIN_INCLUDES), build.getEntry(IBuildPropertiesConstants.PROPERTY_SRC_EXCLUDES), build.getEntry(IBuildPropertiesConstants.PROPERTY_SRC_INCLUDES)};
try {
for (IBuildEntry entry : entries) {
if (entry != null && entry.getTokens().length == 0)
build.remove(entry);
}
} catch (CoreException e) {
PDEPlugin.logException(e);
}
}
public CheckboxTreeViewer getTreeViewer() {
return fTreeViewer;
}
@Override
protected IStructuredSelection getViewerSelection() {
return getTreeViewer().getStructuredSelection();
}
@Override
public void refresh() {
initializeCheckState();
super.refresh();
}
public void uncheckAll() {
fTreeViewer.setCheckedElements(new Object[0]);
}
protected void removeChildren(IBuildEntry entry, String parentFolder) {
try {
if (entry != null) {
String[] tokens = entry.getTokens();
for (int i = 0; i < tokens.length; i++) {
if (tokens[i].indexOf(IPath.SEPARATOR) != -1 && tokens[i].startsWith(parentFolder) && !tokens[i].equals(parentFolder)) {
entry.removeToken(tokens[i]);
}
}
}
} catch (CoreException e) {
PDEPlugin.logException(e);
}
}
@Override
public void resourceChanged(IResourceChangeEvent event) {
if (fTreeViewer.getControl().isDisposed())
return;
fDoRefresh = false;
IResourceDelta delta = event.getDelta();
try {
if (delta != null)
delta.accept(this);
if (fDoRefresh) {
asyncRefresh();
fDoRefresh = false;
}
} catch (CoreException e) {
}
}
@Override
public boolean visit(IResourceDelta delta) throws CoreException {
IResource resource = delta.getResource();
IProject project = fBuildModel.getUnderlyingResource().getProject();
if ((resource instanceof IFile || resource instanceof IFolder) && resource.getProject().equals(project)) {
if (delta.getKind() == IResourceDelta.ADDED || delta.getKind() == IResourceDelta.REMOVED) {
fDoRefresh = true;
return false;
}
} else if (resource instanceof IProject && ((IProject) resource).equals(project)) {
return delta.getKind() != IResourceDelta.REMOVED;
}
return true;
}
private void asyncRefresh() {
Control control = fTreeViewer.getControl();
if (!control.isDisposed()) {
control.getDisplay().asyncExec(() -> {
if (!fTreeViewer.getControl().isDisposed()) {
fTreeViewer.refresh(true);
initializeCheckState();
}
});
}
}
@Override
protected void selectionChanged(IStructuredSelection selection) {
getPage().getPDEEditor().setSelection(selection);
}
@Override
public void modelChanged(IModelChangedEvent event) {
if (event.getChangeType() == IModelChangedEvent.WORLD_CHANGED) {
markStale();
}
Object changeObject = event.getChangedObjects()[0];
if (!(changeObject instanceof IBuildEntry && (((IBuildEntry) changeObject).getName().equals(IBuildPropertiesConstants.PROPERTY_BIN_EXCLUDES) || ((IBuildEntry) changeObject).getName().equals(IBuildPropertiesConstants.PROPERTY_BIN_INCLUDES) || ((IBuildEntry) changeObject).getName().equals(IBuildPropertiesConstants.PROPERTY_SRC_EXCLUDES) || ((IBuildEntry) changeObject).getName().equals(IBuildPropertiesConstants.PROPERTY_SRC_INCLUDES))))
return;
if ((fParentResource == null && fOriginalResource != null) || (fOriginalResource == null && fParentResource != null)) {
initializeCheckState();
return;
}
if ((fParentResource == null && fOriginalResource == null) || (event.getChangedProperty() != null && event.getChangedProperty().equals(IBuildPropertiesConstants.PROPERTY_BIN_INCLUDES))) {
return;
}
fTreeViewer.setChecked(fParentResource, isChecked);
fTreeViewer.setGrayed(fOriginalResource, false);
fTreeViewer.setParentsGrayed(fParentResource, true);
setParentsChecked(fParentResource);
fTreeViewer.setGrayed(fParentResource, false);
if (fParentResource instanceof IFolder) {
fTreeViewer.setSubtreeChecked(fParentResource, isChecked);
setChildrenGrayed(fParentResource, false);
}
while (!fOriginalResource.equals(fParentResource)) {
fTreeViewer.setChecked(fOriginalResource, isChecked);
fOriginalResource = fOriginalResource.getParent();
}
fParentResource = null;
fOriginalResource = null;
}
}