package com.redhat.ceylon.eclipse.code.parse;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.ProgressMonitorWrapper;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.text.IDocument;
import com.redhat.ceylon.eclipse.code.editor.CeylonEditor;
import com.redhat.ceylon.eclipse.code.editor.CeylonSourceViewer;
import com.redhat.ceylon.eclipse.code.parse.TreeLifecycleListener.Stage;
public class CeylonParserScheduler extends Job {
private boolean canceling = false;
private CeylonParseController parseController;
private CeylonEditor editor;
private final List<TreeLifecycleListener> listeners = new ArrayList<TreeLifecycleListener>();
public CeylonParserScheduler(CeylonParseController parseController,
CeylonEditor editor) {
super("Parsing and typechecking " + editor.getEditorInput().getName());
setSystem(true); //do not show this job in the Progress view
setPriority(SHORT);
setRule(new ISchedulingRule() {
@Override
public boolean isConflicting(ISchedulingRule rule) {
return rule==this;
}
@Override
public boolean contains(ISchedulingRule rule) {
return rule==this;
}
});
// Note: The parse controller is now initialized before
// it gets handed to us here, since some other services
// may actually depend on that.
this.parseController = parseController;
this.editor = editor;
}
@Override
protected void canceling() {
canceling = true;
}
public boolean isCanceling() {
return canceling;
}
public class Stager {
void afterStage(Stage stage, IProgressMonitor monitor) {
notifyModelListeners(stage, monitor);
}
}
private boolean sourceStillExists() {
IProject project= parseController.getProject();
if (project==null) {
return true; // this wasn't a workspace resource to begin with
}
if (!project.exists()) {
return false;
}
IFile file= project.getFile(parseController.getPath());
return file.exists();
}
@Override
public IStatus run(IProgressMonitor monitor) {
try {
if (canceling) {
if (monitor!=null) {
monitor.setCanceled(true);
}
return Status.CANCEL_STATUS;
}
if (editor.isBackgroundParsingPaused()) {
return Status.OK_STATUS;
}
IProgressMonitor wrappedMonitor = new ProgressMonitorWrapper(monitor) {
@Override
public boolean isCanceled() {
boolean isCanceled = false;
if (Job.getJobManager().currentJob() == CeylonParserScheduler.this) {
isCanceled = canceling;
}
return isCanceled || super.isCanceled();
}
};
try {
CeylonSourceViewer csv = editor.getCeylonSourceViewer();
IDocument document = csv==null ? null : csv.getDocument();
// If we're editing a workspace resource, check
// to make sure that it still exists
if (document==null || !sourceStillExists()) {
return Status.OK_STATUS;
}
//TODO: is this a better way to clear existing
// annotations/markers:
//fMsgHandler.clearMessages();
// don't bother to retrieve the AST; we don't
// need it; just make sure the document gets
// parsed
parseController.parseAndTypecheck(document, 0, wrappedMonitor, new Stager());
}
catch (Exception e) {
e.printStackTrace();
}
return wrappedMonitor.isCanceled() ? //&& sourceStillExists()
Status.OK_STATUS :
Status.CANCEL_STATUS;
}
finally {
canceling = false;
}
}
public void addModelListener(TreeLifecycleListener listener) {
listeners.add(listener);
}
public void removeModelListener(TreeLifecycleListener listener) {
listeners.remove(listener);
}
public void dispose() {
listeners.clear();
}
private synchronized void notifyModelListeners(Stage stage, IProgressMonitor monitor) {
if (parseController!=null) {
for (TreeLifecycleListener listener: new ArrayList<TreeLifecycleListener>(listeners)) {
if (listener.getStage()==stage) {
listener.update(parseController, monitor);
}
}
}
}
}