blob: 9dcba6ea9fb4e86a839a35e64310d668c0e85c38 [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.corext.buildpath;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.eclipse.core.filesystem.EFS;
import org.eclipse.core.filesystem.IFileStore;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.dltk.core.DLTKCore;
import org.eclipse.dltk.core.DLTKLanguageManager;
import org.eclipse.dltk.core.IBuildpathEntry;
import org.eclipse.dltk.core.IModelElement;
import org.eclipse.dltk.core.IModelStatus;
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.internal.core.BuildpathEntry;
import org.eclipse.dltk.internal.corext.util.Messages;
import org.eclipse.dltk.internal.ui.scriptview.BuildPathContainer;
import org.eclipse.dltk.internal.ui.wizards.NewWizardMessages;
import org.eclipse.dltk.internal.ui.wizards.buildpath.ArchiveFileFilter;
import org.eclipse.dltk.internal.ui.wizards.buildpath.BPListElement;
import org.eclipse.dltk.internal.ui.wizards.buildpath.BPListElementAttribute;
import org.eclipse.dltk.internal.ui.wizards.buildpath.BuildPathBasePage;
import org.eclipse.dltk.internal.ui.wizards.buildpath.newsourcepage.BuildpathModifierQueries;
import org.eclipse.dltk.internal.ui.wizards.buildpath.newsourcepage.BuildpathModifierQueries.IAddArchivesQuery;
import org.eclipse.dltk.internal.ui.wizards.buildpath.newsourcepage.BuildpathModifierQueries.IAddLibrariesQuery;
import org.eclipse.dltk.internal.ui.wizards.buildpath.newsourcepage.BuildpathModifierQueries.ICreateFolderQuery;
import org.eclipse.dltk.internal.ui.wizards.buildpath.newsourcepage.BuildpathModifierQueries.IInclusionExclusionQuery;
import org.eclipse.dltk.internal.ui.wizards.buildpath.newsourcepage.BuildpathModifierQueries.ILinkToQuery;
import org.eclipse.dltk.internal.ui.wizards.buildpath.newsourcepage.BuildpathModifierQueries.IRemoveLinkedFolderQuery;
import org.eclipse.dltk.ui.dialogs.StatusInfo;
public class BuildpathModifier {
/**
* Interface for listeners that want to receive a notification about changes
* on <code>IBuildpathEntry</code>. For example, if a source folder changes
* one of it's inclusion/exclusion filters, then this event will be fired.
*/
public static interface IBuildpathModifierListener {
/**
* The new build path entry that was generated upon calling a method of
* <code>BuildpathModifier</code>. The type indicates which kind of
* interaction was executed on this entry.
*
* Note that the list does not contain elements of type
* <code>IBuildpathEntry</code>, but <code>BPListElement</code>
*
* @param newEntries
* list of <code>BPListElement</code>
*/
public void buildpathEntryChanged(List newEntries);
}
private IBuildpathModifierListener fListener;
public BuildpathModifier() {
this(null);
}
protected BuildpathModifier(IBuildpathModifierListener listener) {
fListener = listener;
}
/**
* Create a linked source folder.
*
* @param query
* a query to create a linked source folder
* @param project
* the script project
* @param monitor
* progress monitor, can be <code>null</code>
* @return a list containing a <code>IProjectFragment</code> representing
* the linked source folder
* @throws CoreException
*/
protected List createLinkedSourceFolder(ILinkToQuery query,
IScriptProject project, IProgressMonitor monitor)
throws CoreException {
if (query.doQuery()) {
IFolder folder = query.getCreatedFolder();
if (folder != null) {
List folderList = new ArrayList();
folderList.add(folder);
List root = addToBuildpath(folderList, project, monitor);
if (root.size() == 0)
folder.delete(false, null);
return root;
}
}
return new ArrayList();
}
/**
* Create a folder given a <code>FolderCreationQuery</code>. The query does
* only have to handle the creation of the folder, filter manipulations are
* handlet by the <code>
* Buildpathmodifier</code> itself using the return value of
* <code>FolderCreationQuery.getCreatedFolder()</code>.
*
* @param folderQuery
* query to create the new folder
* @param outputQuery
* query to get information about whether the project
* should be removed as source folder and update
* build folder to <code>outputLocation</code>
* @param project
* the script project
* @param monitor
* progress monitor, can be <code>null</code>
* @return a list containing the created object (either of type
* <code>IResource</code> of <code>IModelElement</code>, or an empty
* list if no folder was created (e.g. the operation was cancelled).
* @throws CoreException
* @throws OperationCanceledException
* @see BuildpathModifierQueries.ICreateFolderQuery
* @see BuildpathModifierQueries.OutputFolderQuery
*/
protected List createFolder(ICreateFolderQuery folderQuery,
IScriptProject project, IProgressMonitor monitor)
throws OperationCanceledException, CoreException {
if (folderQuery.doQuery()) {
IFolder folder = folderQuery.getCreatedFolder();
if (folder != null) {
List folderList = new ArrayList();
folderList.add(folder);
if (folderQuery.isSourceFolder()) {
List root = addToBuildpath(folderList, project, monitor);
if (root.size() == 0)
folder.delete(false, null);
return root;
}
List entries = getExistingEntries(project);
exclude(folder.getFullPath(), entries, new ArrayList(), project,
monitor);
updateBuildpath(entries, project, null);
return folderList;
}
}
return new ArrayList();
}
/**
* Add a list of elements to the build path.
*
* @param elements
* a list of elements to be added to the build path. An
* element must either be of type <code>IFolder</code>,
* <code>IModelElement</code> or <code>IFile</code>
* (only allowed if the file is a .zip file!).
* @param project
* the script project
* @param query
* for information about whether the project should be
* removed as source folder and update build folder
* @param monitor
* progress monitor, can be <code>null</code>
* @return returns a list of elements of type <code>IProjectFragment</code>
* or <code>IScriptProject</code> that have been added to the build
* path or an empty list if the operation was aborted
* @throws CoreException
* @throws OperationCanceledException
* @see BuildpathModifierQueries.OutputFolderQuery
*/
protected List addToBuildpath(List elements, IScriptProject project,
IProgressMonitor monitor)
throws OperationCanceledException, CoreException {
if (monitor == null)
monitor = new NullProgressMonitor();
try {
monitor.beginTask(
NewWizardMessages.BuildpathModifier_Monitor_AddToBuildpath,
2 * elements.size() + 3);
// IWorkspaceRoot workspaceRoot=
// DLTKUIPlugin.getWorkspace().getRoot();
if (DLTKLanguageManager.hasScriptNature(project.getProject())) {
// IPath outputLocation= project.getOutputLocation();
// IPath projPath= project.getProject().getFullPath();
List<BPListElement> existingEntries = getExistingEntries(
project);
List<BPListElement> newEntries = new ArrayList<>();
for (int i = 0; i < elements.size(); i++) {
Object element = elements.get(i);
BPListElement entry;
if (element instanceof IResource)
entry = addToBuildpath((IResource) element,
existingEntries, newEntries, project, monitor);
else
entry = addToBuildpath((IModelElement) element,
existingEntries, newEntries, project, monitor);
newEntries.add(entry);
}
Set modifiedSourceEntries = new HashSet();
BuildPathBasePage.fixNestingConflicts(
newEntries
.toArray(new BPListElement[newEntries.size()]),
existingEntries.toArray(
new BPListElement[existingEntries.size()]),
modifiedSourceEntries);
setNewEntry(existingEntries, newEntries, project,
new SubProgressMonitor(monitor, 1));
updateBuildpath(existingEntries, project,
new SubProgressMonitor(monitor, 1));
List result = new ArrayList();
for (int i = 0; i < newEntries.size(); i++) {
IBuildpathEntry entry = newEntries.get(i)
.getBuildpathEntry();
IModelElement root;
if (entry.getPath().equals(project.getPath()))
root = project;
else
root = project.findProjectFragment(entry.getPath());
if (root != null) {
result.add(root);
}
}
return result;
}
StatusInfo rootStatus = new StatusInfo();
rootStatus.setError(
NewWizardMessages.BuildpathModifier_Error_NoNatures);
throw new CoreException(rootStatus);
} finally {
monitor.done();
}
}
/**
* Add external archives (.zip files) to the buildpath. The method uses the
* query to find out which entries need to be added.
*
* @param query
* the query to get the information which entries need to
* be added
* @param project
* the script project
* @param monitor
* progress monitor, can be <code>null</code>
* @return a list of <code>IProjectFragment</code>s representing the added
* archives or an empty list if no element was added.
* @throws CoreException
*
* @see IAddArchivesQuery
*/
protected List addExternalArchives(IAddArchivesQuery query,
IScriptProject project, IProgressMonitor monitor)
throws CoreException {
if (monitor == null)
monitor = new NullProgressMonitor();
IPath[] selected = query.doQuery();
List<BPListElement> addedEntries = new ArrayList<>();
try {
monitor.beginTask(
NewWizardMessages.BuildpathModifier_Monitor_AddToBuildpath,
4);
if (selected != null) {
for (int i = 0; i < selected.length; i++) {
addedEntries.add(new BPListElement(project,
IBuildpathEntry.BPE_LIBRARY, selected[i], null,
false));
}
monitor.worked(1);
List<BPListElement> existingEntries = getExistingEntries(
project);
setNewEntry(existingEntries, addedEntries, project,
new SubProgressMonitor(monitor, 1));
updateBuildpath(existingEntries, project,
new SubProgressMonitor(monitor, 1));
List result = new ArrayList(addedEntries.size());
for (int i = 0; i < addedEntries.size(); i++) {
IBuildpathEntry entry = addedEntries.get(i)
.getBuildpathEntry();
IModelElement elem = project
.findProjectFragment(entry.getPath());
if (elem != null) {
result.add(elem);
}
}
monitor.worked(1);
return result;
}
} finally {
monitor.done();
}
return new ArrayList();
}
/**
* Add libraries to the buildpath. The method uses the query to find out
* which entries need to be added.
*
* @param query
* the query to get the information which entries need to
* be added
* @param project
* the script project
* @param monitor
* progress monitor, can be <code>null</code>
* @return a list of <code>BuildpathContainer</code>s representing the added
* archives or an empty list if no element was added.
* @throws CoreException
*
* @see IAddArchivesQuery
*/
protected List<BuildPathContainer> addLibraries(IAddLibrariesQuery query,
IScriptProject project, IProgressMonitor monitor)
throws CoreException {
if (monitor == null)
monitor = new NullProgressMonitor();
IBuildpathEntry[] selected = query.doQuery(project,
project.getRawBuildpath());
List<BPListElement> addedEntries = new ArrayList<>();
try {
monitor.beginTask(
NewWizardMessages.BuildpathModifier_Monitor_AddToBuildpath,
4);
if (selected != null) {
for (int i = 0; i < selected.length; i++) {
addedEntries.add(new BPListElement(project,
IBuildpathEntry.BPE_CONTAINER,
selected[i].getPath(), null, false));
}
monitor.worked(1);
List<BPListElement> existingEntries = getExistingEntries(
project);
setNewEntry(existingEntries, addedEntries, project,
new SubProgressMonitor(monitor, 1));
updateBuildpath(existingEntries, project,
new SubProgressMonitor(monitor, 1));
List<BuildPathContainer> result = new ArrayList<>(
addedEntries.size());
for (int i = 0; i < addedEntries.size(); i++) {
result.add(new BuildPathContainer(project, selected[i]));
}
monitor.worked(1);
return result;
}
} finally {
monitor.done();
}
return new ArrayList<>();
}
protected List addLibraryEntries(List resources, IScriptProject project,
IProgressMonitor monitor) throws CoreException {
if (monitor == null)
monitor = new NullProgressMonitor();
List addedEntries = new ArrayList();
try {
monitor.beginTask(
NewWizardMessages.BuildpathModifier_Monitor_AddToBuildpath,
4);
for (int i = 0; i < resources.size(); i++) {
IResource res = (IResource) resources.get(i);
addedEntries.add(
new BPListElement(project, IBuildpathEntry.BPE_LIBRARY,
res.getFullPath(), res, false));
}
monitor.worked(1);
List existingEntries = getExistingEntries(project);
setNewEntry(existingEntries, addedEntries, project,
new SubProgressMonitor(monitor, 1));
updateBuildpath(existingEntries, project,
new SubProgressMonitor(monitor, 1));
List result = new ArrayList(addedEntries.size());
for (int i = 0; i < resources.size(); i++) {
IResource res = (IResource) resources.get(i);
IModelElement elem = project.getProjectFragment(res);
if (elem != null) {
result.add(elem);
}
}
monitor.worked(1);
return result;
} finally {
monitor.done();
}
}
/**
* Remove a list of elements to the build path.
*
* @param query
* query to remove unused linked folders from the
* project
* @param elements
* a list of elements to be removed from the build path.
* An element must either be of type
* <code>IScriptProject</code>,
* <code>IProjectFragment</code> or
* <code>BuildPathContainer</code>
* @param project
* the script project
* @param monitor
* progress monitor, can be <code>null</code>
* @return returns a list of elements of type <code>IFile</code> (in case of
* removed archives) or <code>IFolder</code> that have been removed
* from the build path
* @throws CoreException
* @throws OperationCanceledException
*/
protected List removeFromBuildpath(IRemoveLinkedFolderQuery query,
List elements, IScriptProject project, IProgressMonitor monitor)
throws CoreException {
if (monitor == null)
monitor = new NullProgressMonitor();
try {
monitor.beginTask(
NewWizardMessages.BuildpathModifier_Monitor_RemoveFromBuildpath,
elements.size() + 1);
List existingEntries = getExistingEntries(project);
List resultElements = new ArrayList();
boolean archiveRemoved = false;
for (int i = 0; i < elements.size(); i++) {
Object element = elements.get(i);
Object res = null;
if (element instanceof IScriptProject) {
res = removeFromBuildpath(project, existingEntries,
new SubProgressMonitor(monitor, 1));
} else {
if (element instanceof IProjectFragment) {
IProjectFragment root = (IProjectFragment) element;
if (root.getKind() == IProjectFragment.K_BINARY) {
archiveRemoved = true;
res = removeFromBuildpath(root, existingEntries,
project,
new SubProgressMonitor(monitor, 1));
} else {
final IResource resource = root
.getCorrespondingResource();
if (resource instanceof IFolder) {
final IFolder folder = (IFolder) resource;
if (folder.isLinked()) {
final int result = query.doQuery(folder);
if (result != IRemoveLinkedFolderQuery.REMOVE_CANCEL) {
if (result == IRemoveLinkedFolderQuery.REMOVE_BUILD_PATH) {
res = removeFromBuildpath(root,
existingEntries, project,
new SubProgressMonitor(
monitor, 1));
} else if (result == IRemoveLinkedFolderQuery.REMOVE_BUILD_PATH_AND_FOLDER) {
res = removeFromBuildpath(root,
existingEntries, project,
new SubProgressMonitor(
monitor, 1));
folder.delete(true, true,
new SubProgressMonitor(
monitor, 1));
}
}
} else {
res = removeFromBuildpath(root,
existingEntries, project,
new SubProgressMonitor(monitor, 1));
}
} else {
res = removeFromBuildpath(root, existingEntries,
project,
new SubProgressMonitor(monitor, 1));
}
}
} else {
archiveRemoved = true;
BuildPathContainer container = (BuildPathContainer) element;
existingEntries.remove(BPListElement.createFromExisting(
container.getBuildpathEntry(), project));
}
}
if (res != null) {
resultElements.add(res);
}
}
updateBuildpath(existingEntries, project,
new SubProgressMonitor(monitor, 1));
fireEvent(existingEntries);
if (archiveRemoved && resultElements.size() == 0)
resultElements.add(project);
return resultElements;
} finally {
monitor.done();
}
}
/**
* Include a list of elements to the build path. This means that the
* inclusion filter for the corresponding <code>IProjectFragment</code>s
* need to be modified. All elements must be either be of type
* <code>IResource</code> or <code>IModelElement</code>.
*
* Note: the <code>IModelElement</code>'s fragment (if there is one) is not
* allowed to be excluded! However, inclusion (or simply no filter) on the
* parent fragment is allowed.
*
* @param elements
* a list of elements to be included. The elements must
* be either of type <code>IResource</code> or
* <code>IModelElement</code>.
* @param project
* the script project
* @param monitor
* progress monitor, can be <code>null</code>
* @return a list of <code>IModelElement</code>s corresponding to the
* included ones.
* @throws ModelException
*
* @see #exclude(List, IScriptProject, IProgressMonitor)
*/
protected List include(List elements, IScriptProject project,
IProgressMonitor monitor) throws ModelException {
if (monitor == null)
monitor = new NullProgressMonitor();
try {
monitor.beginTask(
NewWizardMessages.BuildpathModifier_Monitor_Including,
2 * elements.size());
List existingEntries = getExistingEntries(project);
List resources = new ArrayList();
for (int i = 0; i < elements.size(); i++) {
IResource resource;
if (elements.get(i) instanceof IResource)
resource = (IResource) elements.get(i);
else {
IModelElement elem = (IModelElement) elements.get(i);
resource = elem.getResource();
}
resources.add(resource);
IProjectFragment root = getFragmentRoot(resource, project,
new SubProgressMonitor(monitor, 1));
if (root != null) {
BPListElement entry = getBuildpathEntry(existingEntries,
root);
include(resource, entry, project,
new SubProgressMonitor(monitor, 1));
}
}
updateBuildpath(existingEntries, project,
new SubProgressMonitor(monitor, 4));
List scriptElements = getCorrespondingElements(resources, project);
return scriptElements;
} finally {
monitor.done();
}
}
/**
* Exclude a list of <code>IModelElement</code>s. This means that the
* exclusion filter for the corresponding <code>IProjectFragment</code>s
* needs to be modified.
*
* Note: the <code>IModelElement</code>'s fragment (if there is one) is not
* allowed to be excluded! However, inclusion (or simply no filter) on the
* parent fragment is allowed.
*
* @param scriptElements
* list of script elements to be excluded
* @param project
* the script project
* @param monitor
* progress monitor, can be <code>null</code>
* @return list of objects representing the excluded elements
* @throws ModelException
*/
protected List exclude(List scriptElements, IScriptProject project,
IProgressMonitor monitor) throws ModelException {
if (monitor == null)
monitor = new NullProgressMonitor();
try {
monitor.beginTask(
NewWizardMessages.BuildpathModifier_Monitor_Excluding,
scriptElements.size() + 4);
List existingEntries = getExistingEntries(project);
List resources = new ArrayList();
for (int i = 0; i < scriptElements.size(); i++) {
IModelElement scriptElement = (IModelElement) scriptElements
.get(i);
IProjectFragment root = (IProjectFragment) scriptElement
.getAncestor(IModelElement.PROJECT_FRAGMENT);
BPListElement entry = getBuildpathEntry(existingEntries, root);
IResource resource = exclude(scriptElement, entry, project,
new SubProgressMonitor(monitor, 1));
if (resource != null) {
resources.add(resource);
}
}
updateBuildpath(existingEntries, project,
new SubProgressMonitor(monitor, 4));
return resources;
} finally {
monitor.done();
}
}
/**
* Inverse operation to include. The <code>IModelElement</code>s in the list
* will be removed from their fragment roots inclusion filter.
*
* Note: the <code>IModelElement</code>'s fragment (if there is one) is not
* allowed to be excluded! However, inclusion (or simply no filter) on the
* parent fragment is allowed.
*
* @param scriptElements
* a list of <code>IModelElements</code> to be
* unincluded
* @param project
* the script project
* @param monitor
* progress monitor, can be <code>null</code>
* @return a list of elements representing unexcluded elements
* @throws ModelException
*
* @see #include(List, IScriptProject, IProgressMonitor)
*/
protected List unInclude(List scriptElements, IScriptProject project,
IProgressMonitor monitor) throws ModelException {
if (monitor == null)
monitor = new NullProgressMonitor();
try {
monitor.beginTask(
NewWizardMessages.BuildpathModifier_Monitor_RemoveInclusion,
10);
List existingEntries = getExistingEntries(project);
for (int i = 0; i < scriptElements.size(); i++) {
IModelElement scriptElement = (IModelElement) scriptElements
.get(i);
IProjectFragment root = (IProjectFragment) scriptElement
.getAncestor(IModelElement.PROJECT_FRAGMENT);
BPListElement entry = getBuildpathEntry(existingEntries, root);
unInclude(scriptElement, entry, project,
new SubProgressMonitor(monitor, 1));
}
updateBuildpath(existingEntries, project,
new SubProgressMonitor(monitor, 4));
List result = getCorrespondingElements(scriptElements, project);
return result;
} finally {
monitor.done();
}
}
/**
* Inverse operation to <code>exclude</code>. The list of elements of type
* <code>IResource</code> will be removed from the exclusion filters of
* their parent roots.
*
* Note: the <code>IModelElement</code>'s fragment (if there is one) is not
* allowed to be excluded! However, inclusion (or simply no filter) on the
* parent fragment is allowed.
*
* @param elements
* list of <code>IResource</code>s to be unexcluded
* @param project
* the script project
* @param monitor
* progress monitor, can be <code>null</code>
* @return an object representing the unexcluded element
* @throws ModelException
*
* @see #exclude(List, IScriptProject, IProgressMonitor)
* @see #unExclude(List, IScriptProject, IProgressMonitor)
*/
protected List unExclude(List elements, IScriptProject project,
IProgressMonitor monitor) throws ModelException {
if (monitor == null)
monitor = new NullProgressMonitor();
try {
monitor.beginTask(
NewWizardMessages.BuildpathModifier_Monitor_Including,
2 * elements.size());
List entries = getExistingEntries(project);
for (int i = 0; i < elements.size(); i++) {
IResource resource = (IResource) elements.get(i);
IProjectFragment root = getFragmentRoot(resource, project,
new SubProgressMonitor(monitor, 1));
if (root != null) {
BPListElement entry = getBuildpathEntry(entries, root);
unExclude(resource, entry, project,
new SubProgressMonitor(monitor, 1));
}
}
updateBuildpath(entries, project,
new SubProgressMonitor(monitor, 4));
List resultElements = getCorrespondingElements(elements, project);
return resultElements;
} finally {
monitor.done();
}
}
/**
* Edit the filters of a given <code>IModelElement</code> by using the
* passed <code>IInclusionExclusionQuery</code>.
*
* @param element
* the script element to edit the filters on. Must be
* either of type <code>IScriptProject</code> or
* <code>IProjectFragment</code>.
* @param project
* the script project
* @param monitor
* progress monitor, can be <code>null</code>
* @return returns the edited script element or <code>null</code> if the
* operation was cancelled
* @throws ModelException
*/
protected IModelElement editFilters(IModelElement element,
IScriptProject project, IInclusionExclusionQuery query,
IProgressMonitor monitor) throws ModelException {
if (monitor == null)
monitor = new NullProgressMonitor();
try {
monitor.beginTask(
NewWizardMessages.BuildpathModifier_Monitor_EditInclusionExclusionFilters,
4);
List existingEntries = getExistingEntries(project);
BPListElement entry = getListElement(element.getPath(),
existingEntries);
if (entry != null) {
if (query.doQuery(entry, false)) {
entry.setAttribute(BPListElement.INCLUSION,
query.getInclusionPattern());
entry.setAttribute(BPListElement.EXCLUSION,
query.getExclusionPattern());
updateBuildpath(existingEntries, project,
new SubProgressMonitor(monitor, 4));
return element;
}
}
} finally {
monitor.done();
}
return null;
}
/**
* Reset a list of elements. The elements can be either of type
* <li><code>IScriptProject</code></li>
* <li><code>IProjectFragment</code></li>
* <li><code>BPListElementAttribute</code></li><br>
*
* Depending on the element, resetting performs two different operations:
* <li>On <code>IScriptProject</code> or <code>IProjectFragment</code>, the
* inclusion and exclusion filters are reset. Only entries in the filters
* that correspond to either source folders or output folders will not be
* removed (to prevent damage on the project layout)</li>
* <li>On <code>BPListElementAttribute</code>, the output location of the
* given attribute is reset to the default output location.</li>
*
* @param project
* the script project
* @param monitor
* progress monitor, can be <code>null</code>
* @return a list of elements representing the elements on which 'reset' was
* called. They can either be of type <code>BPListElement</code>,
* <code>IScriptProject</code> or <code>IProjectFragment</code>
*/
protected List reset(List elements, IScriptProject project,
IProgressMonitor monitor) throws ModelException {
if (monitor == null)
monitor = new NullProgressMonitor();
try {
monitor.beginTask(
NewWizardMessages.BuildpathModifier_Monitor_Resetting,
elements.size());
List entries = getExistingEntries(project);
List result = new ArrayList();
for (int i = 0; i < elements.size(); i++) {
Object element = elements.get(i);
if (element instanceof IModelElement) {
IModelElement scriptElement = (IModelElement) element;
IProjectFragment root;
if (element instanceof IScriptProject)
root = project
.getProjectFragment(project.getResource());
else
root = (IProjectFragment) element;
BPListElement entry = getBuildpathEntry(entries, root);
resetFilters(scriptElement, entry, project,
new SubProgressMonitor(monitor, 1));
result.add(scriptElement);
} else {
// BPListElement selElement= ((BPListElementAttribute)
// element).getParent();
// BPListElement entry= getBuildpathEntry(entries,
// selElement);
}
}
updateBuildpath(entries, project, null);
fireEvent(entries);
return result;
} finally {
monitor.done();
}
}
/**
* Get the <code>IBuildpathEntry</code> from the project and convert it into
* a list of <code>BPListElement</code>s.
*
* @param project
* the script project to get it's build path entries from
* @return a list of <code>BPListElement</code>s corresponding to the build
* path entries of the project
* @throws ModelException
*/
public static List<BPListElement> getExistingEntries(IScriptProject project)
throws ModelException {
IBuildpathEntry[] buildpathEntries = project.getRawBuildpath();
ArrayList<BPListElement> newBuildPath = new ArrayList<>();
for (int i = 0; i < buildpathEntries.length; i++) {
IBuildpathEntry curr = buildpathEntries[i];
newBuildPath.add(BPListElement.createFromExisting(curr, project));
}
return newBuildPath;
}
/**
* Try to find the corresponding and modified <code>BPListElement</code> for
* the root in the list of elements and return it. If no one can be found,
* the roots <code>BuildpathEntry</code> is converted to a
* <code>BPListElement</code> and returned.
*
* @param elements
* a list of <code>BPListElements</code>
* @param root
* the root to find the <code>BuildpathEntry</code> for
* represented by a <code>BPListElement</code>
* @return the <code>BPListElement</code> found in the list (matching by
* using the path) or the roots own <code>IBuildpathEntry</code>
* converted to a <code>BPListElement</code>.
* @throws ModelException
*/
public static BPListElement getBuildpathEntry(List elements,
IProjectFragment root) throws ModelException {
IBuildpathEntry entry = root.getRawBuildpathEntry();
for (int i = 0; i < elements.size(); i++) {
BPListElement element = (BPListElement) elements.get(i);
if (element.getPath().equals(root.getPath())
&& element.getEntryKind() == entry.getEntryKind())
return (BPListElement) elements.get(i);
}
BPListElement newElement = BPListElement.createFromExisting(entry,
root.getScriptProject());
elements.add(newElement);
return newElement;
}
/**
* For a given <code>IResource</code>, try to convert it into a
* <code>IProjectFragment</code> if possible or return <code>null</code> if
* no fragment root could be created.
*
* @param resource
* the resource to be converted
* @return the <code>resource<code> as
* <code>IScriptFolder</code>,or <code>null</code> if failed to convert
*/
public static IScriptFolder getFragment(IResource resource) {
IModelElement elem = DLTKCore.create(resource);
if (elem instanceof IScriptFolder)
return (IScriptFolder) elem;
return null;
}
/**
* Get the source folder of a given <code>IResource</code> element, starting
* with the resource's parent.
*
* @param resource
* the resource to get the fragment root from
* @param project
* the script project
* @param monitor
* progress monitor, can be <code>null</code>
* @return resolved fragment root
* @throws ModelException
*/
public static IProjectFragment getFragmentRoot(IResource resource,
IScriptProject project, IProgressMonitor monitor)
throws ModelException {
if (monitor == null)
monitor = new NullProgressMonitor();
IModelElement scriptElem = null;
if (resource.getFullPath().equals(project.getPath()))
return project.getProjectFragment(resource);
IContainer container = resource.getParent();
do {
if (container instanceof IFolder)
scriptElem = DLTKCore.create(container);
if (container.getFullPath().equals(project.getPath())) {
scriptElem = project;
break;
}
container = container.getParent();
if (container == null)
return null;
} while (scriptElem == null
|| !(scriptElem instanceof IProjectFragment));
if (scriptElem instanceof IScriptProject) {
if (!isSourceFolder((IScriptProject) scriptElem))
return null;
scriptElem = project.getProjectFragment(project.getResource());
}
return (IProjectFragment) scriptElem;
}
/**
* Get the <code>IBuildpathEntry</code> for the given path by looking up all
* build path entries on the project
*
* @param path
* the path to find a build path entry for
* @param project
* the script project
* @return the <code>IBuildpathEntry</code> corresponding to the
* <code>path</code> or <code>null</code> if there is no such entry
* @throws ModelException
*/
public static IBuildpathEntry getBuildpathEntryFor(IPath path,
IScriptProject project, int entryKind) throws ModelException {
IBuildpathEntry[] entries = project.getRawBuildpath();
for (int i = 0; i < entries.length; i++) {
IBuildpathEntry entry = entries[i];
if (entry.getPath().equals(path)
&& equalEntryKind(entry, entryKind))
return entry;
}
return null;
}
/**
* Check whether the current selection is the project's default output
* folder or not
*
* @param attrib
* the attribute to be checked
* @return <code>true</code> if is the default output folder,
* <code>false</code> otherwise.
*/
public static boolean isDefaultOutputFolder(BPListElementAttribute attrib) {
return attrib.getValue() == null;
}
/**
* Determines whether the current selection (of type
* <code>ISourceModule</code> or <code>IScriptFolder</code>) is on the
* inclusion filter of it's parent source folder.
*
* @param selection
* the current script element
* @param project
* the script project
* @param monitor
* progress monitor, can be <code>null</code>
* @return <code>true</code> if the current selection is included,
* <code>false</code> otherwise.
* @throws ModelException
*/
public static boolean isIncluded(IModelElement selection,
IScriptProject project, IProgressMonitor monitor)
throws ModelException {
if (monitor == null)
monitor = new NullProgressMonitor();
try {
monitor.beginTask(
NewWizardMessages.BuildpathModifier_Monitor_ContainsPath,
4);
IProjectFragment root = (IProjectFragment) selection
.getAncestor(IModelElement.PROJECT_FRAGMENT);
IBuildpathEntry entry = root.getRawBuildpathEntry();
if (entry == null)
return false;
return contains(
selection.getPath()
.removeFirstSegments(root.getPath().segmentCount()),
entry.getInclusionPatterns(),
new SubProgressMonitor(monitor, 2));
} finally {
monitor.done();
}
}
/**
* Find out whether the <code>IResource</code> excluded or not.
*
* @param resource
* the resource to be checked
* @param project
* the script project
* @return <code>true</code> if the resource is excluded, <code>
* false</code> otherwise
* @throws ModelException
*/
public static boolean isExcluded(IResource resource, IScriptProject project)
throws ModelException {
IProjectFragment root = getFragmentRoot(resource, project, null);
if (root == null)
return false;
String fragmentName = getName(resource.getFullPath(), root.getPath());
fragmentName = completeName(project, fragmentName);
IBuildpathEntry entry = root.getRawBuildpathEntry();
return entry != null && contains(new Path(fragmentName),
entry.getExclusionPatterns(), null);
}
/**
* Find out whether one of the <code>IResource</code>'s parents is excluded.
*
* @param resource
* check the resources parents whether they are excluded
* or not
* @param project
* the script project
* @return <code>true</code> if there is an excluded parent,
* <code>false</code> otherwise
* @throws ModelException
*/
public static boolean parentExcluded(IResource resource,
IScriptProject project) throws ModelException {
if (resource.getFullPath().equals(project.getPath()))
return false;
IProjectFragment root = getFragmentRoot(resource, project, null);
if (root == null) {
return true;
}
IPath path = resource.getFullPath()
.removeFirstSegments(root.getPath().segmentCount());
IBuildpathEntry entry = root.getRawBuildpathEntry();
if (entry == null)
return true; // there is no build path entry, this is equal to the
// fact that the parent is excluded
while (path.segmentCount() > 0) {
if (contains(path, entry.getExclusionPatterns(), null))
return true;
path = path.removeLastSegments(1);
}
return false;
}
protected static String escapeSpecialChars(String value) {
StringBuffer buf = new StringBuffer();
for (int i = 0; i < value.length(); i++) {
char c = value.charAt(i);
switch (c) {
case '&':
buf.append("&amp;"); //$NON-NLS-1$
break;
case '<':
buf.append("&lt;"); //$NON-NLS-1$
break;
case '>':
buf.append("&gt;"); //$NON-NLS-1$
break;
case '\'':
buf.append("&apos;"); //$NON-NLS-1$
break;
case '\"':
buf.append("&quot;"); //$NON-NLS-1$
break;
case 160:
buf.append(" "); //$NON-NLS-1$
break;
default:
buf.append(c);
break;
}
}
return buf.toString();
}
/**
* Check whether the <code>IScriptProject</code> is a source folder
*
* @param project
* the project to test
* @return <code>true</code> if <code>project</code> is a source folder
* <code>false</code> otherwise.
*/
public static boolean isSourceFolder(IScriptProject project)
throws ModelException {
return BuildpathModifier.getBuildpathEntryFor(project.getPath(),
project, IBuildpathEntry.BPE_SOURCE) != null;
}
/**
* Check whether the <code>IScriptFolder</code> corresponds to the project's
* default fragment.
*
* @param fragment
* the package fragment to be checked
* @return <code>true</code> if is the default package fragment,
* <code>false</code> otherwise.
*/
public static boolean isDefaultFragment(IScriptFolder fragment) {
return fragment.getElementName().length() == 0;
}
/**
* Determines whether the inclusion filter of the element's source folder is
* empty or not
*
* @return <code>true</code> if the inclusion filter is empty,
* <code>false</code> otherwise.
* @throws ModelException
*/
public static boolean includeFiltersEmpty(IResource resource,
IScriptProject project, IProgressMonitor monitor)
throws ModelException {
if (monitor == null)
monitor = new NullProgressMonitor();
try {
monitor.beginTask(
NewWizardMessages.BuildpathModifier_Monitor_ExamineInputFilters,
4);
IProjectFragment root = getFragmentRoot(resource, project,
new SubProgressMonitor(monitor, 4));
if (root != null) {
IBuildpathEntry entry = root.getRawBuildpathEntry();
return entry.getInclusionPatterns().length == 0;
}
return true;
} finally {
monitor.done();
}
}
/**
* Check whether the input paramenter of type <code>
* IProjectFragment</code> has either it's inclusion or exclusion filter or
* both set (that means they are not empty).
*
* @param root
* the fragment root to be inspected
* @return <code>true</code> inclusion or exclusion filter set,
* <code>false</code> otherwise.
*/
public static boolean filtersSet(IProjectFragment root)
throws ModelException {
if (root == null)
return false;
IBuildpathEntry entry = root.getRawBuildpathEntry();
IPath[] inclusions = entry.getInclusionPatterns();
IPath[] exclusions = entry.getExclusionPatterns();
if (inclusions != null && inclusions.length > 0)
return true;
if (exclusions != null && exclusions.length > 0)
return true;
return false;
}
/**
* Add a resource to the build path.
*
* @param resource
* the resource to be added to the build path
* @param project
* the script project
* @param monitor
* progress monitor, can be <code>null</code>
* @return returns the new element of type <code>IProjectFragment</code>
* that has been added to the build path
* @throws CoreException
* @throws OperationCanceledException
*/
public static BPListElement addToBuildpath(IResource resource,
List existingEntries, List newEntries, IScriptProject project,
IProgressMonitor monitor)
throws OperationCanceledException, CoreException {
if (monitor == null)
monitor = new NullProgressMonitor();
try {
monitor.beginTask(
NewWizardMessages.BuildpathModifier_Monitor_AddToBuildpath,
2);
exclude(resource.getFullPath(), existingEntries, newEntries,
project, new SubProgressMonitor(monitor, 1));
BPListElement entry = new BPListElement(project,
IBuildpathEntry.BPE_SOURCE, resource.getFullPath(),
resource, false);
return entry;
} finally {
monitor.done();
}
}
/**
* Check whether the provided file is an archive (.zip).
*
* @param file
* the file to be checked
* @param project
* the script project
* @return <code>true</code> if the file is an archive, <code>false</code>
* otherwise
* @throws ModelException
*/
public static boolean isArchive(IFile file, IScriptProject project)
throws ModelException {
if (!ArchiveFileFilter.isArchivePath(file.getFullPath()))
return false;
if (project != null && project.exists()
&& (project.findProjectFragment(file.getFullPath()) == null))
return true;
return false;
}
/**
* Add a script element to the build path.
*
* @param scriptElement
* element to be added to the build path
* @param project
* the script project
* @param monitor
* progress monitor, can be <code>null</code>
* @return returns the new element of type <code>IProjectFragment</code>
* that has been added to the build path
* @throws CoreException
* @throws OperationCanceledException
*/
public static BPListElement addToBuildpath(IModelElement scriptElement,
List existingEntries, List newEntries, IScriptProject project,
IProgressMonitor monitor)
throws OperationCanceledException, CoreException {
if (monitor == null)
monitor = new NullProgressMonitor();
try {
monitor.beginTask(
NewWizardMessages.BuildpathModifier_Monitor_AddToBuildpath,
10);
BPListElement entry = new BPListElement(project,
IBuildpathEntry.BPE_SOURCE, scriptElement.getPath(),
scriptElement.getResource(), false);
return entry;
} finally {
monitor.done();
}
}
/**
* Remove the script project from the build path
*
* @param project
* the project to be removed
* @param existingEntries
* a list of existing <code>BPListElement</code>.
* This list will be traversed and the entry for
* the project will be removed.
* @param monitor
* progress monitor, can be <code>null</code>
* @return returns the script project
* @throws CoreException
*/
public static IScriptProject removeFromBuildpath(IScriptProject project,
List existingEntries, IProgressMonitor monitor)
throws CoreException {
BPListElement elem = getListElement(project.getPath(), existingEntries);
if (elem != null) {
existingEntries.remove(elem);
}
return project;
}
/**
* Remove a given <code>IProjectFragment</code> from the build path.
*
* @param root
* the <code>IProjectFragment</code> to be
* removed from the build path
* @param existingEntries
* a list of <code>BPListElements</code>
* representing the build path entries of the
* project. The entry for the root will be looked
* up and removed from the list.
* @param project
* the script project
* @param monitor
* progress monitor, can be <code>null</code>
* @return returns the <code>IResource</code> that has been removed from the
* build path; is of type <code>IFile</code> if the root was an
* archive, otherwise <code>IFolder</code> or <code>null<code> for
* external archives.
*/
public static IResource removeFromBuildpath(IProjectFragment root,
List existingEntries, IScriptProject project,
IProgressMonitor monitor) throws CoreException {
if (monitor == null)
monitor = new NullProgressMonitor();
try {
monitor.beginTask(
NewWizardMessages.BuildpathModifier_Monitor_RemoveFromBuildpath,
1);
IBuildpathEntry entry = root.getRawBuildpathEntry();
BPListElement elem = BPListElement.createFromExisting(entry,
project);
existingEntries.remove(elem);
removeFilters(elem.getPath(), project, existingEntries);
return elem.getResource();
} finally {
monitor.done();
}
}
/**
* Remove <code>path</code> from inclusion/exlusion filters in all
* <code>existingEntries</code>
*
* @param path
* the path to remove
* @param project
* the script project
* @param existingEntries
* a list of <code>BPListElement</code>
* representing the build path entries of the
* project.
* @return returns a <code>List</code> of <code>BPListElement</code> of
* modified elements, not null.
*/
public static List removeFilters(IPath path, IScriptProject project,
List existingEntries) {
if (path == null)
return Collections.EMPTY_LIST;
IPath projPath = project.getPath();
if (projPath.isPrefixOf(path)) {
path = path.removeFirstSegments(projPath.segmentCount())
.addTrailingSeparator();
}
List result = new ArrayList();
for (Iterator iter = existingEntries.iterator(); iter.hasNext();) {
BPListElement element = (BPListElement) iter.next();
boolean hasChange = false;
IPath[] exlusions = (IPath[]) element
.getAttribute(BPListElement.EXCLUSION);
if (exlusions != null) {
List<IPath> exlusionList = new ArrayList<>(exlusions.length);
for (int i = 0; i < exlusions.length; i++) {
if (!exlusions[i].equals(path)) {
exlusionList.add(exlusions[i]);
} else {
hasChange = true;
}
}
element.setAttribute(BPListElement.EXCLUSION,
exlusionList.toArray(new IPath[exlusionList.size()]));
}
IPath[] inclusion = (IPath[]) element
.getAttribute(BPListElement.INCLUSION);
if (inclusion != null) {
List<IPath> inclusionList = new ArrayList<>(inclusion.length);
for (int i = 0; i < inclusion.length; i++) {
if (!inclusion[i].equals(path)) {
inclusionList.add(inclusion[i]);
} else {
hasChange = true;
}
}
element.setAttribute(BPListElement.INCLUSION,
inclusionList.toArray(new IPath[inclusionList.size()]));
}
if (hasChange) {
result.add(element);
}
}
return result;
}
/**
* Include the given <code>IResource</code>. This means that the inclusion
* filter for the corresponding <code>IProjectFragment</code> needs to be
* modified.
*
* Note: the <code>IModelElement</code>'s fragment (if there is one) is not
* allowed to be excluded! However, inclusion (or simply no filter) on the
* parent fragment is allowed.
*
* @param resource
* the element to be included
* @param entry
* the <code>BPListElement</code> representing the
* <code>IBuildpathEntry</code> of the resource's root
* @param project
* the script project
* @param monitor
* progress monitor, can be <code>null</code>
*
* @throws ModelException
*
* @see #exclude(List, IScriptProject, IProgressMonitor)
*/
private void include(IResource resource, BPListElement entry,
IScriptProject project, IProgressMonitor monitor)
throws ModelException {
if (monitor == null)
monitor = new NullProgressMonitor();
try {
monitor.beginTask(
NewWizardMessages.BuildpathModifier_Monitor_Including, 10);
String name = getName(resource.getFullPath(), entry.getPath());
IPath[] includedPath = (IPath[]) entry
.getAttribute(BPListElement.INCLUSION);
IPath[] newIncludedPath = new IPath[includedPath.length + 1];
String completedName = completeName(project, name);
IPath relPath = new Path(completedName);
if (!contains(relPath, includedPath,
new SubProgressMonitor(monitor, 2))) {
System.arraycopy(includedPath, 0, newIncludedPath, 0,
includedPath.length);
newIncludedPath[includedPath.length] = relPath;
entry.setAttribute(BPListElement.INCLUSION, newIncludedPath);
entry.setAttribute(BPListElement.EXCLUSION, remove(relPath,
(IPath[]) entry.getAttribute(BPListElement.EXCLUSION),
new SubProgressMonitor(monitor, 2)));
}
} finally {
monitor.done();
}
}
/**
* Exclude an element with a given name and absolute path from the build
* path.
*
* @param name
* the name of the element to be excluded
* @param fullPath
* the absolute path of the element
* @param entry
* the build path entry to be modified
* @param project
* the script project
* @param monitor
* progress monitor, can be <code>null</code>
* @return a <code>IResource</code> corresponding to the excluded element
* @throws ModelException
*/
private static IResource exclude(String name, IPath fullPath,
BPListElement entry, IScriptProject project,
IProgressMonitor monitor) throws ModelException {
if (monitor == null)
monitor = new NullProgressMonitor();
IResource result;
try {
monitor.beginTask(
NewWizardMessages.BuildpathModifier_Monitor_Excluding, 6);
IPath[] excludedPath = (IPath[]) entry
.getAttribute(BPListElement.EXCLUSION);
IPath[] newExcludedPath = new IPath[excludedPath.length + 1];
name = completeName(fullPath, name);
IPath path = new Path(name);
if (!contains(path, excludedPath,
new SubProgressMonitor(monitor, 2))) {
System.arraycopy(excludedPath, 0, newExcludedPath, 0,
excludedPath.length);
newExcludedPath[excludedPath.length] = path;
entry.setAttribute(BPListElement.EXCLUSION, newExcludedPath);
entry.setAttribute(BPListElement.INCLUSION, remove(path,
(IPath[]) entry.getAttribute(BPListElement.INCLUSION),
new SubProgressMonitor(monitor, 4)));
}
result = fullPath == null ? null : getResource(fullPath, project);
} finally {
monitor.done();
}
return result;
}
/**
* Exclude an object at a given path. This means that the exclusion filter
* for the corresponding <code>IProjectFragment</code> needs to be modified.
*
* First, the fragment root needs to be found. To do so, the new entries are
* and the existing entries are traversed for a match and the entry with the
* path is removed from one of those lists.
*
* Note: the <code>IModelElement</code>'s fragment (if there is one) is not
* allowed to be excluded! However, inclusion (or simply no filter) on the
* parent fragment is allowed.
*
* @param path
* absolute path of an object to be excluded
* @param existingEntries
* a list of existing build path entries
* @param newEntries
* a list of new build path entries
* @param project
* the script project
* @param monitor
* progress monitor, can be <code>null</code>
*/
public static void exclude(IPath path, List existingEntries,
List newEntries, IScriptProject project, IProgressMonitor monitor)
throws ModelException {
if (monitor == null)
monitor = new NullProgressMonitor();
try {
monitor.beginTask(
NewWizardMessages.BuildpathModifier_Monitor_Excluding, 1);
BPListElement elem = null;
BPListElement existingElem = null;
int i = 0;
do {
i++;
IPath rootPath = path.removeLastSegments(i);
if (rootPath.segmentCount() == 0)
return;
elem = getListElement(rootPath, newEntries);
existingElem = getListElement(rootPath, existingEntries);
} while (existingElem == null && elem == null);
if (elem == null) {
elem = existingElem;
}
exclude(path.removeFirstSegments(path.segmentCount() - i)
.toString(), null, elem, project,
new SubProgressMonitor(monitor, 1));
} finally {
monitor.done();
}
}
/**
* Exclude a <code>IModelElement</code>. This means that the exclusion
* filter for the corresponding <code>IProjectFragment</code>s need to be
* modified.
*
* Note: the <code>IModelElement</code>'s fragment (if there is one) is not
* allowed to be excluded! However, inclusion (or simply no filter) on the
* parent fragment is allowed.
*
* @param scriptElement
* the script element to be excluded
* @param entry
* the <code>BPListElement</code> representing the
* <code>IBuildpathEntry</code> of the script
* element's root.
* @param project
* the script project
* @param monitor
* progress monitor, can be <code>null</code>
*
* @return the resulting <code>IResource<code>
* @throws ModelException
*/
public static IResource exclude(IModelElement scriptElement,
BPListElement entry, IScriptProject project,
IProgressMonitor monitor) throws ModelException {
if (monitor == null)
monitor = new NullProgressMonitor();
try {
String name = getName(scriptElement.getPath(), entry.getPath());
return exclude(name, scriptElement.getPath(), entry, project,
new SubProgressMonitor(monitor, 1));
} finally {
monitor.done();
}
}
/**
* Inverse operation to <code>include</code>. The provided
* <code>IModelElement</code> will be removed from the inclusion filters of
* it's root.
*
* Note: the <code>IModelElement</code>'s fragment (if there is one) is not
* allowed to be excluded! However, inclusion (or simply no filter) on the
* parent fragment is allowed.
*
* @param scriptElement
* the script element to be unincluded
* @param entry
* the <code>BPListElement</code> representing the
* <code>IBuildpathEntry</code> of the root.
* @param project
* the script project
* @param monitor
* progress monitor, can be <code>null</code>
* @throws ModelException
*
* @see #include(List, IScriptProject, IProgressMonitor)
*/
private void unInclude(IModelElement scriptElement, BPListElement entry,
IScriptProject project, IProgressMonitor monitor)
throws ModelException {
if (monitor == null)
monitor = new NullProgressMonitor();
try {
monitor.beginTask(
NewWizardMessages.BuildpathModifier_Monitor_RemoveInclusion,
10);
String name = getName(scriptElement.getPath(), entry.getPath());
IPath[] includedPath = (IPath[]) entry
.getAttribute(BPListElement.INCLUSION);
IPath relPath = new Path(completeName(project, name));
IPath[] newIncludedPath = remove(relPath, includedPath,
new SubProgressMonitor(monitor, 3));
entry.setAttribute(BPListElement.INCLUSION, newIncludedPath);
} finally {
monitor.done();
}
}
/**
* Inverse operation to <code>exclude</code>. The resource removed from it's
* fragment roots exlusion filter.
*
* Note: the <code>IModelElement</code>'s fragment (if there is one) is not
* allowed to be excluded! However, inclusion (or simply no filter) on the
* parent fragment is allowed.
*
* @param resource
* the resource to be unexcluded
* @param entry
* the <code>BPListElement</code> representing the
* <code>IBuildpathEntry</code> of the resource's root.
* @param project
* the script project
* @param monitor
* progress monitor, can be <code>null</code>
* @throws ModelException
*
* @see #exclude(List, IScriptProject, IProgressMonitor)
*/
public static void unExclude(IResource resource, BPListElement entry,
IScriptProject project, IProgressMonitor monitor)
throws ModelException {
if (monitor == null)
monitor = new NullProgressMonitor();
try {
monitor.beginTask(
NewWizardMessages.BuildpathModifier_Monitor_RemoveExclusion,
10);
String name = getName(resource.getFullPath(), entry.getPath());
IPath[] excludedPath = (IPath[]) entry
.getAttribute(BPListElement.EXCLUSION);
IPath[] newExcludedPath = remove(
new Path(completeName(project, name)), excludedPath,
new SubProgressMonitor(monitor, 3));
entry.setAttribute(BPListElement.EXCLUSION, newExcludedPath);
} finally {
monitor.done();
}
}
/**
* Resets inclusion and exclusion filters for the given
* <code>IModelElement</code>
*
* @param element
* element to reset it's filters
* @param entry
* the <code>BPListElement</code> to reset its filters
* for
* @param project
* the script project
* @param monitor
* progress monitor, can be <code>null</code>
* @throws ModelException
*/
private void resetFilters(IModelElement element, BPListElement entry,
IScriptProject project, IProgressMonitor monitor)
throws ModelException {
if (monitor == null)
monitor = new NullProgressMonitor();
try {
monitor.beginTask(
NewWizardMessages.BuildpathModifier_Monitor_ResetFilters,
3);
List<IPath> exclusionList = getFoldersOnBP(element.getPath(),
project, new SubProgressMonitor(monitor, 2));
IPath[] exclusions = exclusionList
.toArray(new IPath[exclusionList.size()]);
entry.setAttribute(BPListElement.INCLUSION, new IPath[0]);
entry.setAttribute(BPListElement.EXCLUSION, exclusions);
} finally {
monitor.done();
}
}
/**
* Try to find the corresponding and modified <code>BPListElement</code> for
* the provided <code>BPListElement</code> in the list of elements and
* return it. If no one can be found, the provided
* <code>BPListElement</code> is returned.
*
* @param elements
* a list of <code>BPListElements</code>
* @param cpElement
* the <code>BPListElement</code> to find the
* corresponding entry in the list
* @return the <code>BPListElement</code> found in the list (matching by
* using the path) or the second <code>BPListElement</code>
* parameter itself if there is no match.
* @throws ModelException
*/
public static BPListElement getBuildpathEntry(List elements,
BPListElement cpElement) throws ModelException {
for (int i = 0; i < elements.size(); i++) {
if (((BPListElement) elements.get(i)).getPath()
.equals(cpElement.getPath()))
return (BPListElement) elements.get(i);
}
elements.add(cpElement);
return cpElement;
}
/**
* For a given path, find the corresponding element in the list.
*
* @param path
* the path to found an entry for
* @param elements
* a list of <code>BPListElement</code>s
* @return the mathed <code>BPListElement</code> or <code>null</code> if no
* match could be found
*/
private static BPListElement getListElement(IPath path, List elements) {
for (int i = 0; i < elements.size(); i++) {
BPListElement element = (BPListElement) elements.get(i);
if (element.getEntryKind() == IBuildpathEntry.BPE_SOURCE
&& element.getPath().equals(path)) {
return element;
}
}
return null;
}
/**
* Updates the build path if changes have been applied to a build path
* entry. For example, this can be necessary after having edited some
* filters on a build path entry, which can happen when including or
* excluding an object.
*
* @param newEntries
* a list of <code>BPListElements</code> that should
* be used as build path entries for the project.
* @param project
* the script project
* @param monitor
* progress monitor, can be <code>null</code>
* @throws ModelException
* in case that validation for the new entries
* fails
*/
private void updateBuildpath(List newEntries, IScriptProject project,
IProgressMonitor monitor) throws ModelException {
if (monitor == null)
monitor = new NullProgressMonitor();
try {
IBuildpathEntry[] entries = convert(newEntries);
IModelStatus status = BuildpathEntry.validateBuildpath(project,
entries);
if (!status.isOK())
throw new ModelException(status);
project.setRawBuildpath(entries,
new SubProgressMonitor(monitor, 2));
fireEvent(newEntries);
} finally {
monitor.done();
}
}
public static void commitBuildPath(List newEntries, IScriptProject project,
IProgressMonitor monitor) throws ModelException {
if (monitor == null)
monitor = new NullProgressMonitor();
try {
IBuildpathEntry[] entries = convert(newEntries);
IModelStatus status = BuildpathEntry.validateBuildpath(project,
entries);
if (!status.isOK())
throw new ModelException(status);
project.setRawBuildpath(entries,
new SubProgressMonitor(monitor, 2));
} finally {
monitor.done();
}
}
/**
* For a given list of entries, find out what representation they will have
* in the project and return a list with corresponding elements.
*
* @param entries
* a list of entries to find an appropriate
* representation for. The list can contain elements of
* two types:
* <li><code>IResource</code></li>
* <li><code>IModelElement</code></li>
* @param project
* the script project
* @return a list of elements corresponding to the passed entries.
*/
public static List getCorrespondingElements(List entries,
IScriptProject project) {
List result = new ArrayList();
for (int i = 0; i < entries.size(); i++) {
Object element = entries.get(i);
IPath path;
if (element instanceof IResource)
path = ((IResource) element).getFullPath();
else
path = ((IModelElement) element).getPath();
IResource resource = getResource(path, project);
if (resource != null) {
IModelElement elem = DLTKCore.create(resource);
if (elem != null && project.isOnBuildpath(elem))
result.add(elem);
else
result.add(resource);
}
}
return result;
}
/**
* Returns for the given absolute path the corresponding resource, this is
* either element of type <code>IFile</code> or <code>IFolder</code>.
*
* @param path
* an absolute path to a resource
* @param project
* the script project
* @return the resource matching to the path. Can be either an
* <code>IFile</code> or an <code>IFolder</code>.
*/
private static IResource getResource(IPath path, IScriptProject project) {
return project.getProject().getWorkspace().getRoot().findMember(path);
}
/**
* Find out whether the provided path equals to one in the array.
*
* @param path
* path to find an equivalent for
* @param paths
* set of paths to compare with
* @param monitor
* progress monitor, can be <code>null</code>
* @return <code>true</code> if there is an occurrence, <code>
* false</code> otherwise
*/
private static boolean contains(IPath path, IPath[] paths,
IProgressMonitor monitor) {
if (monitor == null)
monitor = new NullProgressMonitor();
if (path == null)
return false;
try {
monitor.beginTask(
NewWizardMessages.BuildpathModifier_Monitor_ComparePaths,
paths.length);
if (path.getFileExtension() == null)
path = new Path(completeName(path.toString()));
for (int i = 0; i < paths.length; i++) {
if (paths[i].equals(path))
return true;
monitor.worked(1);
}
} finally {
monitor.done();
}
return false;
}
/**
* Add a '/' at the end of the name if it does not end with extension.
*
* XXX this method does nothing, use {@link #completeName(IPath, String)}
*
* @param name
* append '/' at the end if necessary
* @return modified string
*/
@Deprecated
private static String completeName(String name) {
if (DLTKCore.DEBUG) {
System.err.println("Add Buildpath name completion here"); //$NON-NLS-1$
}
// if (!DLTKCore.isScriptLikeFileName(name)) {
// name= name + "/"; //$NON-NLS-1$
// name= name.replace('.', '/');
// return name;
// }
return name;
}
/**
* Add a '/' at the end of the name if it does not end with extension.
*
* XXX this method incorrectly finds from project, use
* {@link #completeName(IPath, String)}
*
* @param project
* - the project containing the resource
* @param name
* - name of the resource append '/' at the end if
* necessary
* @return modified string
*/
@Deprecated
private static String completeName(IScriptProject project, String name) {
// Try to locate the resource in the workspace
IResource resource = ResourcesPlugin.getWorkspace().getRoot()
.findMember(project.getElementName() + "/" + name); //$NON-NLS-1$
// If the resource is a folder and does not end with "/" add it
if (resource != null && resource.getType() == IResource.FOLDER
&& !name.endsWith("/")) { //$NON-NLS-1$
name = name + "/"; //$NON-NLS-1$
}
return name;
}
/**
* Add a '/' at the end of the name if it maps to a folder.
*
* @param fullPath
* - the project containing the resource
* @param name
* - name of the resource append '/' at the end if
* necessary
* @return modified string
*/
private static String completeName(IPath fullPath, String name) {
// Try to locate the resource in the workspace
IResource resource = ResourcesPlugin.getWorkspace().getRoot()
.findMember(fullPath); // $NON-NLS-1$
// If the resource is a folder and does not end with "/" add it
if (resource != null && resource.getType() == IResource.FOLDER
&& !name.endsWith("/")) { //$NON-NLS-1$
name = name + "/"; //$NON-NLS-1$
}
return name;
}
/**
* Removes <code>path</code> out of the set of given <code>
* paths</code>. If the path is not contained, then the initially provided
* array of paths is returned.
*
* Only the first occurrence will be removed.
*
* @param path
* path to be removed
* @param paths
* array of path to apply the removal on
* @param monitor
* progress monitor, can be <code>null</code>
* @return array which does not contain <code>path</code>
*/
private static IPath[] remove(IPath path, IPath[] paths,
IProgressMonitor monitor) {
if (monitor == null)
monitor = new NullProgressMonitor();
try {
monitor.beginTask(
NewWizardMessages.BuildpathModifier_Monitor_RemovePath,
paths.length + 5);
if (!contains(path, paths, new SubProgressMonitor(monitor, 5)))
return paths;
ArrayList newPaths = new ArrayList();
for (int i = 0; i < paths.length; i++) {
monitor.worked(1);
if (!paths[i].equals(path))
newPaths.add(paths[i]);
}
return (IPath[]) newPaths.toArray(new IPath[newPaths.size()]);
} finally {
monitor.done();
}
}
/**
* Find all folders that are on the build path and <code>path</code> is a
* prefix of those folders path entry, that is, all folders which are a
* subfolder of <code>path</code>.
*
* For example, if <code>path</code>=/MyProject/src then all folders having
* a path like /MyProject/src/*, where * can be any valid string are
* returned if they are also on the project's build path.
*
* @param path
* absolute path
* @param project
* the script project
* @param monitor
* progress monitor, can be <code>null</code>
* @return an array of paths which belong to subfolders of <code>path</code>
* and which are on the build path
* @throws ModelException
*/
private List<IPath> getFoldersOnBP(IPath path, IScriptProject project,
IProgressMonitor monitor) throws ModelException {
if (monitor == null)
monitor = new NullProgressMonitor();
List<IPath> srcFolders = new ArrayList<>();
IBuildpathEntry[] cpEntries = project.getRawBuildpath();
for (int i = 0; i < cpEntries.length; i++) {
IPath cpPath = cpEntries[i].getPath();
if (path.isPrefixOf(cpPath)
&& path.segmentCount() + 1 == cpPath.segmentCount())
srcFolders.add(new Path(completeName(cpPath.lastSegment())));
}
return srcFolders;
}
/**
* Returns a string corresponding to the <code>path</code> with the
* <code>rootPath<code>'s number of segments removed
*
* @param path
* path to remove segments
* @param rootPath
* provides the number of segments to be removed
* @return a string corresponding to the mentioned action
*/
private static String getName(IPath path, IPath rootPath) {
return path.removeFirstSegments(rootPath.segmentCount()).toString();
}
/**
* Sets and validates the new entries. Note that the elments of the list
* containing the new entries will be added to the list of existing entries
* (therefore, there is no return list for this method).
*
* @param existingEntries
* a list of existing buildpath entries
* @param newEntries
* a list of entries to be added to the existing
* ones
* @param project
* the script project
* @param monitor
* a progress monitor, can be <code>null</code>
* @throws CoreException
* in case that validation on one of the new
* entries fails
*/
public static void setNewEntry(List existingEntries, List newEntries,
IScriptProject project, IProgressMonitor monitor)
throws CoreException {
try {
monitor.beginTask(
NewWizardMessages.BuildpathModifier_Monitor_SetNewEntry,
existingEntries.size());
for (int i = 0; i < newEntries.size(); i++) {
BPListElement entry = (BPListElement) newEntries.get(i);
validateAndAddEntry(entry, existingEntries, project);
monitor.worked(1);
}
} finally {
monitor.done();
}
}
/**
* Convert a list of <code>BPListElement</code>s to an array of
* <code>IBuildpathEntry</code>.
*
* @param list
* the list to be converted
* @return an array containing build path entries corresponding to the list
*/
private static IBuildpathEntry[] convert(List list) {
IBuildpathEntry[] entries = new IBuildpathEntry[list.size()];
for (int i = 0; i < list.size(); i++) {
BPListElement element = (BPListElement) list.get(i);
entries[i] = element.getBuildpathEntry();
}
return entries;
}
/**
* Validate the new entry in the context of the existing entries.
* Furthermore, check if exclusion filters need to be applied and do so if
* necessary.
*
* If validation was successfull, add the new entry to the list of existing
* entries.
*
* @param entry
* the entry to be validated and added to the
* list of existing entries.
* @param existingEntries
* a list of existing entries representing the
* build path
* @param project
* the script project
* @throws CoreException
* in case that validation fails
*/
private static void validateAndAddEntry(BPListElement entry,
List existingEntries, IScriptProject project) throws CoreException {
IPath path = entry.getPath();
// IPath projPath= project.getProject().getFullPath();
IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
IStatus validate = workspaceRoot.getWorkspace()
.validatePath(path.toString(), IResource.FOLDER);
StatusInfo rootStatus = new StatusInfo();
rootStatus.setOK();
boolean isExternal = isExternalArchiveOrLibrary(entry, project);
if (!isExternal && validate.matches(IStatus.ERROR)
&& !project.getPath().equals(path)) {
rootStatus.setError(Messages.format(
NewWizardMessages.NewSourceFolderWizardPage_error_InvalidRootName,
validate.getMessage()));
throw new CoreException(rootStatus);
} else {
if (!isExternal && !project.getPath().equals(path)) {
IResource res = workspaceRoot.findMember(path);
if (res != null) {
if (res.getType() != IResource.FOLDER
&& res.getType() != IResource.FILE) {
rootStatus.setError(
NewWizardMessages.NewSourceFolderWizardPage_error_NotAFolder);
throw new CoreException(rootStatus);
}
} else {
URI projLocation = project.getProject().getLocationURI();
if (projLocation != null) {
IFileStore store = EFS.getStore(projLocation)
.getChild(path);
if (store.fetchInfo().exists()) {
rootStatus.setError(
NewWizardMessages.NewSourceFolderWizardPage_error_AlreadyExistingDifferentCase);
throw new CoreException(rootStatus);
}
}
}
}
for (int i = 0; i < existingEntries.size(); i++) {
BPListElement curr = (BPListElement) existingEntries.get(i);
if (curr.getEntryKind() == IBuildpathEntry.BPE_SOURCE) {
if (path.equals(curr.getPath())
&& !project.getPath().equals(path)) {
rootStatus.setError(
NewWizardMessages.NewSourceFolderWizardPage_error_AlreadyExisting);
throw new CoreException(rootStatus);
}
}
}
if (!isExternal && !entry.getPath().equals(project.getPath()))
exclude(entry.getPath(), existingEntries, new ArrayList(),
project, null);
insertAtEndOfCategory(entry, existingEntries);
IBuildpathEntry[] entries = convert(existingEntries);
IModelStatus status = BuildpathEntry.validateBuildpath(project,
entries);
if (!status.isOK()) {
rootStatus.setError(status.getMessage());
throw new CoreException(rootStatus);
}
if (isSourceFolder(project) || project.getPath().equals(path)) {
rootStatus.setWarning(
NewWizardMessages.NewSourceFolderWizardPage_warning_ReplaceSF);
return;
}
rootStatus.setOK();
return;
}
}
private static void insertAtEndOfCategory(BPListElement entry,
List existingEntries) {
int length = existingEntries.size();
BPListElement[] elements = (BPListElement[]) existingEntries
.toArray(new BPListElement[length]);
int i = 0;
while (i < length && elements[i].getBuildpathEntry()
.getEntryKind() != entry.getBuildpathEntry().getEntryKind()) {
i++;
}
if (i < length) {
i++;
while (i < length
&& elements[i].getBuildpathEntry().getEntryKind() == entry
.getBuildpathEntry().getEntryKind()) {
i++;
}
existingEntries.add(i, entry);
return;
}
switch (entry.getBuildpathEntry().getEntryKind()) {
case IBuildpathEntry.BPE_SOURCE:
existingEntries.add(0, entry);
break;
case IBuildpathEntry.BPE_CONTAINER:
case IBuildpathEntry.BPE_LIBRARY:
case IBuildpathEntry.BPE_PROJECT:
default:
existingEntries.add(entry);
break;
}
}
private static boolean isExternalArchiveOrLibrary(BPListElement entry,
IScriptProject project) {
if (entry.getEntryKind() == IBuildpathEntry.BPE_LIBRARY
|| entry.getEntryKind() == IBuildpathEntry.BPE_CONTAINER) {
if (entry.getResource() instanceof IFolder) {
return false;
}
return true;
}
return false;
}
/**
* Test if the provided kind is of type
* <code>IBuildpathEntry.BPE_SOURCE</code>
*
* @param entry
* the buildpath entry to be compared with the provided
* type
* @param kind
* the kind to be checked
* @return <code>true</code> if kind equals
* <code>IBuildpathEntry.BPE_SOURCE</code>, <code>false</code>
* otherwise
*/
private static boolean equalEntryKind(IBuildpathEntry entry, int kind) {
return entry.getEntryKind() == kind;
}
/**
* Event fired whenever build pathentries changed. The event parameter
* corresponds to the a <code>List</code> of <code>BPListElement</code>s
*
* @param newEntries
*
* @see #addToBuildpath(List, IScriptProject, OutputFolderQuery,
* IProgressMonitor)
* @see #removeFromBuildpath(IRemoveLinkedFolderQuery, List, IScriptProject,
* IProgressMonitor)
*/
private void fireEvent(List newEntries) {
if (fListener != null)
fListener.buildpathEntryChanged(newEntries);
}
}