blob: bc32e88f0d5863f1977f3c3e6ef0f11295c6b301 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2016 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.core;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceProxy;
import org.eclipse.core.resources.IResourceProxyVisitor;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.AssertionFailedException;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.dltk.core.DLTKCore;
import org.eclipse.dltk.core.DLTKLanguageManager;
import org.eclipse.dltk.core.IBuildpathEntry;
import org.eclipse.dltk.core.IModelStatus;
import org.eclipse.dltk.core.IModelStatusConstants;
import org.eclipse.dltk.core.IProjectFragment;
import org.eclipse.dltk.core.IScriptProject;
import org.eclipse.dltk.core.ModelException;
import org.eclipse.dltk.internal.core.util.Messages;
public class CopyProjectFragmentOperation extends ModelOperation {
IPath destination;
int updateResourceFlags;
int updateModelFlags;
IBuildpathEntry sibling;
public CopyProjectFragmentOperation(IProjectFragment root,
IPath destination, int updateResourceFlags, int updateModelFlags,
IBuildpathEntry sibling) {
super(root);
this.destination = destination;
this.updateResourceFlags = updateResourceFlags;
this.updateModelFlags = updateModelFlags;
this.sibling = sibling;
}
@Override
protected void executeOperation() throws ModelException {
IProjectFragment root = (IProjectFragment) this.getElementToProcess();
IBuildpathEntry rootEntry = root.getRawBuildpathEntry();
IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
// copy resource
if (!root.isExternal()
&& (this.updateModelFlags & IProjectFragment.NO_RESOURCE_MODIFICATION) == 0) {
copyResource(root, rootEntry, workspaceRoot);
}
// update buildpath if needed
if ((this.updateModelFlags & IProjectFragment.DESTINATION_PROJECT_BUILDPATH) != 0) {
addEntryToBuildpath(rootEntry, workspaceRoot);
}
}
protected void copyResource(IProjectFragment root,
IBuildpathEntry rootEntry, final IWorkspaceRoot workspaceRoot)
throws ModelException {
final char[][] exclusionPatterns = ((BuildpathEntry) rootEntry)
.fullExclusionPatternChars();
IResource rootResource = root.getResource();
if (root.getKind() == IProjectFragment.K_BINARY
|| exclusionPatterns == null) {
try {
IResource destRes;
if ((this.updateModelFlags & IProjectFragment.REPLACE) != 0) {
if (rootEntry.getPath().equals(this.destination))
return;
if ((destRes = workspaceRoot.findMember(this.destination)) != null) {
destRes.delete(this.updateResourceFlags,
progressMonitor);
}
}
rootResource.copy(this.destination, this.updateResourceFlags,
progressMonitor);
} catch (CoreException e) {
throw new ModelException(e);
}
} else {
final int sourceSegmentCount = rootEntry.getPath().segmentCount();
final IFolder destFolder = workspaceRoot
.getFolder(this.destination);
final IPath[] nestedFolders = getNestedFolders(root);
IResourceProxyVisitor visitor = new IResourceProxyVisitor() {
@Override
public boolean visit(IResourceProxy proxy) throws CoreException {
if (proxy.getType() == IResource.FOLDER) {
IPath path = proxy.requestFullPath();
if (prefixesOneOf(path, nestedFolders)) {
if (equalsOneOf(path, nestedFolders)) {
// nested source folder
return false;
} else {
// folder containing nested source folder
IFolder folder = destFolder
.getFolder(path
.removeFirstSegments(sourceSegmentCount));
if ((updateModelFlags & IProjectFragment.REPLACE) != 0
&& folder.exists()) {
return true;
}
folder.create(updateResourceFlags, true,
progressMonitor);
return true;
}
} else {
// subtree doesn't contain any nested source folders
IPath destPath = destination.append(path
.removeFirstSegments(sourceSegmentCount));
IResource destRes;
if ((updateModelFlags & IProjectFragment.REPLACE) != 0
&& (destRes = workspaceRoot
.findMember(destPath)) != null) {
destRes.delete(updateResourceFlags,
progressMonitor);
}
proxy.requestResource().copy(destPath,
updateResourceFlags, progressMonitor);
return false;
}
} else {
IPath path = proxy.requestFullPath();
IPath destPath = destination.append(path
.removeFirstSegments(sourceSegmentCount));
IResource destRes;
if ((updateModelFlags & IProjectFragment.REPLACE) != 0
&& (destRes = workspaceRoot
.findMember(destPath)) != null) {
destRes
.delete(updateResourceFlags,
progressMonitor);
}
proxy.requestResource().copy(destPath,
updateResourceFlags, progressMonitor);
return false;
}
}
};
try {
rootResource.accept(visitor, IResource.NONE);
} catch (CoreException e) {
throw new ModelException(e);
}
}
this.setAttribute(HAS_MODIFIED_RESOURCE_ATTR, TRUE);
}
protected void addEntryToBuildpath(IBuildpathEntry rootEntry,
IWorkspaceRoot workspaceRoot) throws ModelException {
IProject destProject = workspaceRoot.getProject(this.destination
.segment(0));
IScriptProject jProject = DLTKCore.create(destProject);
IBuildpathEntry[] buildpath = jProject.getRawBuildpath();
int length = buildpath.length;
IBuildpathEntry[] newBuildpath;
// case of existing entry and REPLACE was specified
if ((this.updateModelFlags & IProjectFragment.REPLACE) != 0) {
// find existing entry
for (int i = 0; i < length; i++) {
if (this.destination.equals(buildpath[i].getPath())) {
newBuildpath = new IBuildpathEntry[length];
System.arraycopy(buildpath, 0, newBuildpath, 0, length);
newBuildpath[i] = copy(rootEntry);
jProject.setRawBuildpath(newBuildpath, progressMonitor);
return;
}
}
}
// other cases
int position;
if (this.sibling == null) {
// insert at the end
position = length;
} else {
// insert before sibling
position = -1;
for (int i = 0; i < length; i++) {
if (this.sibling.equals(buildpath[i])) {
position = i;
break;
}
}
}
if (position == -1) {
throw new ModelException(new ModelStatus(
IModelStatusConstants.INVALID_SIBLING, this.sibling
.toString()));
}
newBuildpath = new IBuildpathEntry[length + 1];
if (position != 0) {
System.arraycopy(buildpath, 0, newBuildpath, 0, position);
}
if (position != length) {
System.arraycopy(buildpath, position, newBuildpath, position + 1,
length - position);
}
IBuildpathEntry newEntry = copy(rootEntry);
newBuildpath[position] = newEntry;
jProject.setRawBuildpath(newBuildpath, progressMonitor);
}
/*
* Copies the given buildpath entry replacing its path with the destination
* path if it is a source folder or a library.
*/
protected IBuildpathEntry copy(IBuildpathEntry entry) throws ModelException {
switch (entry.getEntryKind()) {
case IBuildpathEntry.BPE_CONTAINER:
return DLTKCore.newContainerEntry(entry.getPath(), entry
.getAccessRules(), entry.getExtraAttributes(), entry
.isExported());
case IBuildpathEntry.BPE_LIBRARY:
try {
return DLTKCore.newLibraryEntry(this.destination, entry
.getAccessRules(), entry.getExtraAttributes(), entry
.isExported(), entry.isExternal());
} catch (AssertionFailedException e) {
IModelStatus status = new ModelStatus(
IModelStatusConstants.INVALID_PATH, e.getMessage());
throw new ModelException(status);
}
case IBuildpathEntry.BPE_PROJECT:
return DLTKCore.newProjectEntry(entry.getPath(), entry
.getAccessRules(), entry.combineAccessRules(), entry
.getExtraAttributes(), entry.isExported());
case IBuildpathEntry.BPE_SOURCE:
return DLTKCore.newSourceEntry(this.destination, entry
.getInclusionPatterns(), entry.getExclusionPatterns(),
entry.getExtraAttributes());
default:
throw new ModelException(new ModelStatus(
IModelStatusConstants.ELEMENT_DOES_NOT_EXIST, this
.getElementToProcess()));
}
}
@Override
public IModelStatus verify() {
IModelStatus status = super.verify();
if (!status.isOK()) {
return status;
}
IProjectFragment root = (IProjectFragment) getElementToProcess();
if (root == null || !root.exists()) {
return new ModelStatus(
IModelStatusConstants.ELEMENT_DOES_NOT_EXIST, root);
}
IResource resource = root.getResource();
if (resource instanceof IFolder) {
if (resource.isLinked()) {
return new ModelStatus(IModelStatusConstants.INVALID_RESOURCE,
root);
}
}
if ((this.updateModelFlags & IProjectFragment.DESTINATION_PROJECT_BUILDPATH) != 0) {
String destProjectName = this.destination.segment(0);
IProject project = ResourcesPlugin.getWorkspace().getRoot()
.getProject(destProjectName);
if (DLTKLanguageManager.hasScriptNature(project)) {
try {
IScriptProject destProject = DLTKCore.create(project);
IBuildpathEntry[] destBuildpath = destProject
.getRawBuildpath();
boolean foundSibling = false;
boolean foundExistingEntry = false;
for (int i = 0, length = destBuildpath.length; i < length; i++) {
IBuildpathEntry entry = destBuildpath[i];
if (entry.equals(this.sibling)) {
foundSibling = true;
break;
}
if (entry.getPath().equals(this.destination)) {
foundExistingEntry = true;
}
}
if (this.sibling != null && !foundSibling) {
return new ModelStatus(
IModelStatusConstants.INVALID_SIBLING,
this.sibling.toString());
}
if (foundExistingEntry
&& (this.updateModelFlags & IProjectFragment.REPLACE) == 0) {
return new ModelStatus(
IModelStatusConstants.NAME_COLLISION, Messages
.bind(Messages.status_nameCollision,
new String[] { this.destination
.toString() }));
}
} catch (ModelException e) {
return e.getModelStatus();
}
}
}
return ModelStatus.VERIFIED_OK;
}
}