/******************************************************************************* * Copyright (c) 2008, 2017 xored software, Inc. and others. * * 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: * xored software, Inc. - initial API and Implementation (Alex Panchenko) *******************************************************************************/ package org.eclipse.dltk.tcl.internal.tclchecker.qfix; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.dltk.core.DLTKCore; import org.eclipse.dltk.core.ISourceModule; import org.eclipse.dltk.core.environment.EnvironmentManager; import org.eclipse.dltk.core.environment.IEnvironment; import org.eclipse.dltk.tcl.internal.tclchecker.TclChecker; import org.eclipse.dltk.tcl.internal.tclchecker.TclCheckerConfigUtils; import org.eclipse.dltk.tcl.internal.tclchecker.TclCheckerConfigUtils.ValidatorInstanceRef; import org.eclipse.dltk.tcl.internal.tclchecker.TclCheckerConfigUtils.ValidatorInstanceResponse; import org.eclipse.dltk.tcl.internal.tclchecker.TclCheckerMarker; import org.eclipse.dltk.tcl.tclchecker.TclCheckerPlugin; import org.eclipse.dltk.utils.TextUtils; import org.eclipse.dltk.validators.core.NullValidatorOutput; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IRegion; public class TclCheckerFixUtils { public static String getLabel(String replacement) { final int totalLines = TextUtils.countLines(replacement); final String[] lines = TextUtils.splitLines(replacement, 3); for (int i = 0; i < lines.length; ++i) { lines[i] = lines[i].trim(); } String result = Messages.TclCheckerMarkerResolution_replaceWith + TextUtils.join(lines, ' '); if (totalLines > lines.length) { result += " ..."; //$NON-NLS-1$ } return result; } public static String getDescription(String replacement) { return "<pre>" + replacement + "</pre>"; //$NON-NLS-1$ //$NON-NLS-2$ } private static class MarkerFinder implements IMarkerFinder { final int lineNumber; final String message; final String messageId; public MarkerFinder(IMarker pattern) { this.lineNumber = pattern.getAttribute(IMarker.LINE_NUMBER, -1); this.message = pattern.getAttribute(IMarker.MESSAGE, null); this.messageId = pattern.getAttribute(TclCheckerMarker.MESSAGE_ID, null); } @Override public IMarker find(IResource resource) { final IMarker[] markers; try { markers = resource.findMarkers(TclCheckerMarker.TYPE, true, IResource.DEPTH_INFINITE); } catch (CoreException e) { TclCheckerPlugin.log(IStatus.ERROR, e.toString(), e); return null; } if (lineNumber < 0 || message == null || messageId == null) { return null; } for (int i = 0; i < markers.length; ++i) { final IMarker m = markers[i]; if (lineNumber == m.getAttribute(IMarker.LINE_NUMBER, -1) && message.equals(m.getAttribute(IMarker.MESSAGE, null)) && messageId.equals(m.getAttribute(TclCheckerMarker.MESSAGE_ID, null))) { return m; } } return null; } } private static boolean isValidTimestamp(IResource resource, List<IMarker> markers) { final String resourceTimestamp = String.valueOf(resource.getModificationStamp()); for (IMarker marker : markers) { final String savedStamp = marker.getAttribute(TclCheckerMarker.TIMESTAMP, null); if (savedStamp == null || !savedStamp.equals(resourceTimestamp)) { return false; } } return true; } public static IMarker verify(IMarker marker, ISourceModule module) { final List<IMarker> result = verify(Collections.singletonList(marker), module); return !result.isEmpty() ? result.get(0) : null; } public static List<IMarker> verify(List<IMarker> markers, ISourceModule module) { final IResource resource = markers.get(0).getResource(); if (resource.getType() != IResource.FILE) { return Collections.emptyList(); } for (IMarker marker : markers) { if (!resource.equals(marker.getResource())) { return Collections.emptyList(); } } if (isValidTimestamp(resource, markers)) { return markers; } final List<IMarkerFinder> finders = new ArrayList<>(); for (IMarker marker : markers) { finders.add(createMarkerFinder(marker)); } if (revalidate(resource, module)) { final List<IMarker> result = new ArrayList<>(); for (IMarkerFinder finder : finders) { final IMarker marker = finder.find(resource); if (marker != null) { result.add(marker); } } return result; } return Collections.emptyList(); } public static boolean revalidate(final IResource resource, ISourceModule module) { if (module == null) { module = (ISourceModule) DLTKCore.create((IFile) resource); if (module == null) { return false; } } final IEnvironment environment = EnvironmentManager.getEnvironment(module); if (environment == null) { return false; } final ValidatorInstanceResponse response = TclCheckerConfigUtils.getConfiguration(resource.getProject(), TclCheckerConfigUtils.AUTO); if (response.isEmpty()) { return false; } ValidatorInstanceRef pair = response.instances.get(0); new TclChecker(pair.environmentInstance, pair.config, environment).validate(new ISourceModule[] { module }, new NullValidatorOutput(), new NullProgressMonitor()); return true; } public static IMarkerFinder createMarkerFinder(IMarker marker) { final IMarkerFinder finder = new MarkerFinder(marker); return finder; } /** * @param target * @param document */ public static void updateDocument(IMarker marker, IDocument document, String replacement, ITclCheckerQFixReporter reporter) { final int lineNumber = marker.getAttribute(IMarker.LINE_NUMBER, -1); int commandStart = marker.getAttribute(TclCheckerMarker.COMMAND_START, -1); int commandEnd = marker.getAttribute(TclCheckerMarker.COMMAND_END, -1); if (lineNumber < 0 || commandStart < 0 || commandEnd < 0) { reporter.showError(Messages.TclCheckerAnnotationResolution_wrongMarkerAttributes); return; } try { final IRegion lineRegion = document.getLineInformation(lineNumber - 1); if (commandStart >= lineRegion.getOffset() && commandStart < lineRegion.getOffset() + lineRegion.getLength()) { if (TextUtils.countLines(replacement) == 1 && commandEnd >= lineRegion.getOffset() + lineRegion.getLength()) { reporter.showError(Messages.TclCheckerFixUtils_errorLineLengthOverrun); commandEnd = lineRegion.getOffset() + lineRegion.getLength() - 1; } document.replace(commandStart, commandEnd - commandStart + 1, replacement); try { marker.delete(); } catch (CoreException e) { reporter.showError(Messages.TclCheckerAnnotationResolution_markerDeleteError + e.getMessage()); } } else { reporter.showError(Messages.TclCheckerAnnotationResolution_wrongCommandStart); } } catch (BadLocationException e) { reporter.showError(Messages.TclCheckerAnnotationResolution_internalError + e.getMessage()); } } }