/******************************************************************************* * 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; } }