/*******************************************************************************
* Copyright (c) 2012-2015 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.core;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaModelStatus;
import org.eclipse.jdt.core.IJavaModelStatusConstants;
import org.eclipse.jdt.core.IProblemRequestor;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.WorkingCopyOwner;
import org.eclipse.jdt.core.compiler.CategorizedProblem;
import org.eclipse.jdt.core.dom.CheAST;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.eclipse.jdt.internal.core.util.Messages;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
* Reconcile a working copy and signal the changes through a delta.
* <p/>
* High level summmary of what a reconcile does:
* <ul>
* <li>populates the model with the new working copy contents</li>
* <li>fires a fine grained delta (flag F_FINE_GRAINED) describing the difference between the previous content
* and the new content (which method was added/removed, which field was changed, etc.)</li>
* <li>computes problems and reports them to the IProblemRequestor (begingReporting(), n x acceptProblem(...), endReporting()) iff
* (working copy is not consistent with its buffer || forceProblemDetection is set)
* && problem requestor is active
* </li>
* <li>produces a DOM AST (either JLS_2, JLS_3 or NO_AST) that is resolved if flag is set</li>
* <li>notifies compilation participants of the reconcile allowing them to participate in this operation and report problems</li>
* </ul>
*/
@SuppressWarnings({"rawtypes"})
public class ReconcileWorkingCopyOperation extends JavaModelOperation {
public static boolean PERF = false;
public int astLevel;
public boolean resolveBindings;
public HashMap problems;
public int reconcileFlags;
public org.eclipse.jdt.core.dom.CompilationUnit ast;
public JavaElementDeltaBuilder deltaBuilder;
public boolean requestorIsActive;
WorkingCopyOwner workingCopyOwner;
public ReconcileWorkingCopyOperation(IJavaElement workingCopy, int astLevel, int reconcileFlags, WorkingCopyOwner workingCopyOwner
) {
super(new IJavaElement[]{workingCopy});
this.astLevel = astLevel;
this.reconcileFlags = reconcileFlags;
this.workingCopyOwner = workingCopyOwner;
}
/**
* @throws org.eclipse.jdt.core.JavaModelException
* if setting the source
* of the original compilation unit fails
*/
protected void executeOperation() throws JavaModelException {
checkCanceled();
try {
beginTask(Messages.element_reconciling, 2);
CompilationUnit workingCopy = getWorkingCopy();
boolean wasConsistent = workingCopy.isConsistent();
// check is problem requestor is active
IProblemRequestor problemRequestor = workingCopy.getPerWorkingCopyInfo();
if (problemRequestor != null)
problemRequestor = ((JavaModelManager.PerWorkingCopyInfo)problemRequestor).getProblemRequestor();
boolean defaultRequestorIsActive = problemRequestor != null && problemRequestor.isActive();
IProblemRequestor ownerProblemRequestor = this.workingCopyOwner.getProblemRequestor(workingCopy);
boolean ownerRequestorIsActive =
ownerProblemRequestor != null && ownerProblemRequestor != problemRequestor && ownerProblemRequestor.isActive();
this.requestorIsActive = defaultRequestorIsActive || ownerRequestorIsActive;
// create the delta builder (this remembers the current content of the cu)
this.deltaBuilder = new JavaElementDeltaBuilder(workingCopy);
// make working copy consistent if needed and compute AST if needed
makeConsistent(workingCopy);
// notify reconcile participants only if working copy was not consistent or if forcing problem detection
// (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=177319)
if (!wasConsistent || ((this.reconcileFlags & ICompilationUnit.FORCE_PROBLEM_DETECTION) != 0)) {
notifyParticipants(workingCopy);
// recreate ast if one participant reset it
if (this.ast == null)
makeConsistent(workingCopy);
}
// report problems
if (this.problems != null && (((this.reconcileFlags & ICompilationUnit.FORCE_PROBLEM_DETECTION) != 0) || !wasConsistent)) {
if (defaultRequestorIsActive) {
reportProblems(workingCopy, problemRequestor);
}
if (ownerRequestorIsActive) {
reportProblems(workingCopy, ownerProblemRequestor);
}
}
// report delta
JavaElementDelta delta = this.deltaBuilder.delta;
if (delta != null) {
addReconcileDelta(workingCopy, delta);
}
} finally {
done();
}
}
/**
* Report working copy problems to a given requestor.
*
* @param workingCopy
* @param problemRequestor
*/
private void reportProblems(CompilationUnit workingCopy, IProblemRequestor problemRequestor) {
try {
problemRequestor.beginReporting();
for (Iterator iteraror = this.problems.values().iterator(); iteraror.hasNext(); ) {
CategorizedProblem[] categorizedProblems = (CategorizedProblem[])iteraror.next();
if (categorizedProblems == null) continue;
for (int i = 0, length = categorizedProblems.length; i < length; i++) {
CategorizedProblem problem = categorizedProblems[i];
if (JavaModelManager.VERBOSE) {
System.out.println("PROBLEM FOUND while reconciling : " + problem.getMessage());//$NON-NLS-1$
}
if (this.progressMonitor != null && this.progressMonitor.isCanceled()) break;
problemRequestor.acceptProblem(problem);
}
}
} finally {
problemRequestor.endReporting();
}
}
/**
* Returns the working copy this operation is working on.
*/
protected CompilationUnit getWorkingCopy() {
return (CompilationUnit)getElementToProcess();
}
/* (non-Javadoc)
* @see org.eclipse.jdt.internal.core.JavaModelOperation#isReadOnly()
*/
public boolean isReadOnly() {
return true;
}
/*
* Makes the given working copy consistent, computes the delta and computes an AST if needed.
* Returns the AST.
*/
public org.eclipse.jdt.core.dom.CompilationUnit makeConsistent(CompilationUnit workingCopy) throws JavaModelException {
if (!workingCopy.isConsistent()) {
// make working copy consistent
if (this.problems == null) this.problems = new HashMap();
this.resolveBindings = this.requestorIsActive;
this.ast = workingCopy
.makeConsistent(this.astLevel, this.resolveBindings, this.reconcileFlags, this.problems, this.progressMonitor);
this.deltaBuilder.buildDeltas();
if (this.ast != null && this.deltaBuilder.delta != null)
this.deltaBuilder.delta.changedAST(this.ast);
return this.ast;
}
if (this.ast != null)
return this.ast; // no need to recompute AST if known already
CompilationUnitDeclaration unit = null;
try {
JavaModelManager.getJavaModelManager().abortOnMissingSource.set(Boolean.TRUE);
CompilationUnit source = workingCopy.cloneCachingContents();
// find problems if needed
if (JavaProject.hasJavaNature(workingCopy.getJavaProject().getProject())
&& (this.reconcileFlags & ICompilationUnit.FORCE_PROBLEM_DETECTION) != 0) {
this.resolveBindings = this.requestorIsActive;
if (this.problems == null)
this.problems = new HashMap();
unit =
CompilationUnitProblemFinder.process(
source,
this.workingCopyOwner,
this.problems,
this.astLevel != ICompilationUnit.NO_AST/*creating AST if level is not NO_AST */,
this.reconcileFlags,
this.progressMonitor);
if (this.progressMonitor != null) this.progressMonitor.worked(1);
}
// create AST if needed
if (this.astLevel != ICompilationUnit.NO_AST
&& unit !=
null/*unit is null if working copy is consistent && (problem detection not forced || non-Java project) -> don't create
AST as per API*/) {
Map options = workingCopy.getJavaProject().getOptions(true);
// convert AST
this.ast =
CheAST.convertCompilationUnit(
this.astLevel,
unit,
options,
this.resolveBindings,
source,
this.reconcileFlags,
this.progressMonitor);
if (this.ast != null) {
if (this.deltaBuilder.delta == null) {
this.deltaBuilder.delta = new JavaElementDelta(workingCopy);
}
this.deltaBuilder.delta.changedAST(this.ast);
}
if (this.progressMonitor != null) this.progressMonitor.worked(1);
}
} catch (JavaModelException e) {
if (JavaProject.hasJavaNature(workingCopy.getJavaProject().getProject()))
throw e;
// else JavaProject has lost its nature (or most likely was closed/deleted) while reconciling -> ignore
// (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=100919)
} finally {
JavaModelManager.getJavaModelManager().abortOnMissingSource.set(null);
if (unit != null) {
unit.cleanUp();
}
}
return this.ast;
}
private void notifyParticipants(final CompilationUnit workingCopy) {
// IJavaProject javaProject = getWorkingCopy().getJavaProject();
// CompilationParticipant[] participants = manager.compilationParticipants.getCompilationParticipants
// (javaProject);
// if (participants == null) return;
//
// final ReconcileContext context = new ReconcileContext(this, workingCopy);
// for (int i = 0, length = participants.length; i < length; i++) {
// final CompilationParticipant participant = participants[i];
// SafeRunner.run(new ISafeRunnable() {
// public void handleException(Throwable exception) {
// if (exception instanceof Error) {
// throw (Error)exception; // errors are not supposed to be caught
// } else if (exception instanceof OperationCanceledException)
// throw (OperationCanceledException)exception;
// else if (exception instanceof UnsupportedOperationException) {
// // might want to disable participant as it tried to modify the buffer of the working copy being reconciled
// Util.log(exception,
// "Reconcile participant attempted to modify the buffer of the working copy being reconciled"); //$NON-NLS-1$
// } else
// Util.log(exception, "Exception occurred in reconcile participant"); //$NON-NLS-1$
// }
//
// public void run() throws Exception {
// participant.reconcile(context);
// }
// });
// }
}
protected IJavaModelStatus verify() {
IJavaModelStatus status = super.verify();
if (!status.isOK()) {
return status;
}
CompilationUnit workingCopy = getWorkingCopy();
if (!workingCopy.isWorkingCopy()) {
return new JavaModelStatus(IJavaModelStatusConstants.ELEMENT_DOES_NOT_EXIST, workingCopy); //was destroyed
}
return status;
}
}