/*******************************************************************************
* Copyright (c) 2000, 2004 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.rubypeople.rdt.internal.core;
import java.util.HashMap;
import java.util.Iterator;
import org.eclipse.core.runtime.ISafeRunnable;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.SafeRunner;
import org.jruby.ast.RootNode;
import org.rubypeople.rdt.core.IProblemRequestor;
import org.rubypeople.rdt.core.IRubyElement;
import org.rubypeople.rdt.core.IRubyModelStatus;
import org.rubypeople.rdt.core.IRubyModelStatusConstants;
import org.rubypeople.rdt.core.IRubyProject;
import org.rubypeople.rdt.core.RubyModelException;
import org.rubypeople.rdt.core.WorkingCopyOwner;
import org.rubypeople.rdt.core.compiler.CategorizedProblem;
import org.rubypeople.rdt.core.compiler.CompilationParticipant;
import org.rubypeople.rdt.core.compiler.ReconcileContext;
import org.rubypeople.rdt.internal.core.parser.MarkerUtility;
import org.rubypeople.rdt.internal.core.util.Messages;
import org.rubypeople.rdt.internal.core.util.Util;
/**
* Reconcile a working copy and signal the changes through a delta.
*/
public class ReconcileWorkingCopyOperation extends RubyModelOperation {
public static boolean PERF = false;
boolean createAST;
boolean forceProblemDetection;
WorkingCopyOwner workingCopyOwner;
public RootNode ast;
public RubyElementDeltaBuilder deltaBuilder;
public HashMap problems;
public ReconcileWorkingCopyOperation(IRubyElement workingCopy, boolean forceProblemDetection, WorkingCopyOwner workingCopyOwner) {
super(new IRubyElement[] { workingCopy });
this.forceProblemDetection = forceProblemDetection;
this.workingCopyOwner = workingCopyOwner;
}
/**
* @exception RubyModelException
* if setting the source of the original compilation unit
* fails
*/
protected void executeOperation() throws RubyModelException {
if (this.progressMonitor != null) {
if (this.progressMonitor.isCanceled())
throw new OperationCanceledException();
this.progressMonitor.beginTask(Messages.element_reconciling, 2);
}
RubyScript workingCopy = getWorkingCopy();
boolean wasConsistent = workingCopy.isConsistent();
IProblemRequestor problemRequestor = workingCopy.getPerWorkingCopyInfo();
// create the delta builder (this remembers the current content of the
// cu)
this.deltaBuilder = new RubyElementDeltaBuilder(workingCopy);
// make working copy consistent if needed and compute AST if needed
makeConsistent(workingCopy, problemRequestor);
// notify reconcile participants
notifyParticipants(workingCopy);
// recreate ast if needed
// if (this.ast == null)
// makeConsistent(workingCopy, problemRequestor);
// report problems
if (this.problems != null && (this.forceProblemDetection || !wasConsistent)) {
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 (RubyModelManager.VERBOSE) {
System.out.println("PROBLEM FOUND while reconciling : " + problem.getMessage());//$NON-NLS-1$
}
if (this.progressMonitor != null && this.progressMonitor.isCanceled())
break;
if (!MarkerUtility.ignoring(workingCopy.getResource(), problem.getID(), problem.getSourceStart(), problem.getSourceEnd()))
problemRequestor.acceptProblem(problem);
}
}
} finally {
problemRequestor.endReporting();
}
}
// report delta
try {
RubyElementDelta delta = this.deltaBuilder.delta;
if (delta != null) {
addReconcileDelta(workingCopy, delta);
}
} finally {
if (this.progressMonitor != null)
this.progressMonitor.done();
}
}
/*
* Makes the given working copy consistent, computes the delta and computes
* an AST if needed. Returns the AST.
*/
public RootNode makeConsistent(RubyScript workingCopy, IProblemRequestor problemRequestor) throws RubyModelException {
if (!workingCopy.isConsistent()) {
// make working copy consistent
if (this.problems == null)
this.problems = new HashMap();
this.ast = workingCopy.makeConsistent(true, 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
if (this.forceProblemDetection) {
if (RubyProject.hasRubyNature(workingCopy.getRubyProject().getProject())) {
HashMap problemMap;
if (this.problems == null) {
problemMap = new HashMap();
if (this.forceProblemDetection)
this.problems = problemMap;
} else
problemMap = this.problems;
// find problems
char[] contents = workingCopy.getContents();
this.ast = RubyScriptProblemFinder.process(workingCopy, contents, problemMap, this.progressMonitor);
if (this.ast != null) {
this.deltaBuilder.delta = new RubyElementDelta(workingCopy);
this.deltaBuilder.delta.changedAST(this.ast);
}
if (this.progressMonitor != null) this.progressMonitor.worked(1);
} // else working copy not in a Ruby project
return this.ast;
}
return null;
}
/**
* Returns the working copy this operation is working on.
*/
protected RubyScript getWorkingCopy() {
return (RubyScript) getElementToProcess();
}
/**
* @see RubyModelOperation#isReadOnly
*/
public boolean isReadOnly() {
return true;
}
protected IRubyModelStatus verify() {
IRubyModelStatus status = super.verify();
if (!status.isOK()) {
return status;
}
RubyScript workingCopy = getWorkingCopy();
if (!workingCopy.isWorkingCopy()) {
return new RubyModelStatus(IRubyModelStatusConstants.ELEMENT_DOES_NOT_EXIST, workingCopy); // was
// destroyed
}
return status;
}
private void notifyParticipants(final RubyScript workingCopy) {
IRubyProject rubyProject = getWorkingCopy().getRubyProject();
CompilationParticipant[] participants = RubyModelManager.getRubyModelManager().compilationParticipants.getCompilationParticipants(rubyProject);
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);
}
});
}
}
}