/*
* Created on Apr 18, 2007 Copyright (C) 2001-5, Anthony Harrison anh23@pitt.edu
* (jactr.org) This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of the License,
* or (at your option) any later version. This library is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
* the GNU Lesser General Public License for more details. You should have
* received a copy of the GNU Lesser General Public License along with this
* library; if not, write to the Free Software Foundation, Inc., 59 Temple
* Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.jactr.eclipse.ui.reconciler;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IPositionUpdater;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.reconciler.DirtyRegion;
import org.eclipse.jface.text.reconciler.IReconcilingStrategy;
import org.eclipse.jface.text.reconciler.IReconcilingStrategyExtension;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IFileEditorInput;
import org.jactr.eclipse.core.CorePlugin;
import org.jactr.eclipse.core.comp.ICompilationUnit;
import org.jactr.eclipse.core.concurrent.QueueingJob;
import org.jactr.eclipse.core.parser.ProjectSensitiveParserImportDelegate;
import org.jactr.eclipse.ui.UIPlugin;
import org.jactr.eclipse.ui.editor.ACTRModelEditor;
import org.jactr.eclipse.ui.editor.assist.CodeAssistMarkerParticipant;
import org.jactr.eclipse.ui.editor.markers.FoldingMarkerParticipant;
import org.jactr.eclipse.ui.editor.markers.IPositionMarkerParticipant;
import org.jactr.eclipse.ui.editor.markers.PositionMarker;
import org.jactr.eclipse.ui.preferences.UIPreferences;
import org.jactr.io.antlr3.parser.AbstractModelParser;
import org.jactr.io.parser.CanceledException;
import org.jactr.io.parser.IModelParser;
import org.jactr.io.parser.ModelParserFactory;
public class ACTRReconcilingStrategy implements IReconcilingStrategy,
IReconcilingStrategyExtension
{
/**
* Logger definition
*/
static private final transient Log LOGGER = LogFactory
.getLog(ACTRReconcilingStrategy.class);
private IModelParser _modelParser;
private ICompilationUnit _workingCopy;
private final ACTRModelEditor _editor;
private IResource _resource;
private IDocument _document;
private ANTLRDocumentStream _documentStream;
private boolean _isInitial = true;
// private boolean _shouldCompile = true;
private ParseJob _parseJob;
private IProgressMonitor _nullProgress = new NullProgressMonitor();
// private CompileJob _compileJob;
public ACTRReconcilingStrategy(ACTRModelEditor editor)
{
_editor = editor;
}
public void initialReconcile()
{
if (LOGGER.isDebugEnabled()) LOGGER.debug("Initial reconcile");
if (_parseJob != null) _parseJob.queue();
}
public void setProgressMonitor(IProgressMonitor monitor)
{
}
public void reconcile(IRegion partition)
{
if (LOGGER.isDebugEnabled()) LOGGER.debug("reconcile " + partition);
if (_parseJob != null) _parseJob.queue();
}
public void reconcile(DirtyRegion dirtyRegion, IRegion subRegion)
{
if (LOGGER.isDebugEnabled())
LOGGER.debug("reconcile dirty " + dirtyRegion.getOffset() + ":"
+ dirtyRegion.getLength() + "[" + dirtyRegion.getType() + "] of "
+ subRegion.getOffset() + ":" + subRegion.getLength());
if (_parseJob != null) _parseJob.queue();
}
private void installPositionMarkers(IModelParser parser, IDocument document)
{
PositionMarker marker = new PositionMarker();
marker.setBase(_editor.getBase());
marker.setDocument(document);
IPreferenceStore prefs = UIPlugin.getDefault().getPreferenceStore();
if (prefs.getBoolean(UIPreferences.ENABLE_ASSIST_PREF))
{
IPositionMarkerParticipant participant = new CodeAssistMarkerParticipant();
marker.addParticipant(participant);
}
if (prefs.getBoolean(UIPreferences.ENABLE_FOLDING_PREF))
{
IPositionMarkerParticipant participant = new FoldingMarkerParticipant(
_editor);
marker.addParticipant(participant);
}
_modelParser.addTreeTracker(marker);
}
public void setDocument(IDocument document)
{
if (LOGGER.isDebugEnabled()) LOGGER.debug("Setting document " + _document);
/*
* we dont reuse reconcilers
*/
if (_resource != null) return;
_document = document;
_resource = getResource();
if (_resource != null)
try
{
_modelParser = ModelParserFactory.instantiateParser(_resource
.getFileExtension());
ProjectSensitiveParserImportDelegate delegate = new ProjectSensitiveParserImportDelegate();
delegate.setProject(_resource.getProject());
_modelParser.setImportDelegate(delegate);
/*
* set the base url so that relative imports will work
*/
((AbstractModelParser) _modelParser).setBaseURL(_resource
.getLocationURI().toURL());
_documentStream = new ANTLRDocumentStream(_document, null);
((AbstractModelParser) _modelParser).setInput(_documentStream);
installPositionMarkers(_modelParser, document);
// _builder = new ACTRModelBuilder();
}
catch (Exception e)
{
LOGGER.error("Could not set up parser ", e);
}
if (_modelParser != null) // _workingCopy = CompilationUnitManager.getWorkingCopy(_resource);
_parseJob = new ParseJob("Incremental Compile");
}
private IResource getResource()
{
IEditorInput input = _editor.getEditorInput();
if (input instanceof IFileEditorInput)
return ((IFileEditorInput) input).getFile();
return null;
}
private boolean parse(IProgressMonitor monitor)
{
if (monitor != null) _documentStream.setProgressMonitor(monitor);
/*
* we only need to compile if it is initial, or there were previously errors
* and the parse was clean. If the parse was dirty - who cares.
*/
try
{
// boolean hadErrors = _workingCopy.getCompileErrors().size() != 0;
// boolean cleanParse = _builder.parse(_workingCopy, _modelParser,
// _nullProgress);
// boolean shouldCompile = _isInitial || hadErrors && cleanParse;
return false;
}
catch (CanceledException pce)
{
return false;
}
finally
{
_isInitial = false;
_modelParser.reset();
}
}
private void compile(boolean shouldCompile)
{
// if (shouldCompile) _builder.compile(_workingCopy, _nullProgress);
//
// _builder.updateMarkers(_workingCopy, _nullProgress);
}
private void dispose()
{
IDocument doc = _editor.getDocumentProvider().getDocument(
_editor.getEditorInput());
for (IPositionUpdater updater : doc.getPositionUpdaters())
doc.removePositionUpdater(updater);
_workingCopy = null;
// _builder = null;
_modelParser = null;
// _editor.close(false);
_resource = null;
// System.gc();
}
// private class CompilationUnitRule implements ISchedulingRule
// {
//
// private ACTRCompilationUnit _compilationUnit;
//
// public CompilationUnitRule(ACTRCompilationUnit unit)
// {
// _compilationUnit = unit;
// }
//
// public boolean contains(ISchedulingRule rule)
// {
// return false;
// }
//
// public boolean isConflicting(ISchedulingRule rule)
// {
// if (rule instanceof CompilationUnitRule)
// return _compilationUnit == ((CompilationUnitRule) rule)._compilationUnit;
// return false;
// }
//
// }
/**
* the parse job uses an adaptive scheduling time based on the average of
* reconciliation time
*/
private class ParseJob extends QueueingJob
{
private long _reconcileAverage = 400;
private long _reconcileCount = 0;
private long _sleepDuration = 400;
public ParseJob(String name)
{
super(name);
setPriority(Job.LONG);
}
synchronized public void queue()
{
if (cancel())
{
if (LOGGER.isDebugEnabled())
LOGGER.debug("Canceling scheduling and resetting for "
+ _sleepDuration + "ms ("
+ (System.currentTimeMillis() + _sleepDuration) + ")");
schedule(_sleepDuration);
}
else if (LOGGER.isDebugEnabled())
LOGGER.debug("Letting it play through..");
}
@Override
synchronized protected IStatus run(IProgressMonitor monitor)
{
try
{
if (LOGGER.isDebugEnabled()) LOGGER.debug("Starting actual reconcile");
long start = System.currentTimeMillis();
boolean shouldCompile = parse(monitor);
compile(shouldCompile);
_reconcileAverage = (_reconcileAverage * _reconcileCount
+ System.currentTimeMillis() - start)
/ ++_reconcileCount;
if (LOGGER.isDebugEnabled())
LOGGER.debug("Average reconciliation time " + _reconcileAverage);
return Status.OK_STATUS;
}
catch (Exception e)
{
dispose();
_editor.close(false);
return new Status(IStatus.ERROR, CorePlugin.PLUGIN_ID,
"Parsing snafu on " + _resource.getName() + " forcing closed", e);
}
}
}
}