/* * SonarLint for Eclipse * Copyright (C) 2015-2017 SonarSource SA * sonarlint@sonarsource.com * * This program 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 3 of the License, or (at your option) any later version. * * This program 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 program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package org.sonarlint.eclipse.core.internal.markers; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.function.BiFunction; import javax.annotation.CheckForNull; import javax.annotation.Nullable; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.Position; import org.sonarlint.eclipse.core.SonarLintLogger; import org.sonarlint.eclipse.core.internal.PreferencesUtils; import org.sonarlint.eclipse.core.internal.SonarLintCorePlugin; public final class MarkerUtils { public static final String SONAR_MARKER_RULE_KEY_ATTR = "rulekey"; public static final String SONAR_MARKER_RULE_NAME_ATTR = "rulename"; public static final String SONAR_MARKER_ISSUE_SEVERITY_ATTR = "sonarseverity"; public static final String SONAR_MARKER_ISSUE_TYPE_ATTR = "issuetype"; public static final String SONAR_MARKER_CREATION_DATE_ATTR = "creationdate"; public static final String SONAR_MARKER_SERVER_ISSUE_KEY_ATTR = "serverissuekey"; public static final String SONAR_MARKER_HAS_EXTRA_LOCATION_KEY_ATTR = "hasextralocation"; public static final String SONARLINT_EXTRA_POSITIONS_CATEGORY = "sonarlintextralocations"; private MarkerUtils() { } public static List<IMarker> findIssuesMarkers(IResource resource) { try { return Arrays.asList(resource.findMarkers(SonarLintCorePlugin.MARKER_ID, true, IResource.DEPTH_INFINITE)); } catch (CoreException e) { SonarLintLogger.get().error(e.getMessage(), e); return Collections.emptyList(); } } public static List<IMarker> findChangeSetIssuesMarkers(IResource resource) { try { return Arrays.asList(resource.findMarkers(SonarLintCorePlugin.MARKER_CHANGESET_ID, true, IResource.DEPTH_INFINITE)); } catch (CoreException e) { SonarLintLogger.get().error(e.getMessage(), e); return Collections.emptyList(); } } public static void updateAllSonarMarkerSeverity() throws CoreException { for (IProject project : ResourcesPlugin.getWorkspace().getRoot().getProjects()) { if (project.isAccessible()) { for (IMarker marker : project.findMarkers(SonarLintCorePlugin.MARKER_ID, true, IResource.DEPTH_INFINITE)) { marker.setAttribute(IMarker.SEVERITY, PreferencesUtils.getMarkerSeverity()); } } } } @CheckForNull public static Position getPosition(final IDocument document, @Nullable TextRange textRange) { if (textRange == null || textRange.getStartLine() == null) { return null; } if (textRange.getStartLineOffset() == null) { return getPosition(document, textRange.getStartLine()); } return getPosition(document, textRange.getStartLine(), textRange.getStartLineOffset(), textRange.getEndLine(), textRange.getEndLineOffset()); } @CheckForNull public static Position getPosition(final IDocument document, int startLine) { int startLineStartOffset; int length; String lineDelimiter; try { startLineStartOffset = document.getLineOffset(startLine - 1); length = document.getLineLength(startLine - 1); lineDelimiter = document.getLineDelimiter(startLine - 1); } catch (BadLocationException e) { SonarLintLogger.get().error("failed to compute flat text range for line " + startLine, e); return null; } int lineDelimiterLength = lineDelimiter != null ? lineDelimiter.length() : 0; return new Position(startLineStartOffset, length - lineDelimiterLength); } @CheckForNull public static Position getPosition(final IDocument document, int startLine, int startLineOffset, int endLine, int endLineOffset) { try { return convertToGlobalOffset(document, startLine, startLineOffset, endLine, endLineOffset, Position::new); } catch (BadLocationException e) { SonarLintLogger.get().error("failed to compute line offsets for start, end = " + startLine + ", " + endLine, e); return null; } } @CheckForNull public static ExtraPosition getExtraPosition(final IDocument document, int startLine, int startLineOffset, int endLine, int endLineOffset, String message, long markerId, ExtraPosition parent) { try { return convertToGlobalOffset(document, startLine, startLineOffset, endLine, endLineOffset, (o, l) -> new ExtraPosition(o, l, message, markerId, parent)); } catch (BadLocationException e) { SonarLintLogger.get().error("failed to compute line offsets for start, end = " + startLine + ", " + endLine, e); return null; } } private static <G> G convertToGlobalOffset(final IDocument document, int startLine, int startLineOffset, int endLine, int endLineOffset, BiFunction<Integer, Integer, G> function) throws BadLocationException { int startLineStartOffset = document.getLineOffset(startLine - 1); int endLineStartOffset = endLine != startLine ? document.getLineOffset(endLine - 1) : startLineStartOffset; int start = startLineStartOffset + startLineOffset; int end = endLineStartOffset + endLineOffset; return function.apply(start, end - start); } public static class ExtraPosition extends Position { private final String message; private final long markerId; private final ExtraPosition previous; public ExtraPosition(int offset, int length, @Nullable String message, long markerId, @Nullable ExtraPosition previous) { super(offset, length); this.message = message; this.markerId = markerId; this.previous = previous; } @CheckForNull public String getMessage() { return message; } public long getMarkerId() { return markerId; } @CheckForNull public ExtraPosition getParent() { return previous; } @Override public boolean equals(Object other) { if (other instanceof ExtraPosition) { ExtraPosition rp = (ExtraPosition) other; return (rp.offset == offset) && (rp.length == length) && (rp.markerId == markerId) && Objects.equals(rp.message, message) && Objects.equals(rp.previous, previous); } return super.equals(other); } @Override public int hashCode() { final int prime = 31; int result = super.hashCode(); result = prime * result + (int) (markerId ^ (markerId >>> 32)); return result; } } }