/******************************************************************************* * Copyright (c) 2015 Bruno Medeiros and other Contributors. * 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: * Bruno Medeiros - initial API and implementation *******************************************************************************/ package melnorme.lang.ide.core.engine; import static melnorme.utilbox.core.Assert.AssertNamespace.assertNotNull; import static melnorme.utilbox.core.Assert.AssertNamespace.assertTrue; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspace; import org.eclipse.core.resources.IWorkspaceRunnable; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.jobs.Job; import melnorme.lang.ide.core.EclipseCore; import melnorme.lang.ide.core.LangCore_Actual; import melnorme.lang.ide.core.engine.SourceModelManager.StructureInfo; import melnorme.lang.ide.core.operations.ToolMarkersHelper; import melnorme.lang.ide.core.utils.CoreExecutors; import melnorme.lang.ide.core.utils.ResourceUtils; import melnorme.lang.tooling.common.ParserError; import melnorme.lang.tooling.structure.SourceFileStructure; import melnorme.utilbox.concurrency.CancellableTask; import melnorme.utilbox.concurrency.ITaskAgent; import melnorme.utilbox.concurrency.OperationCancellation; import melnorme.utilbox.core.CommonException; import melnorme.utilbox.core.fntypes.CommonResult; import melnorme.utilbox.misc.Location; import melnorme.utilbox.ownership.IDisposable; import melnorme.utilbox.ownership.LifecycleObject; public class ProblemMarkerUpdater extends LifecycleObject { protected SourceModelManager sourceModelManager; protected final ITaskAgent executor = CoreExecutors.newExecutorTaskAgent(getClass()); public ProblemMarkerUpdater() { } public void install(SourceModelManager sourceModelManager) { this.sourceModelManager = sourceModelManager; IDisposable listenerRegistration = sourceModelManager.addListener(problemUpdaterListener); asOwner().bind(listenerRegistration); sourceModelManager.asOwner().bind(this); // Make ProblemMarkerUpdater be owned by the model manager } @Override protected void dispose_post() { executor.shutdownNowAndCancelAll(); super.dispose_post(); } protected final IStructureModelListener problemUpdaterListener = new IStructureModelListener() { @Override public void dataChanged(StructureInfo structureInfo) { Location location = structureInfo.getLocation(); if(location == null) return; assertTrue(Job.getJobManager().currentRule() == null); UpdateProblemMarkersTask task; try { task = new UpdateProblemMarkersTask(structureInfo); } catch(CommonException e) { // Ignore return; } executor.submitTask(task); } }; protected static class UpdateProblemMarkersTask extends CancellableTask { protected final StructureInfo structureInfo; protected final Location location; protected final SourceFileStructure structure; protected final CommonResult<SourceFileStructure> storedData; public UpdateProblemMarkersTask(StructureInfo structureInfo) throws CommonException { this.structureInfo = assertNotNull(structureInfo); this.location = assertNotNull(structureInfo.getLocation()); this.storedData = structureInfo.getStoredData(); this.structure = storedData.get(); } @Override protected void doRun() { try { checkIsStillValid(); updateProblemMarkers(); } catch(CoreException ce) { EclipseCore.logStatus(ce); } catch(OperationCancellation e) { return; } } protected void checkIsStillValid() throws OperationCancellation { if(structureInfo.isStale(storedData)) { // A new update is on the way, so ignore these marker updates throw new OperationCancellation(); } } protected void updateProblemMarkers() throws CoreException { // Review if this can run outside lock IFile[] files = ResourceUtils.getWorkspaceRoot().findFilesForLocationURI(location.toUri()); if(files.length == 0) { return; } final IFile file = files[0]; ResourceUtils.getWorkspace().run(new IWorkspaceRunnable() { @Override public void run(IProgressMonitor monitor) throws CoreException { try { doCreateProblemMarkers(file); } catch(OperationCancellation e) { return; } } }, file, IWorkspace.AVOID_UPDATE, null); } protected void doCreateProblemMarkers(IFile file) throws CoreException, OperationCancellation { checkIsStillValid(); if(!file.exists()) { return; // It could have been removed in the meanwhile. } file.deleteMarkers(LangCore_Actual.SOURCE_PROBLEM_ID, true, IResource.DEPTH_ZERO); if(structure == null) { return; } for (ParserError problem : structure.getParserProblems()) { // checkIsStillValid(); createMarker(location, file, problem); } } protected void createMarker(final Location location, IFile file, ParserError problem) throws CoreException { IMarker marker = file.createMarker(LangCore_Actual.SOURCE_PROBLEM_ID); marker.setAttribute(IMarker.LOCATION, location.toPathString()); marker.setAttribute(IMarker.MESSAGE, problem.getUserMessage()); marker.setAttribute(IMarker.SEVERITY, ToolMarkersHelper.markerSeverityFrom(problem.getSeverity())); marker.setAttribute(IMarker.CHAR_START, problem.getStartPos()); marker.setAttribute(IMarker.CHAR_END, problem.getEndPos()); } } }