package org.eclipse.dltk.tcl.internal.tclchecker.ui; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceVisitor; import org.eclipse.core.runtime.CoreException; 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.dltk.compiler.util.Util; import org.eclipse.dltk.core.CorrectionEngine; import org.eclipse.dltk.core.DLTKCore; import org.eclipse.dltk.core.IModelElement; import org.eclipse.dltk.core.IModelElementVisitor; import org.eclipse.dltk.core.IParent; import org.eclipse.dltk.core.IProjectFragment; import org.eclipse.dltk.core.ISourceModule; import org.eclipse.dltk.core.ModelException; import org.eclipse.dltk.internal.core.BuiltinSourceModule; import org.eclipse.dltk.internal.core.ExternalSourceModule; import org.eclipse.dltk.tcl.internal.tclchecker.TclCheckerMarker; import org.eclipse.dltk.tcl.internal.tclchecker.qfix.TclCheckerFixUtils; import org.eclipse.dltk.tcl.tclchecker.TclCheckerPlugin; import org.eclipse.jface.action.IAction; import org.eclipse.jface.text.Document; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.IWorkbenchWindowActionDelegate; public class AutoCorrectTclCheckerProblemsActionDelegate implements IWorkbenchWindowActionDelegate { private ISelection selection; @Override public void dispose() { } @Override public void init(IWorkbenchWindow window) { } @Override public void run(IAction action) { if (this.selection != null && this.selection instanceof IStructuredSelection && !this.selection.isEmpty()) { processSelectionToElements(); } } private static class SourceModuleVisitor implements IModelElementVisitor { private Set<ISourceModule> elements = new HashSet<>(); @Override public boolean visit(IModelElement element) { if (element.getElementType() == IModelElement.PROJECT_FRAGMENT) { return !((IProjectFragment) element).isExternal(); } if (element.getElementType() == IModelElement.SOURCE_MODULE) { if (!(element instanceof ExternalSourceModule || element instanceof BuiltinSourceModule) && element.getResource() != null) { elements.add((ISourceModule) element); } return false; // do not enter into source module content. } return true; } } private static class ResourceVisitor implements IResourceVisitor { private Set<IFile> files = new HashSet<>(); @Override public boolean visit(IResource resource) { if (resource.getType() == IResource.FILE) { files.add((IFile) resource); return false; } return true; } } private static class Collector { final ResourceVisitor resourceVisitor = new ResourceVisitor(); final SourceModuleVisitor moduleVisitor = new SourceModuleVisitor(); public void processResourcesToElements(Object o) { if (o instanceof IResource) { try { ((IResource) o).accept(resourceVisitor); } catch (CoreException e) { if (DLTKCore.DEBUG) { e.printStackTrace(); } } } else if (o instanceof IModelElement) { if (o instanceof IParent) { try { ((IModelElement) o).accept(moduleVisitor); } catch (ModelException e) { if (DLTKCore.DEBUG) { e.printStackTrace(); } } } else if (!(o instanceof ISourceModule)) { final ISourceModule module = (ISourceModule) ((IModelElement) o) .getAncestor(IModelElement.SOURCE_MODULE); moduleVisitor.elements.add(module); } else if (o instanceof ISourceModule) { moduleVisitor.elements.add((ISourceModule) o); } } } void convert() { for (ISourceModule module : moduleVisitor.elements) { IResource resource = module.getResource(); if (resource.getType() == IResource.FILE) { resourceVisitor.files.add((IFile) resource); } } } /** * @return */ public Set<IFile> getFiles() { return resourceVisitor.files; } } protected void processSelectionToElements() { final Collector collector = new Collector(); for (Iterator<?> iterator = ((IStructuredSelection) this.selection).iterator(); iterator.hasNext();) { collector.processResourcesToElements(iterator.next()); } collector.convert(); final Set<IFile> files = collector.getFiles(); final Job job = new Job("Auto correct...") { //$NON-NLS-1$ @Override protected IStatus run(IProgressMonitor monitor) { monitor.beginTask("Processing", files.size()); //$NON-NLS-1$ try { for (IFile file : files) { final ISourceModule module = (ISourceModule) DLTKCore.create(file); if (module == null) { continue; } int count = 0; while (count < 100 && processFile(file, module)) { ++count; } monitor.worked(1); } } catch (CoreException e) { TclCheckerPlugin.error(e); } finally { monitor.done(); } return Status.OK_STATUS; } private boolean processFile(IFile file, ISourceModule module) throws CoreException { final IMarker[] markers = file.findMarkers(TclCheckerMarker.TYPE, true, IResource.DEPTH_ZERO); for (int i = 0; i < markers.length; ++i) { if ("1".equals(markers[i].getAttribute(TclCheckerMarker.AUTO_CORRECTABLE, Util.EMPTY_STRING))) { //$NON-NLS-1$ final IMarker valid = TclCheckerFixUtils.verify(markers[i], module); if (valid != null) { final String[] corrections = CorrectionEngine .decodeArguments(valid.getAttribute(TclCheckerMarker.SUGGESTED_CORRECTIONS, null)); if (corrections.length != 1) { return false; } /* * FIXME instead of these hacks let's try to create * Positions in document and replace all in single * operation. */ module.becomeWorkingCopy(null, new NullProgressMonitor()); try { IDocument document = new Document(module.getSource()); TclCheckerFixUtils.updateDocument(valid, document, corrections[0], message -> { // NOP }); module.getBuffer().setContents(document.get()); module.commitWorkingCopy(true, new NullProgressMonitor()); } finally { module.discardWorkingCopy(); } } return true; } } return false; } }; job.setUser(true); job.schedule(); } @Override public void selectionChanged(IAction action, ISelection selection) { this.selection = selection; } }