diff options
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.java | 1306 |
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; - } - } - } -} - |