aboutsummaryrefslogtreecommitdiff
path: root/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/EclipseLintClient.java
diff options
context:
space:
mode:
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/EclipseLintClient.java')
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/EclipseLintClient.java1306
1 files changed, 0 insertions, 1306 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/EclipseLintClient.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/EclipseLintClient.java
deleted file mode 100644
index 3dd424087..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/EclipseLintClient.java
+++ /dev/null
@@ -1,1306 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.eclipse.org/org/documents/epl-v10.php
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.ide.eclipse.adt.internal.lint;
-
-import static com.android.SdkConstants.DOT_JAR;
-import static com.android.SdkConstants.DOT_XML;
-import static com.android.SdkConstants.FD_NATIVE_LIBS;
-import static com.android.ide.eclipse.adt.AdtConstants.MARKER_LINT;
-import static com.android.ide.eclipse.adt.AdtUtils.workspacePathToFile;
-
-import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.AdtUtils;
-import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate;
-import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode;
-import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs;
-import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
-import com.android.ide.eclipse.adt.internal.sdk.Sdk;
-import com.android.sdklib.IAndroidTarget;
-import com.android.tools.lint.checks.BuiltinIssueRegistry;
-import com.android.tools.lint.client.api.Configuration;
-import com.android.tools.lint.client.api.IssueRegistry;
-import com.android.tools.lint.client.api.JavaParser;
-import com.android.tools.lint.client.api.LintClient;
-import com.android.tools.lint.client.api.LintDriver;
-import com.android.tools.lint.client.api.XmlParser;
-import com.android.tools.lint.detector.api.ClassContext;
-import com.android.tools.lint.detector.api.Context;
-import com.android.tools.lint.detector.api.DefaultPosition;
-import com.android.tools.lint.detector.api.Detector;
-import com.android.tools.lint.detector.api.Issue;
-import com.android.tools.lint.detector.api.JavaContext;
-import com.android.tools.lint.detector.api.LintUtils;
-import com.android.tools.lint.detector.api.Location;
-import com.android.tools.lint.detector.api.Location.Handle;
-import com.android.tools.lint.detector.api.Position;
-import com.android.tools.lint.detector.api.Project;
-import com.android.tools.lint.detector.api.Severity;
-import com.android.tools.lint.detector.api.TextFormat;
-import com.android.tools.lint.detector.api.XmlContext;
-import com.android.utils.Pair;
-import com.android.utils.SdkUtils;
-import com.google.common.collect.Maps;
-
-import org.eclipse.core.resources.IFile;
-import org.eclipse.core.resources.IMarker;
-import org.eclipse.core.resources.IProject;
-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.jdt.core.IClasspathEntry;
-import org.eclipse.jdt.core.IJavaProject;
-import org.eclipse.jdt.core.IType;
-import org.eclipse.jdt.core.ITypeHierarchy;
-import org.eclipse.jdt.core.JavaCore;
-import org.eclipse.jdt.core.JavaModelException;
-import org.eclipse.jdt.internal.compiler.CompilationResult;
-import org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies;
-import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
-import org.eclipse.jdt.internal.compiler.batch.CompilationUnit;
-import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
-import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
-import org.eclipse.jdt.internal.compiler.parser.Parser;
-import org.eclipse.jdt.internal.compiler.problem.AbortCompilation;
-import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory;
-import org.eclipse.jdt.internal.compiler.problem.ProblemReporter;
-import org.eclipse.jface.text.BadLocationException;
-import org.eclipse.jface.text.IDocument;
-import org.eclipse.jface.text.IRegion;
-import org.eclipse.swt.widgets.Shell;
-import org.eclipse.ui.IEditorPart;
-import org.eclipse.ui.PartInitException;
-import org.eclipse.ui.editors.text.TextFileDocumentProvider;
-import org.eclipse.ui.ide.IDE;
-import org.eclipse.ui.texteditor.IDocumentProvider;
-import org.eclipse.wst.sse.core.StructuredModelManager;
-import org.eclipse.wst.sse.core.internal.provisional.IModelManager;
-import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
-import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion;
-import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
-import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel;
-import org.w3c.dom.Attr;
-import org.w3c.dom.Document;
-import org.w3c.dom.Node;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.WeakHashMap;
-
-import lombok.ast.ecj.EcjTreeConverter;
-import lombok.ast.grammar.ParseProblem;
-import lombok.ast.grammar.Source;
-
-/**
- * Eclipse implementation for running lint on workspace files and projects.
- */
-@SuppressWarnings("restriction") // DOM model
-public class EclipseLintClient extends LintClient {
- static final String MARKER_CHECKID_PROPERTY = "checkid"; //$NON-NLS-1$
- private static final String MODEL_PROPERTY = "model"; //$NON-NLS-1$
- private final List<? extends IResource> mResources;
- private final IDocument mDocument;
- private boolean mWasFatal;
- private boolean mFatalOnly;
- private EclipseJavaParser mJavaParser;
- private boolean mCollectNodes;
- private Map<Node, IMarker> mNodeMap;
-
- /**
- * Creates a new {@link EclipseLintClient}.
- *
- * @param registry the associated detector registry
- * @param resources the associated resources (project, file or null)
- * @param document the associated document, or null if the {@code resource}
- * param is not a file
- * @param fatalOnly whether only fatal issues should be reported (and therefore checked)
- */
- public EclipseLintClient(IssueRegistry registry, List<? extends IResource> resources,
- IDocument document, boolean fatalOnly) {
- mResources = resources;
- mDocument = document;
- mFatalOnly = fatalOnly;
- }
-
- /**
- * Returns true if lint should only check fatal issues
- *
- * @return true if lint should only check fatal issues
- */
- public boolean isFatalOnly() {
- return mFatalOnly;
- }
-
- /**
- * Sets whether the lint client should store associated XML nodes for each
- * reported issue
- *
- * @param collectNodes if true, collect node positions for errors in XML
- * files, retrievable via the {@link #getIssueForNode} method
- */
- public void setCollectNodes(boolean collectNodes) {
- mCollectNodes = collectNodes;
- }
-
- /**
- * Returns one of the issues for the given node (there could be more than one)
- *
- * @param node the node to look up lint issues for
- * @return the marker for one of the issues found for the given node
- */
- @Nullable
- public IMarker getIssueForNode(@NonNull UiViewElementNode node) {
- if (mNodeMap != null) {
- return mNodeMap.get(node.getXmlNode());
- }
-
- return null;
- }
-
- /**
- * Returns a collection of nodes that have one or more lint warnings
- * associated with them (retrievable via
- * {@link #getIssueForNode(UiViewElementNode)})
- *
- * @return a collection of nodes, which should <b>not</b> be modified by the
- * caller
- */
- @Nullable
- public Collection<Node> getIssueNodes() {
- if (mNodeMap != null) {
- return mNodeMap.keySet();
- }
-
- return null;
- }
-
- // ----- Extends LintClient -----
-
- @Override
- public void log(@NonNull Severity severity, @Nullable Throwable exception,
- @Nullable String format, @Nullable Object... args) {
- if (exception == null) {
- AdtPlugin.log(IStatus.WARNING, format, args);
- } else {
- AdtPlugin.log(exception, format, args);
- }
- }
-
- @Override
- public XmlParser getXmlParser() {
- return new XmlParser() {
- @Override
- public Document parseXml(@NonNull XmlContext context) {
- // Map File to IFile
- IFile file = AdtUtils.fileToIFile(context.file);
- if (file == null || !file.exists()) {
- String path = context.file.getPath();
- AdtPlugin.log(IStatus.ERROR, "Can't find file %1$s in workspace", path);
- return null;
- }
-
- IStructuredModel model = null;
- try {
- IModelManager modelManager = StructuredModelManager.getModelManager();
- if (modelManager == null) {
- // This can happen if incremental lint is running right as Eclipse is
- // shutting down
- return null;
- }
- model = modelManager.getModelForRead(file);
- if (model instanceof IDOMModel) {
- context.setProperty(MODEL_PROPERTY, model);
- IDOMModel domModel = (IDOMModel) model;
- return domModel.getDocument();
- }
- } catch (IOException e) {
- AdtPlugin.log(e, "Cannot read XML file");
- } catch (CoreException e) {
- AdtPlugin.log(e, null);
- }
-
- return null;
- }
-
- @Override
- public @NonNull Location getLocation(@NonNull XmlContext context, @NonNull Node node) {
- IStructuredModel model = (IStructuredModel) context.getProperty(MODEL_PROPERTY);
- return new LazyLocation(context.file, model.getStructuredDocument(),
- (IndexedRegion) node);
- }
-
- @Override
- public @NonNull Location getLocation(@NonNull XmlContext context, @NonNull Node node,
- int start, int end) {
- IndexedRegion region = (IndexedRegion) node;
- int nodeStart = region.getStartOffset();
-
- IStructuredModel model = (IStructuredModel) context.getProperty(MODEL_PROPERTY);
- // Get line number
- LazyLocation location = new LazyLocation(context.file,
- model.getStructuredDocument(), region);
- int line = location.getStart().getLine();
-
- Position startPos = new DefaultPosition(line, -1, nodeStart + start);
- Position endPos = new DefaultPosition(line, -1, nodeStart + end);
- return Location.create(context.file, startPos, endPos);
- }
-
- @Override
- public int getNodeStartOffset(@NonNull XmlContext context, @NonNull Node node) {
- IndexedRegion region = (IndexedRegion) node;
- return region.getStartOffset();
- }
-
- @Override
- public int getNodeEndOffset(@NonNull XmlContext context, @NonNull Node node) {
- IndexedRegion region = (IndexedRegion) node;
- return region.getEndOffset();
- }
-
- @Override
- public @NonNull Handle createLocationHandle(final @NonNull XmlContext context,
- final @NonNull Node node) {
- IStructuredModel model = (IStructuredModel) context.getProperty(MODEL_PROPERTY);
- return new LazyLocation(context.file, model.getStructuredDocument(),
- (IndexedRegion) node);
- }
-
- @Override
- public void dispose(@NonNull XmlContext context, @NonNull Document document) {
- IStructuredModel model = (IStructuredModel) context.getProperty(MODEL_PROPERTY);
- assert model != null : context.file;
- if (model != null) {
- model.releaseFromRead();
- }
- }
-
- @Override
- @NonNull
- public Location getNameLocation(@NonNull XmlContext context, @NonNull Node node) {
- return getLocation(context, node);
- }
-
- @Override
- @NonNull
- public Location getValueLocation(@NonNull XmlContext context, @NonNull Attr node) {
- return getLocation(context, node);
- }
-
- };
- }
-
- @Override
- public JavaParser getJavaParser(@Nullable Project project) {
- if (mJavaParser == null) {
- mJavaParser = new EclipseJavaParser();
- }
-
- return mJavaParser;
- }
-
- // Cache for {@link getProject}
- private IProject mLastEclipseProject;
- private Project mLastLintProject;
-
- private IProject getProject(Project project) {
- if (project == mLastLintProject) {
- return mLastEclipseProject;
- }
-
- mLastLintProject = project;
- mLastEclipseProject = null;
-
- if (mResources != null) {
- if (mResources.size() == 1) {
- IProject p = mResources.get(0).getProject();
- mLastEclipseProject = p;
- return p;
- }
-
- IProject last = null;
- for (IResource resource : mResources) {
- IProject p = resource.getProject();
- if (p != last) {
- if (project.getDir().equals(AdtUtils.getAbsolutePath(p).toFile())) {
- mLastEclipseProject = p;
- return p;
- }
- last = p;
- }
- }
- }
-
- return null;
- }
-
- @Override
- @NonNull
- public String getProjectName(@NonNull Project project) {
- // Initialize the lint project's name to the name of the Eclipse project,
- // which might differ from the directory name
- IProject eclipseProject = getProject(project);
- if (eclipseProject != null) {
- return eclipseProject.getName();
- }
-
- return super.getProjectName(project);
- }
-
- @NonNull
- @Override
- public Configuration getConfiguration(@NonNull Project project, @Nullable LintDriver driver) {
- return getConfigurationFor(project);
- }
-
- /**
- * Same as {@link #getConfiguration(Project)}, but {@code project} can be
- * null in which case the global configuration is returned.
- *
- * @param project the project to look up
- * @return a corresponding configuration
- */
- @NonNull
- public Configuration getConfigurationFor(@Nullable Project project) {
- if (project != null) {
- IProject eclipseProject = getProject(project);
- if (eclipseProject != null) {
- return ProjectLintConfiguration.get(this, eclipseProject, mFatalOnly);
- }
- }
-
- return GlobalLintConfiguration.get();
- }
- @Override
- public void report(@NonNull Context context, @NonNull Issue issue, @NonNull Severity s,
- @Nullable Location location,
- @NonNull String message, @NonNull TextFormat format) {
- message = format.toText(message);
- int severity = getMarkerSeverity(s);
- IMarker marker = null;
- if (location != null) {
- Position startPosition = location.getStart();
- if (startPosition == null) {
- if (location.getFile() != null) {
- IResource resource = AdtUtils.fileToResource(location.getFile());
- if (resource != null && resource.isAccessible()) {
- marker = BaseProjectHelper.markResource(resource, MARKER_LINT,
- message, 0, severity);
- }
- }
- } else {
- Position endPosition = location.getEnd();
- int line = startPosition.getLine() + 1; // Marker API is 1-based
- IFile file = AdtUtils.fileToIFile(location.getFile());
- if (file != null && file.isAccessible()) {
- Pair<Integer, Integer> r = getRange(file, mDocument,
- startPosition, endPosition);
- int startOffset = r.getFirst();
- int endOffset = r.getSecond();
- marker = BaseProjectHelper.markResource(file, MARKER_LINT,
- message, line, startOffset, endOffset, severity);
- }
- }
- }
-
- if (marker == null) {
- marker = BaseProjectHelper.markResource(mResources.get(0), MARKER_LINT,
- message, 0, severity);
- }
-
- if (marker != null) {
- // Store marker id such that we can recognize it from the suppress quickfix
- try {
- marker.setAttribute(MARKER_CHECKID_PROPERTY, issue.getId());
- } catch (CoreException e) {
- AdtPlugin.log(e, null);
- }
- }
-
- if (s == Severity.FATAL) {
- mWasFatal = true;
- }
-
- if (mCollectNodes && location != null && marker != null) {
- if (location instanceof LazyLocation) {
- LazyLocation l = (LazyLocation) location;
- IndexedRegion region = l.mRegion;
- if (region instanceof Node) {
- Node node = (Node) region;
- if (node instanceof Attr) {
- node = ((Attr) node).getOwnerElement();
- }
- if (mNodeMap == null) {
- mNodeMap = new WeakHashMap<Node, IMarker>();
- }
- IMarker prev = mNodeMap.get(node);
- if (prev != null) {
- // Only replace the node if this node has higher priority
- int prevSeverity = prev.getAttribute(IMarker.SEVERITY, 0);
- if (prevSeverity < severity) {
- mNodeMap.put(node, marker);
- }
- } else {
- mNodeMap.put(node, marker);
- }
- }
- }
- }
- }
-
- @Override
- @Nullable
- public File findResource(@NonNull String relativePath) {
- // Look within the $ANDROID_SDK
- String sdkFolder = AdtPrefs.getPrefs().getOsSdkFolder();
- if (sdkFolder != null) {
- File file = new File(sdkFolder, relativePath);
- if (file.exists()) {
- return file;
- }
- }
-
- return null;
- }
-
- /**
- * Clears any lint markers from the given resource (project, folder or file)
- *
- * @param resource the resource to remove markers from
- */
- public static void clearMarkers(@NonNull IResource resource) {
- clearMarkers(Collections.singletonList(resource));
- }
-
- /** Clears any lint markers from the given list of resource (project, folder or file) */
- static void clearMarkers(List<? extends IResource> resources) {
- for (IResource resource : resources) {
- try {
- if (resource.isAccessible()) {
- resource.deleteMarkers(MARKER_LINT, false, IResource.DEPTH_INFINITE);
- }
- } catch (CoreException e) {
- AdtPlugin.log(e, null);
- }
- }
-
- IEditorPart activeEditor = AdtUtils.getActiveEditor();
- LayoutEditorDelegate delegate = LayoutEditorDelegate.fromEditor(activeEditor);
- if (delegate != null) {
- delegate.getGraphicalEditor().getLayoutActionBar().updateErrorIndicator();
- }
- }
-
- /**
- * Removes all markers of the given id from the given resource.
- *
- * @param resource the resource to remove markers from (file or project, or
- * null for all open projects)
- * @param id the id for the issue whose markers should be deleted
- */
- public static void removeMarkers(IResource resource, String id) {
- if (resource == null) {
- IJavaProject[] androidProjects = BaseProjectHelper.getAndroidProjects(null);
- for (IJavaProject project : androidProjects) {
- IProject p = project.getProject();
- if (p != null) {
- // Recurse, but with a different parameter so it will not continue recursing
- removeMarkers(p, id);
- }
- }
- return;
- }
- IMarker[] markers = getMarkers(resource);
- for (IMarker marker : markers) {
- if (id.equals(getId(marker))) {
- try {
- marker.delete();
- } catch (CoreException e) {
- AdtPlugin.log(e, null);
- }
- }
- }
- }
-
- /**
- * Returns the lint marker for the given resource (which may be a project, folder or file)
- *
- * @param resource the resource to be checked, typically a source file
- * @return an array of markers, possibly empty but never null
- */
- public static IMarker[] getMarkers(IResource resource) {
- try {
- if (resource.isAccessible()) {
- return resource.findMarkers(MARKER_LINT, false, IResource.DEPTH_INFINITE);
- }
- } catch (CoreException e) {
- AdtPlugin.log(e, null);
- }
-
- return new IMarker[0];
- }
-
- private static int getMarkerSeverity(Severity severity) {
- switch (severity) {
- case INFORMATIONAL:
- return IMarker.SEVERITY_INFO;
- case WARNING:
- return IMarker.SEVERITY_WARNING;
- case FATAL:
- case ERROR:
- default:
- return IMarker.SEVERITY_ERROR;
- }
- }
-
- private static Pair<Integer, Integer> getRange(IFile file, IDocument doc,
- Position startPosition, Position endPosition) {
- int startOffset = startPosition.getOffset();
- int endOffset = endPosition != null ? endPosition.getOffset() : -1;
- if (endOffset != -1) {
- // Attribute ranges often include trailing whitespace; trim this up
- if (doc == null) {
- IDocumentProvider provider = new TextFileDocumentProvider();
- try {
- provider.connect(file);
- doc = provider.getDocument(file);
- if (doc != null) {
- return adjustOffsets(doc, startOffset, endOffset);
- }
- } catch (Exception e) {
- AdtPlugin.log(e, "Can't find range information for %1$s", file.getName());
- } finally {
- provider.disconnect(file);
- }
- } else {
- return adjustOffsets(doc, startOffset, endOffset);
- }
- }
-
- return Pair.of(startOffset, startOffset);
- }
-
- /**
- * Trim off any trailing space on the given offset range in the given
- * document, and don't span multiple lines on ranges since it makes (for
- * example) the XML editor just glow with yellow underlines for all the
- * attributes etc. Highlighting just the element beginning gets the point
- * across. It also makes it more obvious where there are warnings on both
- * the overall element and on individual attributes since without this the
- * warnings on attributes would just overlap with the whole-element
- * highlighting.
- */
- private static Pair<Integer, Integer> adjustOffsets(IDocument doc, int startOffset,
- int endOffset) {
- int originalStart = startOffset;
- int originalEnd = endOffset;
-
- if (doc != null) {
- while (endOffset > startOffset && endOffset < doc.getLength()) {
- try {
- if (!Character.isWhitespace(doc.getChar(endOffset - 1))) {
- break;
- } else {
- endOffset--;
- }
- } catch (BadLocationException e) {
- // Pass - we've already validated offset range above
- break;
- }
- }
-
- // Also don't span lines
- int lineEnd = startOffset;
- while (lineEnd < endOffset) {
- try {
- char c = doc.getChar(lineEnd);
- if (c == '\n' || c == '\r') {
- endOffset = lineEnd;
- if (endOffset > 0 && doc.getChar(endOffset - 1) == '\r') {
- endOffset--;
- }
- break;
- }
- } catch (BadLocationException e) {
- // Pass - we've already validated offset range above
- break;
- }
- lineEnd++;
- }
- }
-
- if (startOffset >= endOffset) {
- // Selecting nothing (for example, for the mangled CRLF delimiter issue selecting
- // just the newline)
- // In that case, use the real range
- return Pair.of(originalStart, originalEnd);
- }
-
- return Pair.of(startOffset, endOffset);
- }
-
- /**
- * Returns true if a fatal error was encountered
- *
- * @return true if a fatal error was encountered
- */
- public boolean hasFatalErrors() {
- return mWasFatal;
- }
-
- /**
- * Describe the issue for the given marker
- *
- * @param marker the marker to look up
- * @return a full description of the corresponding issue, never null
- */
- public static String describe(IMarker marker) {
- IssueRegistry registry = getRegistry();
- String markerId = getId(marker);
- Issue issue = registry.getIssue(markerId);
- if (issue == null) {
- return "";
- }
-
- String summary = issue.getBriefDescription(TextFormat.TEXT);
- String explanation = issue.getExplanation(TextFormat.TEXT);
-
- StringBuilder sb = new StringBuilder(summary.length() + explanation.length() + 20);
- try {
- sb.append((String) marker.getAttribute(IMarker.MESSAGE));
- sb.append('\n').append('\n');
- } catch (CoreException e) {
- }
- sb.append("Issue: ");
- sb.append(summary);
- sb.append('\n');
- sb.append("Id: ");
- sb.append(issue.getId());
- sb.append('\n').append('\n');
- sb.append(explanation);
-
- if (issue.getMoreInfo() != null) {
- sb.append('\n').append('\n');
- sb.append(issue.getMoreInfo());
- }
-
- return sb.toString();
- }
-
- /**
- * Returns the id for the given marker
- *
- * @param marker the marker to look up
- * @return the corresponding issue id, or null
- */
- public static String getId(IMarker marker) {
- try {
- return (String) marker.getAttribute(MARKER_CHECKID_PROPERTY);
- } catch (CoreException e) {
- return null;
- }
- }
-
- /**
- * Shows the given marker in the editor
- *
- * @param marker the marker to be shown
- */
- public static void showMarker(IMarker marker) {
- IRegion region = null;
- try {
- int start = marker.getAttribute(IMarker.CHAR_START, -1);
- int end = marker.getAttribute(IMarker.CHAR_END, -1);
- if (start >= 0 && end >= 0) {
- region = new org.eclipse.jface.text.Region(start, end - start);
- }
-
- IResource resource = marker.getResource();
- if (resource instanceof IFile) {
- IEditorPart editor =
- AdtPlugin.openFile((IFile) resource, region, true /* showEditorTab */);
- if (editor != null) {
- IDE.gotoMarker(editor, marker);
- }
- }
- } catch (PartInitException ex) {
- AdtPlugin.log(ex, null);
- }
- }
-
- /**
- * Show a dialog with errors for the given file
- *
- * @param shell the parent shell to attach the dialog to
- * @param file the file to show the errors for
- * @param editor the editor for the file, if known
- */
- public static void showErrors(
- @NonNull Shell shell,
- @NonNull IFile file,
- @Nullable IEditorPart editor) {
- LintListDialog dialog = new LintListDialog(shell, file, editor);
- dialog.open();
- }
-
- @Override
- public @NonNull String readFile(@NonNull File f) {
- // Map File to IFile
- IFile file = AdtUtils.fileToIFile(f);
- if (file == null || !file.exists()) {
- String path = f.getPath();
- AdtPlugin.log(IStatus.ERROR, "Can't find file %1$s in workspace", path);
- return readPlainFile(f);
- }
-
- if (SdkUtils.endsWithIgnoreCase(file.getName(), DOT_XML)) {
- IStructuredModel model = null;
- try {
- IModelManager modelManager = StructuredModelManager.getModelManager();
- model = modelManager.getModelForRead(file);
- return model.getStructuredDocument().get();
- } catch (IOException e) {
- AdtPlugin.log(e, "Cannot read XML file");
- } catch (CoreException e) {
- AdtPlugin.log(e, null);
- } finally {
- if (model != null) {
- // TODO: This may be too early...
- model.releaseFromRead();
- }
- }
- }
-
- return readPlainFile(f);
- }
-
- private String readPlainFile(File file) {
- try {
- return LintUtils.getEncodedString(this, file);
- } catch (IOException e) {
- return ""; //$NON-NLS-1$
- }
- }
-
- private Map<Project, ClassPathInfo> mProjectInfo;
-
- @Override
- @NonNull
- protected ClassPathInfo getClassPath(@NonNull Project project) {
- ClassPathInfo info;
- if (mProjectInfo == null) {
- mProjectInfo = Maps.newHashMap();
- info = null;
- } else {
- info = mProjectInfo.get(project);
- }
-
- if (info == null) {
- List<File> sources = null;
- List<File> classes = null;
- List<File> libraries = null;
-
- IProject p = getProject(project);
- if (p != null) {
- try {
- IJavaProject javaProject = BaseProjectHelper.getJavaProject(p);
-
- // Output path
- File file = workspacePathToFile(javaProject.getOutputLocation());
- classes = Collections.singletonList(file);
-
- // Source path
- IClasspathEntry[] entries = javaProject.getRawClasspath();
- sources = new ArrayList<File>(entries.length);
- libraries = new ArrayList<File>(entries.length);
- for (int i = 0; i < entries.length; i++) {
- IClasspathEntry entry = entries[i];
- int kind = entry.getEntryKind();
-
- if (kind == IClasspathEntry.CPE_VARIABLE) {
- entry = JavaCore.getResolvedClasspathEntry(entry);
- if (entry == null) {
- // It's possible that the variable is no longer valid; ignore
- continue;
- }
- kind = entry.getEntryKind();
- }
-
- if (kind == IClasspathEntry.CPE_SOURCE) {
- sources.add(workspacePathToFile(entry.getPath()));
- } else if (kind == IClasspathEntry.CPE_LIBRARY) {
- libraries.add(entry.getPath().toFile());
- }
- // Note that we ignore IClasspathEntry.CPE_CONTAINER:
- // Normal Android Eclipse projects supply both
- // AdtConstants.CONTAINER_FRAMEWORK
- // and
- // AdtConstants.CONTAINER_LIBRARIES
- // here. We ignore the framework classes for obvious reasons,
- // but we also ignore the library container because lint will
- // process the libraries differently. When Eclipse builds a
- // project, it gets the .jar output of the library projects
- // from this container, which means it doesn't have to process
- // the library sources. Lint on the other hand wants to process
- // the source code, so instead it actually looks at the
- // project.properties file to find the libraries, and then it
- // iterates over all the library projects in turn and analyzes
- // those separately (but passing the main project for context,
- // such that the including project's manifest declarations
- // are used for data like minSdkVersion level).
- //
- // Note that this container will also contain *other*
- // libraries (Java libraries, not library projects) that we
- // *should* include. However, we can't distinguish these
- // class path entries from the library project jars,
- // so instead of looking at these, we simply listFiles() in
- // the libs/ folder after processing the classpath info
- }
-
- // Add in libraries
- File libs = new File(project.getDir(), FD_NATIVE_LIBS);
- if (libs.isDirectory()) {
- File[] jars = libs.listFiles();
- if (jars != null) {
- for (File jar : jars) {
- if (SdkUtils.endsWith(jar.getPath(), DOT_JAR)) {
- libraries.add(jar);
- }
- }
- }
- }
- } catch (CoreException e) {
- AdtPlugin.log(e, null);
- }
- }
-
- if (sources == null) {
- sources = super.getClassPath(project).getSourceFolders();
- }
- if (classes == null) {
- classes = super.getClassPath(project).getClassFolders();
- }
- if (libraries == null) {
- libraries = super.getClassPath(project).getLibraries();
- }
-
-
- // No test folders in Eclipse:
- // https://bugs.eclipse.org/bugs/show_bug.cgi?id=224708
- List<File> tests = Collections.emptyList();
-
- info = new ClassPathInfo(sources, classes, libraries, tests);
- mProjectInfo.put(project, info);
- }
-
- return info;
- }
-
- /**
- * Returns the registry of issues to check from within Eclipse.
- *
- * @return the issue registry to use to access detectors and issues
- */
- public static IssueRegistry getRegistry() {
- return new EclipseLintIssueRegistry();
- }
-
- @Override
- public @NonNull Class<? extends Detector> replaceDetector(
- @NonNull Class<? extends Detector> detectorClass) {
- return detectorClass;
- }
-
- @Override
- @NonNull
- public IAndroidTarget[] getTargets() {
- Sdk sdk = Sdk.getCurrent();
- if (sdk != null) {
- return sdk.getTargets();
- } else {
- return new IAndroidTarget[0];
- }
- }
-
- private boolean mSearchForSuperClasses;
-
- /**
- * Sets whether this client should search for super types on its own. This
- * is typically not needed when doing a full lint run (because lint will
- * look at all classes and libraries), but is useful during incremental
- * analysis when lint is only looking at a subset of classes. In that case,
- * we want to use Eclipse's data structures for super classes.
- *
- * @param search whether to use a custom Eclipse search for super class
- * names
- */
- public void setSearchForSuperClasses(boolean search) {
- mSearchForSuperClasses = search;
- }
-
- /**
- * Whether this lint client is searching for super types. See
- * {@link #setSearchForSuperClasses(boolean)} for details.
- *
- * @return whether the client will search for super types
- */
- public boolean getSearchForSuperClasses() {
- return mSearchForSuperClasses;
- }
-
- @Override
- @Nullable
- public String getSuperClass(@NonNull Project project, @NonNull String name) {
- if (!mSearchForSuperClasses) {
- // Super type search using the Eclipse index is potentially slow, so
- // only do this when necessary
- return null;
- }
-
- IProject eclipseProject = getProject(project);
- if (eclipseProject == null) {
- return null;
- }
-
- try {
- IJavaProject javaProject = BaseProjectHelper.getJavaProject(eclipseProject);
- if (javaProject == null) {
- return null;
- }
-
- String typeFqcn = ClassContext.getFqcn(name);
- IType type = javaProject.findType(typeFqcn);
- if (type != null) {
- ITypeHierarchy hierarchy = type.newSupertypeHierarchy(new NullProgressMonitor());
- IType superType = hierarchy.getSuperclass(type);
- if (superType != null) {
- String key = superType.getKey();
- if (!key.isEmpty()
- && key.charAt(0) == 'L'
- && key.charAt(key.length() - 1) == ';') {
- return key.substring(1, key.length() - 1);
- } else {
- String fqcn = superType.getFullyQualifiedName();
- return ClassContext.getInternalName(fqcn);
- }
- }
- }
- } catch (JavaModelException e) {
- log(Severity.INFORMATIONAL, e, null);
- } catch (CoreException e) {
- log(Severity.INFORMATIONAL, e, null);
- }
-
- return null;
- }
-
- @Override
- @Nullable
- public Boolean isSubclassOf(
- @NonNull Project project,
- @NonNull String name, @NonNull
- String superClassName) {
- if (!mSearchForSuperClasses) {
- // Super type search using the Eclipse index is potentially slow, so
- // only do this when necessary
- return null;
- }
-
- IProject eclipseProject = getProject(project);
- if (eclipseProject == null) {
- return null;
- }
-
- try {
- IJavaProject javaProject = BaseProjectHelper.getJavaProject(eclipseProject);
- if (javaProject == null) {
- return null;
- }
-
- String typeFqcn = ClassContext.getFqcn(name);
- IType type = javaProject.findType(typeFqcn);
- if (type != null) {
- ITypeHierarchy hierarchy = type.newSupertypeHierarchy(new NullProgressMonitor());
- IType[] allSupertypes = hierarchy.getAllSuperclasses(type);
- if (allSupertypes != null) {
- String target = 'L' + superClassName + ';';
- for (IType superType : allSupertypes) {
- if (target.equals(superType.getKey())) {
- return Boolean.TRUE;
- }
- }
- return Boolean.FALSE;
- }
- }
- } catch (JavaModelException e) {
- log(Severity.INFORMATIONAL, e, null);
- } catch (CoreException e) {
- log(Severity.INFORMATIONAL, e, null);
- }
-
- return null;
- }
-
- private static class LazyLocation extends Location implements Location.Handle {
- private final IStructuredDocument mDocument;
- private final IndexedRegion mRegion;
- private Position mStart;
- private Position mEnd;
-
- public LazyLocation(File file, IStructuredDocument document, IndexedRegion region) {
- super(file, null /*start*/, null /*end*/);
- mDocument = document;
- mRegion = region;
- }
-
- @Override
- public Position getStart() {
- if (mStart == null) {
- int line = -1;
- int column = -1;
- int offset = mRegion.getStartOffset();
-
- if (mRegion instanceof org.w3c.dom.Text && mDocument != null) {
- // For text nodes, skip whitespace prefix, if any
- for (int i = offset;
- i < mRegion.getEndOffset() && i < mDocument.getLength(); i++) {
- try {
- char c = mDocument.getChar(i);
- if (!Character.isWhitespace(c)) {
- offset = i;
- break;
- }
- } catch (BadLocationException e) {
- break;
- }
- }
- }
-
- if (mDocument != null && offset < mDocument.getLength()) {
- line = mDocument.getLineOfOffset(offset);
- column = -1;
- try {
- int lineOffset = mDocument.getLineOffset(line);
- column = offset - lineOffset;
- } catch (BadLocationException e) {
- AdtPlugin.log(e, null);
- }
- }
-
- mStart = new DefaultPosition(line, column, offset);
- }
-
- return mStart;
- }
-
- @Override
- public Position getEnd() {
- if (mEnd == null) {
- mEnd = new DefaultPosition(-1, -1, mRegion.getEndOffset());
- }
-
- return mEnd;
- }
-
- @Override
- public @NonNull Location resolve() {
- return this;
- }
- }
-
- private static class EclipseJavaParser extends JavaParser {
- private static final boolean USE_ECLIPSE_PARSER = true;
- private final Parser mParser;
-
- EclipseJavaParser() {
- if (USE_ECLIPSE_PARSER) {
- CompilerOptions options = new CompilerOptions();
- // Always using JDK 7 rather than basing it on project metadata since we
- // don't do compilation error validation in lint (we leave that to the IDE's
- // error parser or the command line build's compilation step); we want an
- // AST that is as tolerant as possible.
- options.complianceLevel = ClassFileConstants.JDK1_7;
- options.sourceLevel = ClassFileConstants.JDK1_7;
- options.targetJDK = ClassFileConstants.JDK1_7;
- options.parseLiteralExpressionsAsConstants = true;
- ProblemReporter problemReporter = new ProblemReporter(
- DefaultErrorHandlingPolicies.exitOnFirstError(),
- options,
- new DefaultProblemFactory());
- mParser = new Parser(problemReporter, options.parseLiteralExpressionsAsConstants);
- mParser.javadocParser.checkDocComment = false;
- } else {
- mParser = null;
- }
- }
-
- @Override
- public void prepareJavaParse(@NonNull List<JavaContext> contexts) {
- // TODO: Use batch compiler from lint-cli.jar
- }
-
- @Override
- public lombok.ast.Node parseJava(@NonNull JavaContext context) {
- if (USE_ECLIPSE_PARSER) {
- // Use Eclipse's compiler
- EcjTreeConverter converter = new EcjTreeConverter();
- String code = context.getContents();
-
- CompilationUnit sourceUnit = new CompilationUnit(code.toCharArray(),
- context.file.getName(), "UTF-8"); //$NON-NLS-1$
- CompilationResult compilationResult = new CompilationResult(sourceUnit, 0, 0, 0);
- CompilationUnitDeclaration unit = null;
- try {
- unit = mParser.parse(sourceUnit, compilationResult);
- } catch (AbortCompilation e) {
- // No need to report Java parsing errors while running in Eclipse.
- // Eclipse itself will already provide problem markers for these files,
- // so all this achieves is creating "multiple annotations on this line"
- // tooltips instead.
- return null;
- }
- if (unit == null) {
- return null;
- }
-
- try {
- converter.visit(code, unit);
- List<? extends lombok.ast.Node> nodes = converter.getAll();
-
- // There could be more than one node when there are errors; pick out the
- // compilation unit node
- for (lombok.ast.Node node : nodes) {
- if (node instanceof lombok.ast.CompilationUnit) {
- return node;
- }
- }
-
- return null;
- } catch (Throwable t) {
- AdtPlugin.log(t, "Failed converting ECJ parse tree to Lombok for file %1$s",
- context.file.getPath());
- return null;
- }
- } else {
- // Use Lombok for now
- Source source = new Source(context.getContents(), context.file.getName());
- List<lombok.ast.Node> nodes = source.getNodes();
-
- // Don't analyze files containing errors
- List<ParseProblem> problems = source.getProblems();
- if (problems != null && problems.size() > 0) {
- /* Silently ignore the errors. There are still some bugs in Lombok/Parboiled
- * (triggered if you run lint on the AOSP framework directory for example),
- * and having these show up as fatal errors when it's really a tool bug
- * is bad. To make matters worse, the error messages aren't clear:
- * http://code.google.com/p/projectlombok/issues/detail?id=313
- for (ParseProblem problem : problems) {
- lombok.ast.Position position = problem.getPosition();
- Location location = Location.create(context.file,
- context.getContents(), position.getStart(), position.getEnd());
- String message = problem.getMessage();
- context.report(
- IssueRegistry.PARSER_ERROR, location,
- message,
- null);
-
- }
- */
- return null;
- }
-
- // There could be more than one node when there are errors; pick out the
- // compilation unit node
- for (lombok.ast.Node node : nodes) {
- if (node instanceof lombok.ast.CompilationUnit) {
- return node;
- }
- }
- return null;
- }
- }
-
- @Override
- public @NonNull Location getLocation(@NonNull JavaContext context,
- @NonNull lombok.ast.Node node) {
- lombok.ast.Position position = node.getPosition();
- return Location.create(context.file, context.getContents(),
- position.getStart(), position.getEnd());
- }
-
- @Override
- public @NonNull Handle createLocationHandle(@NonNull JavaContext context,
- @NonNull lombok.ast.Node node) {
- return new LocationHandle(context.file, node);
- }
-
- @Override
- public void dispose(@NonNull JavaContext context,
- @NonNull lombok.ast.Node compilationUnit) {
- }
-
- @Override
- @Nullable
- public ResolvedNode resolve(@NonNull JavaContext context,
- @NonNull lombok.ast.Node node) {
- return null;
- }
-
- @Override
- @Nullable
- public TypeDescriptor getType(@NonNull JavaContext context,
- @NonNull lombok.ast.Node node) {
- return null;
- }
-
- /* Handle for creating positions cheaply and returning full fledged locations later */
- private class LocationHandle implements Handle {
- private File mFile;
- private lombok.ast.Node mNode;
- private Object mClientData;
-
- public LocationHandle(File file, lombok.ast.Node node) {
- mFile = file;
- mNode = node;
- }
-
- @Override
- public @NonNull Location resolve() {
- lombok.ast.Position pos = mNode.getPosition();
- return Location.create(mFile, null /*contents*/, pos.getStart(), pos.getEnd());
- }
-
- @Override
- public void setClientData(@Nullable Object clientData) {
- mClientData = clientData;
- }
-
- @Override
- @Nullable
- public Object getClientData() {
- return mClientData;
- }
- }
- }
-}
-