package org.eclipse.jdt.internal.core;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.core.resources.*;
import org.eclipse.core.runtime.*;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.jdt.core.*;
import org.eclipse.jdt.internal.core.util.Messages;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.text.edits.TextEdit;
@SuppressWarnings({"rawtypes", "unchecked"})
public abstract class JavaModelOperation implements IWorkspaceRunnable, IProgressMonitor {
protected interface IPostAction {
String getID();
void run() throws JavaModelException;
}
protected static final int APPEND = 1;
protected static final int REMOVEALL_APPEND = 2;
protected static final int KEEP_EXISTING = 3;
protected static boolean POST_ACTION_VERBOSE;
protected IPostAction[] actions;
protected int actionsStart = 0;
protected int actionsEnd = -1;
protected HashMap attributes;
public static final String HAS_MODIFIED_RESOURCE_ATTR = "hasModifiedResource";
public static final String TRUE = JavaModelManager.TRUE;
protected IJavaElement[] elementsToProcess;
protected IJavaElement[] parentElements;
protected static final IJavaElement[] NO_ELEMENTS= new IJavaElement[] {};
protected IJavaElement[] resultElements= NO_ELEMENTS;
public SubMonitor progressMonitor= SubMonitor.convert(null);
protected boolean isNested = false;
protected boolean force = false;
protected static final ThreadLocal OPERATION_STACKS = new ThreadLocal();
protected JavaModelOperation() {
}
protected JavaModelOperation(IJavaElement[] elements) {
this.elementsToProcess = elements;
}
protected JavaModelOperation(IJavaElement[] elementsToProcess, IJavaElement[] parentElements) {
this.elementsToProcess = elementsToProcess;
this.parentElements= parentElements;
}
protected JavaModelOperation(IJavaElement[] elementsToProcess, IJavaElement[] parentElements, boolean force) {
this.elementsToProcess = elementsToProcess;
this.parentElements= parentElements;
this.force= force;
}
protected JavaModelOperation(IJavaElement[] elements, boolean force) {
this.elementsToProcess = elements;
this.force= force;
}
protected JavaModelOperation(IJavaElement element) {
this.elementsToProcess = new IJavaElement[]{element};
}
protected void addAction(IPostAction action) {
int length = this.actions.length;
if (length == ++this.actionsEnd) {
System.arraycopy(this.actions, 0, this.actions = new IPostAction[length*2], 0, length);
}
this.actions[this.actionsEnd] = action;
}
protected void addDelta(IJavaElementDelta delta) {
JavaModelManager.getJavaModelManager().getDeltaProcessor().registerJavaModelDelta(delta);
}
protected void addReconcileDelta(ICompilationUnit workingCopy, IJavaElementDelta delta) {
Map<ICompilationUnit, IJavaElementDelta> reconcileDeltas = JavaModelManager.getJavaModelManager().getDeltaProcessor().reconcileDeltas;
JavaElementDelta previousDelta = (JavaElementDelta)reconcileDeltas.get(workingCopy);
if (previousDelta != null) {
IJavaElementDelta[] children = delta.getAffectedChildren();
for (int i = 0, length = children.length; i < length; i++) {
JavaElementDelta child = (JavaElementDelta)children[i];
previousDelta.insertDeltaTree(child.getElement(), child);
}
if ((delta.getFlags() & IJavaElementDelta.F_AST_AFFECTED) != 0) {
previousDelta.changedAST(delta.getCompilationUnitAST());
}
} else {
reconcileDeltas.put(workingCopy, delta);
}
}
protected void removeReconcileDelta(ICompilationUnit workingCopy) {
JavaModelManager.getJavaModelManager().getDeltaProcessor().reconcileDeltas.remove(workingCopy);
}
protected void applyTextEdit(ICompilationUnit cu, TextEdit edits) throws JavaModelException {
try {
edits.apply(getDocument(cu));
} catch (BadLocationException e) {
throw new JavaModelException(e, IJavaModelStatusConstants.INVALID_CONTENTS);
}
}
@Override
public void beginTask(String name, int totalWork) {
if (this.progressMonitor != null) {
this.progressMonitor.beginTask(name, totalWork);
}
}
protected boolean canModifyRoots() {
return false;
}
protected void checkCanceled() {
if (isCanceled()) {
throw new OperationCanceledException(Messages.operation_cancelled);
}
}
protected IJavaModelStatus commonVerify() {
if (this.elementsToProcess == null || this.elementsToProcess.length == 0) {
return new JavaModelStatus(IJavaModelStatusConstants.NO_ELEMENTS_TO_PROCESS);
}
for (int i = 0; i < this.elementsToProcess.length; i++) {
if (this.elementsToProcess[i] == null) {
return new JavaModelStatus(IJavaModelStatusConstants.NO_ELEMENTS_TO_PROCESS);
}
}
return JavaModelStatus.VERIFIED_OK;
}
protected void copyResources(IResource[] resources, IPath container) throws JavaModelException {
IProgressMonitor subProgressMonitor = getSubProgressMonitor(resources.length);
IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
try {
for (int i = 0, length = resources.length; i < length; i++) {
IResource resource = resources[i];
IPath destination = container.append(resource.getName());
if (root.findMember(destination) == null) {
resource.copy(destination, false, subProgressMonitor);
}
}
setAttribute(HAS_MODIFIED_RESOURCE_ATTR, TRUE);
} catch (CoreException e) {
throw new JavaModelException(e);
}
}
protected void createFile(IContainer folder, String name, InputStream contents, boolean forceFlag) throws JavaModelException {
IFile file= folder.getFile(new Path(name));
try {
file.create(
contents,
forceFlag ? IResource.FORCE | IResource.KEEP_HISTORY : IResource.KEEP_HISTORY,
getSubProgressMonitor(1));
setAttribute(HAS_MODIFIED_RESOURCE_ATTR, TRUE);
} catch (CoreException e) {
throw new JavaModelException(e);
}
}
protected void createFolder(IContainer parentFolder, String name, boolean forceFlag) throws JavaModelException {
IFolder folder= parentFolder.getFolder(new Path(name));
try {
folder.create(
forceFlag ? IResource.FORCE | IResource.KEEP_HISTORY : IResource.KEEP_HISTORY,
true,
getSubProgressMonitor(1));
setAttribute(HAS_MODIFIED_RESOURCE_ATTR, TRUE);
} catch (CoreException e) {
throw new JavaModelException(e);
}
}
protected void deleteEmptyPackageFragment(
IPackageFragment fragment,
boolean forceFlag,
IResource rootResource)
throws JavaModelException {
IContainer resource = (IContainer) ((JavaElement)fragment).resource();
try {
resource.delete(
forceFlag ? IResource.FORCE | IResource.KEEP_HISTORY : IResource.KEEP_HISTORY,
getSubProgressMonitor(1));
setAttribute(HAS_MODIFIED_RESOURCE_ATTR, TRUE);
while (resource instanceof IFolder) {
resource = resource.getParent();
if (!resource.equals(rootResource) && resource.members().length == 0) {
resource.delete(
forceFlag ? IResource.FORCE | IResource.KEEP_HISTORY : IResource.KEEP_HISTORY,
getSubProgressMonitor(1));
setAttribute(HAS_MODIFIED_RESOURCE_ATTR, TRUE);
} else {
break;
}
}
} catch (CoreException e) {
throw new JavaModelException(e);
}
}
protected void deleteResource(IResource resource,int flags) throws JavaModelException {
try {
resource.delete(flags, getSubProgressMonitor(1));
setAttribute(HAS_MODIFIED_RESOURCE_ATTR, TRUE);
} catch (CoreException e) {
throw new JavaModelException(e);
}
}
protected void deleteResources(IResource[] resources, boolean forceFlag) throws JavaModelException {
if (resources == null || resources.length == 0) return;
IProgressMonitor subProgressMonitor = getSubProgressMonitor(resources.length);
IWorkspace workspace = resources[0].getWorkspace();
try {
workspace.delete(
resources,
forceFlag ? IResource.FORCE | IResource.KEEP_HISTORY : IResource.KEEP_HISTORY,
subProgressMonitor);
setAttribute(HAS_MODIFIED_RESOURCE_ATTR, TRUE);
} catch (CoreException e) {
throw new JavaModelException(e);
}
}
@Override
public void done() {
if (this.progressMonitor != null) {
this.progressMonitor.done();
}
}
protected boolean equalsOneOf(IPath path, IPath[] otherPaths) {
for (int i = 0, length = otherPaths.length; i < length; i++) {
if (path.equals(otherPaths[i])) {
return true;
}
}
return false;
}
public void executeNestedOperation(JavaModelOperation operation, int subWorkAmount) throws JavaModelException {
IJavaModelStatus status= operation.verify();
if (!status.isOK()) {
throw new JavaModelException(status);
}
IProgressMonitor subProgressMonitor = getSubProgressMonitor(subWorkAmount);
try {
operation.setNested(true);
operation.run(subProgressMonitor);
} catch (CoreException ce) {
if (ce instanceof JavaModelException) {
throw (JavaModelException)ce;
} else {
if (ce.getStatus().getCode() == IResourceStatus.OPERATION_FAILED) {
Throwable e = ce.getStatus().getException();
if (e instanceof JavaModelException) {
throw (JavaModelException) e;
}
}
throw new JavaModelException(ce);
}
}
}
protected abstract void executeOperation() throws JavaModelException;
protected static Object getAttribute(Object key) {
ArrayList stack = getCurrentOperationStack();
if (stack.size() == 0) return null;
JavaModelOperation topLevelOp = (JavaModelOperation)stack.get(0);
if (topLevelOp.attributes == null) {
return null;
} else {
return topLevelOp.attributes.get(key);
}
}
protected ICompilationUnit getCompilationUnitFor(IJavaElement element) {
return ((JavaElement)element).getCompilationUnit();
}
protected static ArrayList getCurrentOperationStack() {
ArrayList stack = (ArrayList)OPERATION_STACKS.get();
if (stack == null) {
stack = new ArrayList();
OPERATION_STACKS.set(stack);
}
return stack;
}
protected IDocument getDocument(ICompilationUnit cu) throws JavaModelException {
IBuffer buffer = cu.getBuffer();
if (buffer instanceof IDocument)
return (IDocument) buffer;
return new DocumentAdapter(buffer);
}
protected IJavaElement getElementToProcess() {
if (this.elementsToProcess == null || this.elementsToProcess.length == 0) {
return null;
}
return this.elementsToProcess[0];
}
public IJavaModel getJavaModel() {
return JavaModelManager.getJavaModelManager().getJavaModel();
}
protected IPath[] getNestedFolders(IPackageFragmentRoot root) throws JavaModelException {
IPath rootPath = root.getPath();
IClasspathEntry[] classpath = root.getJavaProject().getRawClasspath();
int length = classpath.length;
IPath[] result = new IPath[length];
int index = 0;
for (int i = 0; i < length; i++) {
IPath path = classpath[i].getPath();
if (rootPath.isPrefixOf(path) && !rootPath.equals(path)) {
result[index++] = path;
}
}
if (index < length) {
System.arraycopy(result, 0, result = new IPath[index], 0, index);
}
return result;
}
protected IJavaElement getParentElement() {
if (this.parentElements == null || this.parentElements.length == 0) {
return null;
}
return this.parentElements[0];
}
protected IJavaElement[] getParentElements() {
return this.parentElements;
}
public IJavaElement[] getResultElements() {
return this.resultElements;
}
protected ISchedulingRule getSchedulingRule() {
return ResourcesPlugin.getWorkspace().getRoot();
}
protected IProgressMonitor getSubProgressMonitor(int workAmount) {
return this.progressMonitor.split(workAmount);
}
public boolean hasModifiedResource() {
return !isReadOnly() && getAttribute(HAS_MODIFIED_RESOURCE_ATTR) == TRUE;
}
@Override
public void internalWorked(double work) {
if (this.progressMonitor != null) {
this.progressMonitor.internalWorked(work);
}
}
@Override
public boolean isCanceled() {
if (this.progressMonitor != null) {
return this.progressMonitor.isCanceled();
}
return false;
}
public boolean isReadOnly() {
return false;
}
protected boolean isTopLevelOperation() {
ArrayList stack;
return
(stack = getCurrentOperationStack()).size() > 0
&& stack.get(0) == this;
}
protected int firstActionWithID(String id, int start) {
for (int i = start; i <= this.actionsEnd; i++) {
if (this.actions[i].getID().equals(id)) {
return i;
}
}
return -1;
}
protected void moveResources(IResource[] resources, IPath container) throws JavaModelException {
SubMonitor subProgressMonitor = this.progressMonitor.newChild(resources.length);
IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
try {
for (int i = 0, length = resources.length; i < length; i++) {
IResource resource = resources[i];
IPath destination = container.append(resource.getName());
if (root.findMember(destination) == null) {
resource.move(destination, false, subProgressMonitor.split(1));
}
}
setAttribute(HAS_MODIFIED_RESOURCE_ATTR, TRUE);
} catch (CoreException e) {
throw new JavaModelException(e);
}
}
public JavaElementDelta newJavaElementDelta() {
return new JavaElementDelta(getJavaModel());
}
protected JavaModelOperation popOperation() {
ArrayList stack = getCurrentOperationStack();
int size = stack.size();
if (size > 0) {
if (size == 1) {
OPERATION_STACKS.set(null);
}
return (JavaModelOperation)stack.remove(size-1);
} else {
return null;
}
}
protected void postAction(IPostAction action, int insertionMode) {
if (POST_ACTION_VERBOSE) {
System.out.print("(" + Thread.currentThread() + ") [JavaModelOperation.postAction(IPostAction, int)] Posting action " + action.getID());
switch(insertionMode) {
case REMOVEALL_APPEND:
System.out.println(" (REMOVEALL_APPEND)");
break;
case KEEP_EXISTING:
System.out.println(" (KEEP_EXISTING)");
break;
case APPEND:
System.out.println(" (APPEND)");
break;
}
}
JavaModelOperation topLevelOp = (JavaModelOperation)getCurrentOperationStack().get(0);
IPostAction[] postActions = topLevelOp.actions;
if (postActions == null) {
topLevelOp.actions = postActions = new IPostAction[1];
postActions[0] = action;
topLevelOp.actionsEnd = 0;
} else {
String id = action.getID();
switch (insertionMode) {
case REMOVEALL_APPEND :
int index = this.actionsStart-1;
while ((index = topLevelOp.firstActionWithID(id, index+1)) >= 0) {
System.arraycopy(postActions, index+1, postActions, index, topLevelOp.actionsEnd - index);
postActions[topLevelOp.actionsEnd--] = null;
}
topLevelOp.addAction(action);
break;
case KEEP_EXISTING:
if (topLevelOp.firstActionWithID(id, 0) < 0) {
topLevelOp.addAction(action);
}
break;
case APPEND:
topLevelOp.addAction(action);
break;
}
}
}
protected boolean prefixesOneOf(IPath path, IPath[] otherPaths) {
for (int i = 0, length = otherPaths.length; i < length; i++) {
if (path.isPrefixOf(otherPaths[i])) {
return true;
}
}
return false;
}
protected void pushOperation(JavaModelOperation operation) {
getCurrentOperationStack().add(operation);
}
protected void removeAllPostAction(String actionID) {
if (POST_ACTION_VERBOSE) {
System.out.println("(" + Thread.currentThread() + ") [JavaModelOperation.removeAllPostAction(String)] Removing actions " + actionID);
}
JavaModelOperation topLevelOp = (JavaModelOperation)getCurrentOperationStack().get(0);
IPostAction[] postActions = topLevelOp.actions;
if (postActions == null) return;
int index = this.actionsStart-1;
while ((index = topLevelOp.firstActionWithID(actionID, index+1)) >= 0) {
System.arraycopy(postActions, index+1, postActions, index, topLevelOp.actionsEnd - index);
postActions[topLevelOp.actionsEnd--] = null;
}
}
@Override
public void run(IProgressMonitor monitor) throws CoreException {
SubMonitor oldMonitor = this.progressMonitor;
try {
JavaModelManager manager = JavaModelManager.getJavaModelManager();
DeltaProcessor deltaProcessor = manager.getDeltaProcessor();
int previousDeltaCount = deltaProcessor.javaModelDeltas.size();
try {
this.progressMonitor = SubMonitor.convert(monitor);
pushOperation(this);
try {
if (canModifyRoots()) {
JavaModelManager.getDeltaState().initializeRoots(false);
}
executeOperation();
} finally {
if (isTopLevelOperation()) {
runPostActions();
}
}
} finally {
try {
deltaProcessor = manager.getDeltaProcessor();
for (int i = previousDeltaCount, size = deltaProcessor.javaModelDeltas.size(); i < size; i++) {
deltaProcessor.updateJavaModel(deltaProcessor.javaModelDeltas.get(i));
}
for (int i = 0, length = this.resultElements.length; i < length; i++) {
IJavaElement element = this.resultElements[i];
Openable openable = (Openable) element.getOpenable();
if (!(openable instanceof CompilationUnit) || !((CompilationUnit) openable).isWorkingCopy()) {
((JavaElement) openable.getParent()).close();
}
switch (element.getElementType()) {
case IJavaElement.PACKAGE_FRAGMENT_ROOT:
case IJavaElement.PACKAGE_FRAGMENT:
deltaProcessor.projectCachesToReset.add(element.getJavaProject());
break;
}
}
deltaProcessor.resetProjectCaches();
if (isTopLevelOperation()) {
if ((deltaProcessor.javaModelDeltas.size() > previousDeltaCount || !deltaProcessor.reconcileDeltas.isEmpty())
&& !hasModifiedResource()) {
deltaProcessor.fire(null, DeltaProcessor.DEFAULT_CHANGE_EVENT);
}
}
} finally {
popOperation();
}
}
} finally {
if (monitor != null) {
monitor.done();
}
this.progressMonitor = oldMonitor;
}
}
public void runOperation(IProgressMonitor monitor) throws JavaModelException {
IJavaModelStatus status= verify();
if (!status.isOK()) {
throw new JavaModelException(status);
}
try {
if (isReadOnly()) {
run(monitor);
} else {
ResourcesPlugin.getWorkspace().run(this, getSchedulingRule(), IWorkspace.AVOID_UPDATE, monitor);
}
} catch (CoreException ce) {
if (ce instanceof JavaModelException) {
throw (JavaModelException)ce;
} else {
if (ce.getStatus().getCode() == IResourceStatus.OPERATION_FAILED) {
Throwable e= ce.getStatus().getException();
if (e instanceof JavaModelException) {
throw (JavaModelException) e;
}
}
throw new JavaModelException(ce);
}
}
}
protected void runPostActions() throws JavaModelException {
while (this.actionsStart <= this.actionsEnd) {
IPostAction postAction = this.actions[this.actionsStart++];
if (POST_ACTION_VERBOSE) {
System.out.println("(" + Thread.currentThread() + ") [JavaModelOperation.runPostActions()] Running action " + postAction.getID());
}
postAction.run();
}
}
protected static void setAttribute(Object key, Object attribute) {
ArrayList operationStack = getCurrentOperationStack();
if (operationStack.size() == 0)
return;
JavaModelOperation topLevelOp = (JavaModelOperation) operationStack.get(0);
if (topLevelOp.attributes == null) {
topLevelOp.attributes = new HashMap();
}
topLevelOp.attributes.put(key, attribute);
}
@Override
public void setCanceled(boolean b) {
if (this.progressMonitor != null) {
this.progressMonitor.setCanceled(b);
}
}
protected void setNested(boolean nested) {
this.isNested = nested;
}
@Override
public void setTaskName(String name) {
if (this.progressMonitor != null) {
this.progressMonitor.setTaskName(name);
}
}
@Override
public void subTask(String name) {
if (this.progressMonitor != null) {
this.progressMonitor.subTask(name);
}
}
protected IJavaModelStatus verify() {
return commonVerify();
}
@Override
public void worked(int work) {
if (this.progressMonitor != null) {
this.progressMonitor.worked(work);
checkCanceled();
}
}
}