1a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye/*
2a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye * Copyright (C) 2011 The Android Open Source Project
3a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye *
4a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye * Licensed under the Eclipse Public License, Version 1.0 (the "License");
5a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye * you may not use this file except in compliance with the License.
6a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye * You may obtain a copy of the License at
7a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye *
8a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye *      http://www.eclipse.org/org/documents/epl-v10.php
9a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye *
10a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye * Unless required by applicable law or agreed to in writing, software
11a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye * distributed under the License is distributed on an "AS IS" BASIS,
12a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye * See the License for the specific language governing permissions and
14a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye * limitations under the License.
15a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye */
16a85107f7d763276a5a040cf68e2046ac54202015Tor Norbyepackage com.android.ide.eclipse.adt.internal.lint;
17a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye
1812d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.DOT_JAR;
1912d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.DOT_XML;
201fb460987f7d832adf12290f41448d0c16a95972Siva Velusamyimport static com.android.SdkConstants.FD_NATIVE_LIBS;
21a85107f7d763276a5a040cf68e2046ac54202015Tor Norbyeimport static com.android.ide.eclipse.adt.AdtConstants.MARKER_LINT;
224ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbyeimport static com.android.ide.eclipse.adt.AdtUtils.workspacePathToFile;
23a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye
245dc20a5c893caa37deff6b2f9f928e571027a137Tor Norbyeimport com.android.annotations.NonNull;
255dc20a5c893caa37deff6b2f9f928e571027a137Tor Norbyeimport com.android.annotations.Nullable;
26a85107f7d763276a5a040cf68e2046ac54202015Tor Norbyeimport com.android.ide.eclipse.adt.AdtPlugin;
27a85107f7d763276a5a040cf68e2046ac54202015Tor Norbyeimport com.android.ide.eclipse.adt.AdtUtils;
28e50549f54810c29bffc681e39d33158ab2a8f26eRaphaelimport com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate;
295ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbyeimport com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode;
305dc20a5c893caa37deff6b2f9f928e571027a137Tor Norbyeimport com.android.ide.eclipse.adt.internal.preferences.AdtPrefs;
31a85107f7d763276a5a040cf68e2046ac54202015Tor Norbyeimport com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
32b52aeca0f2e150d7e7e8ceffa897a7ae1f938724Tor Norbyeimport com.android.ide.eclipse.adt.internal.sdk.Sdk;
33b52aeca0f2e150d7e7e8ceffa897a7ae1f938724Tor Norbyeimport com.android.sdklib.IAndroidTarget;
34229581314076be1b6f82fe1efed2bd00da340899Tor Norbyeimport com.android.tools.lint.checks.BuiltinIssueRegistry;
35229581314076be1b6f82fe1efed2bd00da340899Tor Norbyeimport com.android.tools.lint.client.api.Configuration;
36229581314076be1b6f82fe1efed2bd00da340899Tor Norbyeimport com.android.tools.lint.client.api.IDomParser;
3714312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbyeimport com.android.tools.lint.client.api.IJavaParser;
38229581314076be1b6f82fe1efed2bd00da340899Tor Norbyeimport com.android.tools.lint.client.api.IssueRegistry;
39229581314076be1b6f82fe1efed2bd00da340899Tor Norbyeimport com.android.tools.lint.client.api.LintClient;
40a85107f7d763276a5a040cf68e2046ac54202015Tor Norbyeimport com.android.tools.lint.detector.api.Context;
413ce45b249f898697ae82e8c6dd045966227f3438Tor Norbyeimport com.android.tools.lint.detector.api.DefaultPosition;
428853b3b92496c5734bbcb75499030bb51a060097Tor Norbyeimport com.android.tools.lint.detector.api.Detector;
43a85107f7d763276a5a040cf68e2046ac54202015Tor Norbyeimport com.android.tools.lint.detector.api.Issue;
4414312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbyeimport com.android.tools.lint.detector.api.JavaContext;
450fe590d956435c3c6a2d4c802108f627b173a573Tor Norbyeimport com.android.tools.lint.detector.api.LintUtils;
46a85107f7d763276a5a040cf68e2046ac54202015Tor Norbyeimport com.android.tools.lint.detector.api.Location;
473ce45b249f898697ae82e8c6dd045966227f3438Tor Norbyeimport com.android.tools.lint.detector.api.Location.Handle;
48a85107f7d763276a5a040cf68e2046ac54202015Tor Norbyeimport com.android.tools.lint.detector.api.Position;
49229581314076be1b6f82fe1efed2bd00da340899Tor Norbyeimport com.android.tools.lint.detector.api.Project;
50a85107f7d763276a5a040cf68e2046ac54202015Tor Norbyeimport com.android.tools.lint.detector.api.Severity;
513ce45b249f898697ae82e8c6dd045966227f3438Tor Norbyeimport com.android.tools.lint.detector.api.XmlContext;
5285e4a1a9dd133abb879ec211ce8dd385004edf22Xavier Ducrohetimport com.android.utils.Pair;
531fb460987f7d832adf12290f41448d0c16a95972Siva Velusamyimport com.android.utils.SdkUtils;
544ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbyeimport com.google.common.collect.Maps;
55a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye
56a85107f7d763276a5a040cf68e2046ac54202015Tor Norbyeimport org.eclipse.core.resources.IFile;
57a85107f7d763276a5a040cf68e2046ac54202015Tor Norbyeimport org.eclipse.core.resources.IMarker;
587b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbyeimport org.eclipse.core.resources.IProject;
59a85107f7d763276a5a040cf68e2046ac54202015Tor Norbyeimport org.eclipse.core.resources.IResource;
60a85107f7d763276a5a040cf68e2046ac54202015Tor Norbyeimport org.eclipse.core.runtime.CoreException;
61a85107f7d763276a5a040cf68e2046ac54202015Tor Norbyeimport org.eclipse.core.runtime.IStatus;
624ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbyeimport org.eclipse.jdt.core.IClasspathEntry;
637b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbyeimport org.eclipse.jdt.core.IJavaProject;
644ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbyeimport org.eclipse.jdt.core.JavaCore;
6514312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbyeimport org.eclipse.jdt.internal.compiler.CompilationResult;
6614312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbyeimport org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies;
6714312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbyeimport org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
6814312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbyeimport org.eclipse.jdt.internal.compiler.batch.CompilationUnit;
6914312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbyeimport org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
7014312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbyeimport org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
7114312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbyeimport org.eclipse.jdt.internal.compiler.parser.Parser;
7214312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbyeimport org.eclipse.jdt.internal.compiler.problem.AbortCompilation;
7314312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbyeimport org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory;
7414312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbyeimport org.eclipse.jdt.internal.compiler.problem.ProblemReporter;
75a85107f7d763276a5a040cf68e2046ac54202015Tor Norbyeimport org.eclipse.jface.text.BadLocationException;
76a85107f7d763276a5a040cf68e2046ac54202015Tor Norbyeimport org.eclipse.jface.text.IDocument;
777b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbyeimport org.eclipse.jface.text.IRegion;
78a85107f7d763276a5a040cf68e2046ac54202015Tor Norbyeimport org.eclipse.swt.widgets.Shell;
79a85107f7d763276a5a040cf68e2046ac54202015Tor Norbyeimport org.eclipse.ui.IEditorPart;
807b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbyeimport org.eclipse.ui.PartInitException;
81a85107f7d763276a5a040cf68e2046ac54202015Tor Norbyeimport org.eclipse.ui.editors.text.TextFileDocumentProvider;
82b45957a134d6fd6184348387fd0f0b14ffa7021cTor Norbyeimport org.eclipse.ui.ide.IDE;
83a85107f7d763276a5a040cf68e2046ac54202015Tor Norbyeimport org.eclipse.ui.texteditor.IDocumentProvider;
84a85107f7d763276a5a040cf68e2046ac54202015Tor Norbyeimport org.eclipse.wst.sse.core.StructuredModelManager;
85a85107f7d763276a5a040cf68e2046ac54202015Tor Norbyeimport org.eclipse.wst.sse.core.internal.provisional.IModelManager;
86a85107f7d763276a5a040cf68e2046ac54202015Tor Norbyeimport org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
87a85107f7d763276a5a040cf68e2046ac54202015Tor Norbyeimport org.eclipse.wst.sse.core.internal.provisional.IndexedRegion;
88a85107f7d763276a5a040cf68e2046ac54202015Tor Norbyeimport org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
89a85107f7d763276a5a040cf68e2046ac54202015Tor Norbyeimport org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel;
905ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbyeimport org.w3c.dom.Attr;
91a85107f7d763276a5a040cf68e2046ac54202015Tor Norbyeimport org.w3c.dom.Document;
92a85107f7d763276a5a040cf68e2046ac54202015Tor Norbyeimport org.w3c.dom.Node;
93a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye
948853b3b92496c5734bbcb75499030bb51a060097Tor Norbyeimport java.io.File;
95a85107f7d763276a5a040cf68e2046ac54202015Tor Norbyeimport java.io.IOException;
964ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbyeimport java.util.ArrayList;
975ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbyeimport java.util.Collection;
984c68f3dc9f10b76f1a32ff9c86587adca385a6d7Tor Norbyeimport java.util.Collections;
994c68f3dc9f10b76f1a32ff9c86587adca385a6d7Tor Norbyeimport java.util.List;
1005ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbyeimport java.util.Map;
1015ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbyeimport java.util.WeakHashMap;
102a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye
10314312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbyeimport lombok.ast.ecj.EcjTreeConverter;
10414312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbyeimport lombok.ast.grammar.ParseProblem;
10514312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbyeimport lombok.ast.grammar.Source;
10614312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye
107a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye/**
108a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye * Eclipse implementation for running lint on workspace files and projects.
109a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye */
110a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye@SuppressWarnings("restriction") // DOM model
111229581314076be1b6f82fe1efed2bd00da340899Tor Norbyepublic class EclipseLintClient extends LintClient implements IDomParser {
112a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye    static final String MARKER_CHECKID_PROPERTY = "checkid";    //$NON-NLS-1$
113c8e42c3bfdef4941696155e5b333dd6d4075bbf0Tor Norbye    private static final String MODEL_PROPERTY = "model";       //$NON-NLS-1$
1144c68f3dc9f10b76f1a32ff9c86587adca385a6d7Tor Norbye    private final List<? extends IResource> mResources;
115a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye    private final IDocument mDocument;
116229581314076be1b6f82fe1efed2bd00da340899Tor Norbye    private boolean mWasFatal;
117229581314076be1b6f82fe1efed2bd00da340899Tor Norbye    private boolean mFatalOnly;
11814312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye    private EclipseJavaParser mJavaParser;
1195ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye    private boolean mCollectNodes;
1205ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye    private Map<Node, IMarker> mNodeMap;
121a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye
122a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye    /**
123229581314076be1b6f82fe1efed2bd00da340899Tor Norbye     * Creates a new {@link EclipseLintClient}.
124a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye     *
125a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye     * @param registry the associated detector registry
1264c68f3dc9f10b76f1a32ff9c86587adca385a6d7Tor Norbye     * @param resources the associated resources (project, file or null)
127a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye     * @param document the associated document, or null if the {@code resource}
128a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye     *            param is not a file
129229581314076be1b6f82fe1efed2bd00da340899Tor Norbye     * @param fatalOnly whether only fatal issues should be reported (and therefore checked)
130a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye     */
1314c68f3dc9f10b76f1a32ff9c86587adca385a6d7Tor Norbye    public EclipseLintClient(IssueRegistry registry, List<? extends IResource> resources,
1324c68f3dc9f10b76f1a32ff9c86587adca385a6d7Tor Norbye            IDocument document, boolean fatalOnly) {
1334c68f3dc9f10b76f1a32ff9c86587adca385a6d7Tor Norbye        mResources = resources;
134a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        mDocument = document;
135229581314076be1b6f82fe1efed2bd00da340899Tor Norbye        mFatalOnly = fatalOnly;
136a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye    }
137a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye
1385ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye    /**
1395ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye     * Returns true if lint should only check fatal issues
1405ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye     *
1415ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye     * @return true if lint should only check fatal issues
1425ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye     */
1435ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye    public boolean isFatalOnly() {
1445ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye        return mFatalOnly;
1455ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye    }
1465ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye
1475ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye    /**
1485ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye     * Sets whether the lint client should store associated XML nodes for each
1495ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye     * reported issue
1505ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye     *
1515ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye     * @param collectNodes if true, collect node positions for errors in XML
1525ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye     *            files, retrievable via the {@link #getIssueForNode} method
1535ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye     */
1545ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye    public void setCollectNodes(boolean collectNodes) {
1555ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye        mCollectNodes = collectNodes;
1565ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye    }
1575ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye
1585ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye    /**
1595ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye     * Returns one of the issues for the given node (there could be more than one)
1605ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye     *
1615ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye     * @param node the node to look up lint issues for
1625ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye     * @return the marker for one of the issues found for the given node
1635ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye     */
1645ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye    @Nullable
1655ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye    public IMarker getIssueForNode(@NonNull UiViewElementNode node) {
1665ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye        if (mNodeMap != null) {
1675ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye            return mNodeMap.get(node.getXmlNode());
1685ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye        }
1695ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye
1705ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye        return null;
1715ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye    }
1725ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye
1735ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye    /**
1745ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye     * Returns a collection of nodes that have one or more lint warnings
1755ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye     * associated with them (retrievable via
1765ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye     * {@link #getIssueForNode(UiViewElementNode)})
1775ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye     *
1785ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye     * @return a collection of nodes, which should <b>not</b> be modified by the
1795ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye     *         caller
1805ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye     */
1815ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye    @Nullable
1825ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye    public Collection<Node> getIssueNodes() {
1835ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye        if (mNodeMap != null) {
1845ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye            return mNodeMap.keySet();
1855ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye        }
1865ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye
1875ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye        return null;
1885ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye    }
1895ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye
190229581314076be1b6f82fe1efed2bd00da340899Tor Norbye    // ----- Extends LintClient -----
191a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye
1928853b3b92496c5734bbcb75499030bb51a060097Tor Norbye    @Override
1937e4b8e9d595e45baa9d87cdb8282f02759e73abcTor Norbye    public void log(@NonNull Severity severity, @Nullable Throwable exception,
1947e4b8e9d595e45baa9d87cdb8282f02759e73abcTor Norbye            @Nullable String format, @Nullable Object... args) {
195a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        if (exception == null) {
196a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye            AdtPlugin.log(IStatus.WARNING, format, args);
197a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        } else {
198a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye            AdtPlugin.log(exception, format, args);
199a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        }
200a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye    }
201a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye
2028853b3b92496c5734bbcb75499030bb51a060097Tor Norbye    @Override
2033ce45b249f898697ae82e8c6dd045966227f3438Tor Norbye    public IDomParser getDomParser() {
204a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        return this;
205a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye    }
206a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye
20714312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye    @Override
20814312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye    public IJavaParser getJavaParser() {
20914312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye        if (mJavaParser == null) {
21014312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye            mJavaParser = new EclipseJavaParser();
21114312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye        }
21214312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye
21314312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye        return mJavaParser;
21414312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye    }
21514312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye
216a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye    // ----- Implements IDomParser -----
2178853b3b92496c5734bbcb75499030bb51a060097Tor Norbye
218ab36f4e7488358dea4ab6b54ee2b7bef3da0232bTor Norbye    @Override
2197e4b8e9d595e45baa9d87cdb8282f02759e73abcTor Norbye    public Document parseXml(@NonNull XmlContext context) {
220a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        // Map File to IFile
221a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        IFile file = AdtUtils.fileToIFile(context.file);
222a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        if (file == null || !file.exists()) {
223a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye            String path = context.file.getPath();
224a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye            AdtPlugin.log(IStatus.ERROR, "Can't find file %1$s in workspace", path);
225a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye            return null;
226a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        }
227a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye
228a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        IStructuredModel model = null;
229a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        try {
230a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye            IModelManager modelManager = StructuredModelManager.getModelManager();
231ec0dfbb255330df32eaafb5d785af458ef9a660cTor Norbye            if (modelManager == null) {
232ec0dfbb255330df32eaafb5d785af458ef9a660cTor Norbye                // This can happen if incremental lint is running right as Eclipse is shutting down
233ec0dfbb255330df32eaafb5d785af458ef9a660cTor Norbye                return null;
234ec0dfbb255330df32eaafb5d785af458ef9a660cTor Norbye            }
235a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye            model = modelManager.getModelForRead(file);
236a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye            if (model instanceof IDOMModel) {
237c8e42c3bfdef4941696155e5b333dd6d4075bbf0Tor Norbye                context.setProperty(MODEL_PROPERTY, model);
238a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye                IDOMModel domModel = (IDOMModel) model;
239a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye                return domModel.getDocument();
240a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye            }
241a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        } catch (IOException e) {
242a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye            AdtPlugin.log(e, "Cannot read XML file");
243a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        } catch (CoreException e) {
244a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye            AdtPlugin.log(e, null);
245a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        }
246a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye
247a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        return null;
248a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye    }
249a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye
2504ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye    // Cache for {@link getProject}
2514ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye    private IProject mLastEclipseProject;
2524ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye    private Project mLastLintProject;
2534ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye
2544c68f3dc9f10b76f1a32ff9c86587adca385a6d7Tor Norbye    private IProject getProject(Project project) {
2554ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye        if (project == mLastLintProject) {
2564ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye            return mLastEclipseProject;
2574ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye        }
2584ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye
2594ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye        mLastLintProject = project;
2604ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye        mLastEclipseProject = null;
2614ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye
2624c68f3dc9f10b76f1a32ff9c86587adca385a6d7Tor Norbye        if (mResources != null) {
2634c68f3dc9f10b76f1a32ff9c86587adca385a6d7Tor Norbye            if (mResources.size() == 1) {
2644ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                IProject p = mResources.get(0).getProject();
2654ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                mLastEclipseProject = p;
2664ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                return p;
2674c68f3dc9f10b76f1a32ff9c86587adca385a6d7Tor Norbye            }
2684c68f3dc9f10b76f1a32ff9c86587adca385a6d7Tor Norbye
2694ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye            IProject last = null;
2704c68f3dc9f10b76f1a32ff9c86587adca385a6d7Tor Norbye            for (IResource resource : mResources) {
2714c68f3dc9f10b76f1a32ff9c86587adca385a6d7Tor Norbye                IProject p = resource.getProject();
2724ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                if (p != last) {
2734ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                    if (project.getDir().equals(AdtUtils.getAbsolutePath(p).toFile())) {
2744ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                        mLastEclipseProject = p;
2754ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                        return p;
2764ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                    }
2774ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                    last = p;
2784c68f3dc9f10b76f1a32ff9c86587adca385a6d7Tor Norbye                }
2794c68f3dc9f10b76f1a32ff9c86587adca385a6d7Tor Norbye            }
2804c68f3dc9f10b76f1a32ff9c86587adca385a6d7Tor Norbye        }
2814ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye
2824c68f3dc9f10b76f1a32ff9c86587adca385a6d7Tor Norbye        return null;
2834c68f3dc9f10b76f1a32ff9c86587adca385a6d7Tor Norbye    }
2844c68f3dc9f10b76f1a32ff9c86587adca385a6d7Tor Norbye
2855ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye    @NonNull
2868853b3b92496c5734bbcb75499030bb51a060097Tor Norbye    @Override
2877e4b8e9d595e45baa9d87cdb8282f02759e73abcTor Norbye    public Configuration getConfiguration(@NonNull Project project) {
2887e4b8e9d595e45baa9d87cdb8282f02759e73abcTor Norbye        return getConfigurationFor(project);
2897e4b8e9d595e45baa9d87cdb8282f02759e73abcTor Norbye    }
2907e4b8e9d595e45baa9d87cdb8282f02759e73abcTor Norbye
2917e4b8e9d595e45baa9d87cdb8282f02759e73abcTor Norbye    /**
2927e4b8e9d595e45baa9d87cdb8282f02759e73abcTor Norbye     * Same as {@link #getConfiguration(Project)}, but {@code project} can be
2937e4b8e9d595e45baa9d87cdb8282f02759e73abcTor Norbye     * null in which case the global configuration is returned.
2947e4b8e9d595e45baa9d87cdb8282f02759e73abcTor Norbye     *
2957e4b8e9d595e45baa9d87cdb8282f02759e73abcTor Norbye     * @param project the project to look up
2967e4b8e9d595e45baa9d87cdb8282f02759e73abcTor Norbye     * @return a corresponding configuration
2977e4b8e9d595e45baa9d87cdb8282f02759e73abcTor Norbye     */
2987e4b8e9d595e45baa9d87cdb8282f02759e73abcTor Norbye    @NonNull
2997e4b8e9d595e45baa9d87cdb8282f02759e73abcTor Norbye    public Configuration getConfigurationFor(@Nullable Project project) {
300105dc49ddd885c07b520dd343aa326c306d2c3deTor Norbye        if (project != null) {
301105dc49ddd885c07b520dd343aa326c306d2c3deTor Norbye            IProject eclipseProject = getProject(project);
302105dc49ddd885c07b520dd343aa326c306d2c3deTor Norbye            if (eclipseProject != null) {
303105dc49ddd885c07b520dd343aa326c306d2c3deTor Norbye                return ProjectLintConfiguration.get(this, eclipseProject, mFatalOnly);
304a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye            }
305a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        }
306105dc49ddd885c07b520dd343aa326c306d2c3deTor Norbye
307105dc49ddd885c07b520dd343aa326c306d2c3deTor Norbye        return GlobalLintConfiguration.get();
308a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye    }
3098853b3b92496c5734bbcb75499030bb51a060097Tor Norbye    @Override
3107e4b8e9d595e45baa9d87cdb8282f02759e73abcTor Norbye    public void report(@NonNull Context context, @NonNull Issue issue, @NonNull Severity s,
3117e4b8e9d595e45baa9d87cdb8282f02759e73abcTor Norbye            @Nullable Location location,
3127e4b8e9d595e45baa9d87cdb8282f02759e73abcTor Norbye            @NonNull String message, @Nullable Object data) {
313a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        int severity = getMarkerSeverity(s);
314a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        IMarker marker = null;
315402af1286193f536946b36979b259511d6a9bb06Tor Norbye        if (location != null) {
316a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye            Position startPosition = location.getStart();
317a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye            if (startPosition == null) {
3188853b3b92496c5734bbcb75499030bb51a060097Tor Norbye                if (location.getFile() != null) {
319402af1286193f536946b36979b259511d6a9bb06Tor Norbye                    IResource resource = AdtUtils.fileToResource(location.getFile());
320402af1286193f536946b36979b259511d6a9bb06Tor Norbye                    if (resource != null && resource.isAccessible()) {
321402af1286193f536946b36979b259511d6a9bb06Tor Norbye                        marker = BaseProjectHelper.markResource(resource, MARKER_LINT,
322402af1286193f536946b36979b259511d6a9bb06Tor Norbye                                message, 0, severity);
323402af1286193f536946b36979b259511d6a9bb06Tor Norbye                    }
3248853b3b92496c5734bbcb75499030bb51a060097Tor Norbye                }
325a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye            } else {
326a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye                Position endPosition = location.getEnd();
327a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye                int line = startPosition.getLine() + 1; // Marker API is 1-based
328a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye                IFile file = AdtUtils.fileToIFile(location.getFile());
329402af1286193f536946b36979b259511d6a9bb06Tor Norbye                if (file != null && file.isAccessible()) {
330a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye                    Pair<Integer, Integer> r = getRange(file, mDocument,
331a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye                            startPosition, endPosition);
332a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye                    int startOffset = r.getFirst();
333a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye                    int endOffset = r.getSecond();
334a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye                    marker = BaseProjectHelper.markResource(file, MARKER_LINT,
335a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye                            message, line, startOffset, endOffset, severity);
336a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye                }
337a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye            }
338a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        }
339a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye
340402af1286193f536946b36979b259511d6a9bb06Tor Norbye        if (marker == null) {
3414c68f3dc9f10b76f1a32ff9c86587adca385a6d7Tor Norbye            marker = BaseProjectHelper.markResource(mResources.get(0), MARKER_LINT,
342402af1286193f536946b36979b259511d6a9bb06Tor Norbye                        message, 0, severity);
343402af1286193f536946b36979b259511d6a9bb06Tor Norbye        }
344402af1286193f536946b36979b259511d6a9bb06Tor Norbye
345a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        if (marker != null) {
346a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye            // Store marker id such that we can recognize it from the suppress quickfix
347a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye            try {
348a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye                marker.setAttribute(MARKER_CHECKID_PROPERTY, issue.getId());
349a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye            } catch (CoreException e) {
350a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye                AdtPlugin.log(e, null);
351a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye            }
352a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        }
353a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye
3545da3610b249d74267a67a6b48acdf4cc95475bc0Tor Norbye        if (s == Severity.FATAL) {
355229581314076be1b6f82fe1efed2bd00da340899Tor Norbye            mWasFatal = true;
356a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        }
3575ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye
3585ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye        if (mCollectNodes && location != null && marker != null) {
3595ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye            if (location instanceof LazyLocation) {
3605ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye                LazyLocation l = (LazyLocation) location;
3615ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye                IndexedRegion region = l.mRegion;
3625ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye                if (region instanceof Node) {
3635ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye                    Node node = (Node) region;
3645ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye                    if (node instanceof Attr) {
3655ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye                        node = ((Attr) node).getOwnerElement();
3665ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye                    }
3675ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye                    if (mNodeMap == null) {
3685ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye                        mNodeMap = new WeakHashMap<Node, IMarker>();
3695ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye                    }
3705ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye                    IMarker prev = mNodeMap.get(node);
3715ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye                    if (prev != null) {
3725ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye                        // Only replace the node if this node has higher priority
3735ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye                        int prevSeverity = prev.getAttribute(IMarker.SEVERITY, 0);
3745ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye                        if (prevSeverity < severity) {
3755ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye                            mNodeMap.put(node, marker);
3765ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye                        }
3775ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye                    } else {
3785ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye                        mNodeMap.put(node, marker);
3795ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye                    }
3805ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye                }
3815ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye            }
3825ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye        }
383a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye    }
384a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye
3855dc20a5c893caa37deff6b2f9f928e571027a137Tor Norbye    @Override
3865dc20a5c893caa37deff6b2f9f928e571027a137Tor Norbye    @Nullable
3875dc20a5c893caa37deff6b2f9f928e571027a137Tor Norbye    public File findResource(@NonNull String relativePath) {
388cb3bddedd22155d23f741e764168174c3e223807Tor Norbye        // Look within the $ANDROID_SDK
3895dc20a5c893caa37deff6b2f9f928e571027a137Tor Norbye        String sdkFolder = AdtPrefs.getPrefs().getOsSdkFolder();
3905dc20a5c893caa37deff6b2f9f928e571027a137Tor Norbye        if (sdkFolder != null) {
391cb3bddedd22155d23f741e764168174c3e223807Tor Norbye            File file = new File(sdkFolder, relativePath);
3925dc20a5c893caa37deff6b2f9f928e571027a137Tor Norbye            if (file.exists()) {
3935dc20a5c893caa37deff6b2f9f928e571027a137Tor Norbye                return file;
3945dc20a5c893caa37deff6b2f9f928e571027a137Tor Norbye            }
3955dc20a5c893caa37deff6b2f9f928e571027a137Tor Norbye        }
3965dc20a5c893caa37deff6b2f9f928e571027a137Tor Norbye
3975dc20a5c893caa37deff6b2f9f928e571027a137Tor Norbye        return null;
3985dc20a5c893caa37deff6b2f9f928e571027a137Tor Norbye    }
3995dc20a5c893caa37deff6b2f9f928e571027a137Tor Norbye
4009105cd9318152b97845ad0255d921e7921715deaTor Norbye    /**
4019105cd9318152b97845ad0255d921e7921715deaTor Norbye     * Clears any lint markers from the given resource (project, folder or file)
4029105cd9318152b97845ad0255d921e7921715deaTor Norbye     *
4039105cd9318152b97845ad0255d921e7921715deaTor Norbye     * @param resource the resource to remove markers from
4049105cd9318152b97845ad0255d921e7921715deaTor Norbye     */
4059105cd9318152b97845ad0255d921e7921715deaTor Norbye    public static void clearMarkers(@NonNull IResource resource) {
4064c68f3dc9f10b76f1a32ff9c86587adca385a6d7Tor Norbye        clearMarkers(Collections.singletonList(resource));
4074c68f3dc9f10b76f1a32ff9c86587adca385a6d7Tor Norbye    }
4084c68f3dc9f10b76f1a32ff9c86587adca385a6d7Tor Norbye
4094c68f3dc9f10b76f1a32ff9c86587adca385a6d7Tor Norbye    /** Clears any lint markers from the given list of resource (project, folder or file) */
4104c68f3dc9f10b76f1a32ff9c86587adca385a6d7Tor Norbye    static void clearMarkers(List<? extends IResource> resources) {
4114c68f3dc9f10b76f1a32ff9c86587adca385a6d7Tor Norbye        for (IResource resource : resources) {
4124c68f3dc9f10b76f1a32ff9c86587adca385a6d7Tor Norbye            try {
4134c68f3dc9f10b76f1a32ff9c86587adca385a6d7Tor Norbye                if (resource.isAccessible()) {
4144c68f3dc9f10b76f1a32ff9c86587adca385a6d7Tor Norbye                    resource.deleteMarkers(MARKER_LINT, false, IResource.DEPTH_INFINITE);
4154c68f3dc9f10b76f1a32ff9c86587adca385a6d7Tor Norbye                }
4164c68f3dc9f10b76f1a32ff9c86587adca385a6d7Tor Norbye            } catch (CoreException e) {
4174c68f3dc9f10b76f1a32ff9c86587adca385a6d7Tor Norbye                AdtPlugin.log(e, null);
4184c68f3dc9f10b76f1a32ff9c86587adca385a6d7Tor Norbye            }
419a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        }
420a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye
4217e4b8e9d595e45baa9d87cdb8282f02759e73abcTor Norbye        IEditorPart activeEditor = AdtUtils.getActiveEditor();
4227e4b8e9d595e45baa9d87cdb8282f02759e73abcTor Norbye        LayoutEditorDelegate delegate = LayoutEditorDelegate.fromEditor(activeEditor);
423e50549f54810c29bffc681e39d33158ab2a8f26eRaphael        if (delegate != null) {
424e50549f54810c29bffc681e39d33158ab2a8f26eRaphael            delegate.getGraphicalEditor().getLayoutActionBar().updateErrorIndicator();
425a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        }
426a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye    }
427a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye
428a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye    /**
4297b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye     * Removes all markers of the given id from the given resource.
4307b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye     *
4317b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye     * @param resource the resource to remove markers from (file or project, or
4327b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye     *            null for all open projects)
4337b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye     * @param id the id for the issue whose markers should be deleted
4347b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye     */
4357b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye    public static void removeMarkers(IResource resource, String id) {
4367b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye        if (resource == null) {
4377b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye            IJavaProject[] androidProjects = BaseProjectHelper.getAndroidProjects(null);
4387b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye            for (IJavaProject project : androidProjects) {
4397b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye                IProject p = project.getProject();
4407b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye                if (p != null) {
4417b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye                    // Recurse, but with a different parameter so it will not continue recursing
4427b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye                    removeMarkers(p, id);
4437b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye                }
4447b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye            }
4457b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye            return;
4467b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye        }
4477b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye        IMarker[] markers = getMarkers(resource);
4487b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye        for (IMarker marker : markers) {
4497b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye            if (id.equals(getId(marker))) {
4507b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye                try {
4517b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye                    marker.delete();
4527b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye                } catch (CoreException e) {
4537b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye                    AdtPlugin.log(e, null);
4547b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye                }
4557b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye            }
4567b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye        }
4577b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye    }
4587b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye
4597b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye    /**
460a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye     * Returns the lint marker for the given resource (which may be a project, folder or file)
461a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye     *
462a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye     * @param resource the resource to be checked, typically a source file
463a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye     * @return an array of markers, possibly empty but never null
464a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye     */
465a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye    public static IMarker[] getMarkers(IResource resource) {
466a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        try {
467402af1286193f536946b36979b259511d6a9bb06Tor Norbye            if (resource.isAccessible()) {
468402af1286193f536946b36979b259511d6a9bb06Tor Norbye                return resource.findMarkers(MARKER_LINT, false, IResource.DEPTH_INFINITE);
469402af1286193f536946b36979b259511d6a9bb06Tor Norbye            }
470a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        } catch (CoreException e) {
471a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye            AdtPlugin.log(e, null);
472a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        }
473a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye
474a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        return new IMarker[0];
475a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye    }
476a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye
477a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye    private static int getMarkerSeverity(Severity severity) {
478a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        switch (severity) {
479a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye            case INFORMATIONAL:
480a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye                return IMarker.SEVERITY_INFO;
481a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye            case WARNING:
482a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye                return IMarker.SEVERITY_WARNING;
4835da3610b249d74267a67a6b48acdf4cc95475bc0Tor Norbye            case FATAL:
484a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye            case ERROR:
485a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye            default:
486a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye                return IMarker.SEVERITY_ERROR;
487a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        }
488a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye    }
489a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye
490a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye    private static Pair<Integer, Integer> getRange(IFile file, IDocument doc,
491a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye            Position startPosition, Position endPosition) {
492a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        int startOffset = startPosition.getOffset();
493a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        int endOffset = endPosition != null ? endPosition.getOffset() : -1;
494a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        if (endOffset != -1) {
495a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye            // Attribute ranges often include trailing whitespace; trim this up
496a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye            if (doc == null) {
497a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye                IDocumentProvider provider = new TextFileDocumentProvider();
498a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye                try {
499a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye                    provider.connect(file);
500a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye                    doc = provider.getDocument(file);
501a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye                    if (doc != null) {
502b4622eb274f472919a7d7ae0bab045e4b9e4325fTor Norbye                        return adjustOffsets(doc, startOffset, endOffset);
503a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye                    }
504a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye                } catch (Exception e) {
505a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye                    AdtPlugin.log(e, "Can't find range information for %1$s", file.getName());
506a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye                } finally {
507a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye                    provider.disconnect(file);
508a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye                }
509a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye            } else {
510b4622eb274f472919a7d7ae0bab045e4b9e4325fTor Norbye                return adjustOffsets(doc, startOffset, endOffset);
511a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye            }
512a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        }
513a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye
514a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        return Pair.of(startOffset, startOffset);
515a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye    }
516a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye
517b4622eb274f472919a7d7ae0bab045e4b9e4325fTor Norbye    /**
518b4622eb274f472919a7d7ae0bab045e4b9e4325fTor Norbye     * Trim off any trailing space on the given offset range in the given
519b4622eb274f472919a7d7ae0bab045e4b9e4325fTor Norbye     * document, and don't span multiple lines on ranges since it makes (for
520b4622eb274f472919a7d7ae0bab045e4b9e4325fTor Norbye     * example) the XML editor just glow with yellow underlines for all the
521b4622eb274f472919a7d7ae0bab045e4b9e4325fTor Norbye     * attributes etc. Highlighting just the element beginning gets the point
522b4622eb274f472919a7d7ae0bab045e4b9e4325fTor Norbye     * across. It also makes it more obvious where there are warnings on both
523b4622eb274f472919a7d7ae0bab045e4b9e4325fTor Norbye     * the overall element and on individual attributes since without this the
524b4622eb274f472919a7d7ae0bab045e4b9e4325fTor Norbye     * warnings on attributes would just overlap with the whole-element
525b4622eb274f472919a7d7ae0bab045e4b9e4325fTor Norbye     * highlighting.
526b4622eb274f472919a7d7ae0bab045e4b9e4325fTor Norbye     */
527b4622eb274f472919a7d7ae0bab045e4b9e4325fTor Norbye    private static Pair<Integer, Integer> adjustOffsets(IDocument doc, int startOffset,
528a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye            int endOffset) {
529a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        if (doc != null) {
530a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye            while (endOffset > startOffset && endOffset < doc.getLength()) {
531a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye                try {
532a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye                    if (!Character.isWhitespace(doc.getChar(endOffset - 1))) {
533a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye                        break;
534a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye                    } else {
535a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye                        endOffset--;
536a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye                    }
537a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye                } catch (BadLocationException e) {
538a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye                    // Pass - we've already validated offset range above
539a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye                    break;
540a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye                }
541a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye            }
542b4622eb274f472919a7d7ae0bab045e4b9e4325fTor Norbye
543b4622eb274f472919a7d7ae0bab045e4b9e4325fTor Norbye            // Also don't span lines
544b4622eb274f472919a7d7ae0bab045e4b9e4325fTor Norbye            int lineEnd = startOffset;
545b4622eb274f472919a7d7ae0bab045e4b9e4325fTor Norbye            while (lineEnd < endOffset) {
546b4622eb274f472919a7d7ae0bab045e4b9e4325fTor Norbye                try {
547b4622eb274f472919a7d7ae0bab045e4b9e4325fTor Norbye                    char c = doc.getChar(lineEnd);
548b4622eb274f472919a7d7ae0bab045e4b9e4325fTor Norbye                    if (c == '\n' || c == '\r') {
549b4622eb274f472919a7d7ae0bab045e4b9e4325fTor Norbye                        endOffset = lineEnd;
550b4622eb274f472919a7d7ae0bab045e4b9e4325fTor Norbye                        break;
551b4622eb274f472919a7d7ae0bab045e4b9e4325fTor Norbye                    }
552b4622eb274f472919a7d7ae0bab045e4b9e4325fTor Norbye                } catch (BadLocationException e) {
553b4622eb274f472919a7d7ae0bab045e4b9e4325fTor Norbye                    // Pass - we've already validated offset range above
554b4622eb274f472919a7d7ae0bab045e4b9e4325fTor Norbye                    break;
555b4622eb274f472919a7d7ae0bab045e4b9e4325fTor Norbye                }
556b4622eb274f472919a7d7ae0bab045e4b9e4325fTor Norbye                lineEnd++;
557b4622eb274f472919a7d7ae0bab045e4b9e4325fTor Norbye            }
558a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        }
559a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye
560a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        return Pair.of(startOffset, endOffset);
561a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye    }
562a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye
563a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye    /**
564a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye     * Returns true if a fatal error was encountered
565a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye     *
566a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye     * @return true if a fatal error was encountered
567a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye     */
568229581314076be1b6f82fe1efed2bd00da340899Tor Norbye    public boolean hasFatalErrors() {
569229581314076be1b6f82fe1efed2bd00da340899Tor Norbye        return mWasFatal;
570a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye    }
571a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye
572a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye    /**
5737b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye     * Describe the issue for the given marker
5747b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye     *
5757b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye     * @param marker the marker to look up
5767b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye     * @return a full description of the corresponding issue, never null
5777b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye     */
5787b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye    public static String describe(IMarker marker) {
579229581314076be1b6f82fe1efed2bd00da340899Tor Norbye        IssueRegistry registry = getRegistry();
580b45957a134d6fd6184348387fd0f0b14ffa7021cTor Norbye        String markerId = getId(marker);
581b45957a134d6fd6184348387fd0f0b14ffa7021cTor Norbye        Issue issue = registry.getIssue(markerId);
582b45957a134d6fd6184348387fd0f0b14ffa7021cTor Norbye        if (issue == null) {
583b45957a134d6fd6184348387fd0f0b14ffa7021cTor Norbye            return "";
584b45957a134d6fd6184348387fd0f0b14ffa7021cTor Norbye        }
585b45957a134d6fd6184348387fd0f0b14ffa7021cTor Norbye
5867b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye        String summary = issue.getDescription();
587addbd7110d87e9f48d854e5ba6a729359110ada8Tor Norbye        String explanation = issue.getExplanationAsSimpleText();
5887b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye
5897b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye        StringBuilder sb = new StringBuilder(summary.length() + explanation.length() + 20);
5907b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye        try {
5917b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye            sb.append((String) marker.getAttribute(IMarker.MESSAGE));
5927b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye            sb.append('\n').append('\n');
5937b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye        } catch (CoreException e) {
5947b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye        }
5957b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye        sb.append("Issue: ");
5967b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye        sb.append(summary);
597105dc49ddd885c07b520dd343aa326c306d2c3deTor Norbye        sb.append('\n');
598105dc49ddd885c07b520dd343aa326c306d2c3deTor Norbye        sb.append("Id: ");
599105dc49ddd885c07b520dd343aa326c306d2c3deTor Norbye        sb.append(issue.getId());
6007b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye        sb.append('\n').append('\n');
6017b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye        sb.append(explanation);
6027b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye
6037b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye        if (issue.getMoreInfo() != null) {
6047b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye            sb.append('\n').append('\n');
6057b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye            sb.append(issue.getMoreInfo());
6067b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye        }
6077b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye
6087b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye        return sb.toString();
6097b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye    }
6107b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye
6117b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye    /**
6127b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye     * Returns the id for the given marker
6137b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye     *
6147b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye     * @param marker the marker to look up
6157b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye     * @return the corresponding issue id, or null
6167b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye     */
6177b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye    public static String getId(IMarker marker) {
6187b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye        try {
6197b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye            return (String) marker.getAttribute(MARKER_CHECKID_PROPERTY);
6207b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye        } catch (CoreException e) {
6217b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye            return null;
6227b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye        }
6237b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye    }
6247b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye
6257b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye    /**
6267b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye     * Shows the given marker in the editor
6277b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye     *
6287b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye     * @param marker the marker to be shown
6297b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye     */
6307b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye    public static void showMarker(IMarker marker) {
6317b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye        IRegion region = null;
6327b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye        try {
6337b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye            int start = marker.getAttribute(IMarker.CHAR_START, -1);
6347b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye            int end = marker.getAttribute(IMarker.CHAR_END, -1);
6357b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye            if (start >= 0 && end >= 0) {
6367b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye                region = new org.eclipse.jface.text.Region(start, end - start);
6377b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye            }
6387b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye
6397b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye            IResource resource = marker.getResource();
6407b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye            if (resource instanceof IFile) {
641b45957a134d6fd6184348387fd0f0b14ffa7021cTor Norbye                IEditorPart editor =
642b45957a134d6fd6184348387fd0f0b14ffa7021cTor Norbye                        AdtPlugin.openFile((IFile) resource, region, true /* showEditorTab */);
643b45957a134d6fd6184348387fd0f0b14ffa7021cTor Norbye                if (editor != null) {
644b45957a134d6fd6184348387fd0f0b14ffa7021cTor Norbye                    IDE.gotoMarker(editor, marker);
645b45957a134d6fd6184348387fd0f0b14ffa7021cTor Norbye                }
6467b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye            }
6477b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye        } catch (PartInitException ex) {
6487b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye            AdtPlugin.log(ex, null);
6497b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye        }
6507b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye    }
6517b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye
6527b08b8258cffcb3085a90d32e02da95fafbfdeafTor Norbye    /**
653a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye     * Show a dialog with errors for the given file
654a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye     *
655a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye     * @param shell the parent shell to attach the dialog to
656a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye     * @param file the file to show the errors for
6575ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye     * @param editor the editor for the file, if known
658a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye     */
6595ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye    public static void showErrors(
6605ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye            @NonNull Shell shell,
6615ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye            @NonNull IFile file,
6625ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye            @Nullable IEditorPart editor) {
6635ae903bf8670f40e9eb40ebbd0a25c15eaac5962Tor Norbye        LintListDialog dialog = new LintListDialog(shell, file, editor);
664a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        dialog.open();
665a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye    }
666a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye
6678853b3b92496c5734bbcb75499030bb51a060097Tor Norbye    @Override
6687e4b8e9d595e45baa9d87cdb8282f02759e73abcTor Norbye    public @NonNull String readFile(@NonNull File f) {
6698853b3b92496c5734bbcb75499030bb51a060097Tor Norbye        // Map File to IFile
6708853b3b92496c5734bbcb75499030bb51a060097Tor Norbye        IFile file = AdtUtils.fileToIFile(f);
6718853b3b92496c5734bbcb75499030bb51a060097Tor Norbye        if (file == null || !file.exists()) {
6728853b3b92496c5734bbcb75499030bb51a060097Tor Norbye            String path = f.getPath();
6738853b3b92496c5734bbcb75499030bb51a060097Tor Norbye            AdtPlugin.log(IStatus.ERROR, "Can't find file %1$s in workspace", path);
67423f0d6a5e0d332d7a29f8d320b9c33badfae9451Tor Norbye            return readPlainFile(f);
6758853b3b92496c5734bbcb75499030bb51a060097Tor Norbye        }
6768853b3b92496c5734bbcb75499030bb51a060097Tor Norbye
6771fb460987f7d832adf12290f41448d0c16a95972Siva Velusamy        if (SdkUtils.endsWithIgnoreCase(file.getName(), DOT_XML)) {
678e24afba79ef6ee7a298cee003355d40bcad5058eTor Norbye            IStructuredModel model = null;
679e24afba79ef6ee7a298cee003355d40bcad5058eTor Norbye            try {
680e24afba79ef6ee7a298cee003355d40bcad5058eTor Norbye                IModelManager modelManager = StructuredModelManager.getModelManager();
681e24afba79ef6ee7a298cee003355d40bcad5058eTor Norbye                model = modelManager.getModelForRead(file);
682e24afba79ef6ee7a298cee003355d40bcad5058eTor Norbye                return model.getStructuredDocument().get();
683e24afba79ef6ee7a298cee003355d40bcad5058eTor Norbye            } catch (IOException e) {
684e24afba79ef6ee7a298cee003355d40bcad5058eTor Norbye                AdtPlugin.log(e, "Cannot read XML file");
685e24afba79ef6ee7a298cee003355d40bcad5058eTor Norbye            } catch (CoreException e) {
686e24afba79ef6ee7a298cee003355d40bcad5058eTor Norbye                AdtPlugin.log(e, null);
687e24afba79ef6ee7a298cee003355d40bcad5058eTor Norbye            } finally {
688e24afba79ef6ee7a298cee003355d40bcad5058eTor Norbye                if (model != null) {
689e24afba79ef6ee7a298cee003355d40bcad5058eTor Norbye                    // TODO: This may be too early...
690e24afba79ef6ee7a298cee003355d40bcad5058eTor Norbye                    model.releaseFromRead();
691e24afba79ef6ee7a298cee003355d40bcad5058eTor Norbye                }
6928853b3b92496c5734bbcb75499030bb51a060097Tor Norbye            }
6938853b3b92496c5734bbcb75499030bb51a060097Tor Norbye        }
6948853b3b92496c5734bbcb75499030bb51a060097Tor Norbye
6958853b3b92496c5734bbcb75499030bb51a060097Tor Norbye        return readPlainFile(f);
6968853b3b92496c5734bbcb75499030bb51a060097Tor Norbye    }
6978853b3b92496c5734bbcb75499030bb51a060097Tor Norbye
698581ca2e75fd638f6e95570ac54ff9e6b7b7cde84Tor Norbye    private String readPlainFile(File file) {
6998853b3b92496c5734bbcb75499030bb51a060097Tor Norbye        try {
7008dcd0726088eee54368e032e7dc6b31a450b4ee1Tor Norbye            return LintUtils.getEncodedString(this, file);
7018853b3b92496c5734bbcb75499030bb51a060097Tor Norbye        } catch (IOException e) {
702581ca2e75fd638f6e95570ac54ff9e6b7b7cde84Tor Norbye            return ""; //$NON-NLS-1$
7038853b3b92496c5734bbcb75499030bb51a060097Tor Norbye        }
7048853b3b92496c5734bbcb75499030bb51a060097Tor Norbye    }
7058853b3b92496c5734bbcb75499030bb51a060097Tor Norbye
706ab36f4e7488358dea4ab6b54ee2b7bef3da0232bTor Norbye    @Override
7077e4b8e9d595e45baa9d87cdb8282f02759e73abcTor Norbye    public @NonNull Location getLocation(@NonNull XmlContext context, @NonNull Node node) {
708c8e42c3bfdef4941696155e5b333dd6d4075bbf0Tor Norbye        IStructuredModel model = (IStructuredModel) context.getProperty(MODEL_PROPERTY);
709c8e42c3bfdef4941696155e5b333dd6d4075bbf0Tor Norbye        return new LazyLocation(context.file, model.getStructuredDocument(), (IndexedRegion) node);
7103ce45b249f898697ae82e8c6dd045966227f3438Tor Norbye    }
7113ce45b249f898697ae82e8c6dd045966227f3438Tor Norbye
712ab36f4e7488358dea4ab6b54ee2b7bef3da0232bTor Norbye    @Override
7135e290ab6060c3204be3a3eb6084db6f92c88adeeTor Norbye    public @NonNull Location getLocation(@NonNull XmlContext context, @NonNull Node node,
7145e290ab6060c3204be3a3eb6084db6f92c88adeeTor Norbye            int start, int end) {
7155e290ab6060c3204be3a3eb6084db6f92c88adeeTor Norbye        IndexedRegion region = (IndexedRegion) node;
7165e290ab6060c3204be3a3eb6084db6f92c88adeeTor Norbye        int nodeStart = region.getStartOffset();
7175e290ab6060c3204be3a3eb6084db6f92c88adeeTor Norbye
7185e290ab6060c3204be3a3eb6084db6f92c88adeeTor Norbye        IStructuredModel model = (IStructuredModel) context.getProperty(MODEL_PROPERTY);
7195e290ab6060c3204be3a3eb6084db6f92c88adeeTor Norbye        // Get line number
7205e290ab6060c3204be3a3eb6084db6f92c88adeeTor Norbye        LazyLocation location = new LazyLocation(context.file, model.getStructuredDocument(),
7215e290ab6060c3204be3a3eb6084db6f92c88adeeTor Norbye                region);
7225e290ab6060c3204be3a3eb6084db6f92c88adeeTor Norbye        int line = location.getStart().getLine();
7235e290ab6060c3204be3a3eb6084db6f92c88adeeTor Norbye
7245e290ab6060c3204be3a3eb6084db6f92c88adeeTor Norbye        Position startPos = new DefaultPosition(line, -1, nodeStart + start);
7255e290ab6060c3204be3a3eb6084db6f92c88adeeTor Norbye        Position endPos = new DefaultPosition(line, -1, nodeStart + end);
7265e290ab6060c3204be3a3eb6084db6f92c88adeeTor Norbye        return Location.create(context.file, startPos, endPos);
7275e290ab6060c3204be3a3eb6084db6f92c88adeeTor Norbye    }
7285e290ab6060c3204be3a3eb6084db6f92c88adeeTor Norbye
7295e290ab6060c3204be3a3eb6084db6f92c88adeeTor Norbye    @Override
7307e4b8e9d595e45baa9d87cdb8282f02759e73abcTor Norbye    public @NonNull Handle createLocationHandle(final @NonNull XmlContext context,
7317e4b8e9d595e45baa9d87cdb8282f02759e73abcTor Norbye            final @NonNull Node node) {
732c8e42c3bfdef4941696155e5b333dd6d4075bbf0Tor Norbye        IStructuredModel model = (IStructuredModel) context.getProperty(MODEL_PROPERTY);
733c8e42c3bfdef4941696155e5b333dd6d4075bbf0Tor Norbye        return new LazyLocation(context.file, model.getStructuredDocument(), (IndexedRegion) node);
7348853b3b92496c5734bbcb75499030bb51a060097Tor Norbye    }
7358853b3b92496c5734bbcb75499030bb51a060097Tor Norbye
7364ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye    private Map<Project, ClassPathInfo> mProjectInfo;
7374ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye
7384ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye    @Override
7394ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye    @NonNull
7404ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye    protected ClassPathInfo getClassPath(@NonNull Project project) {
7414ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye        ClassPathInfo info;
7424ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye        if (mProjectInfo == null) {
7434ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye            mProjectInfo = Maps.newHashMap();
7444ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye            info = null;
7454ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye        } else {
7464ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye            info = mProjectInfo.get(project);
7474ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye        }
7484ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye
7494ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye        if (info == null) {
7504ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye            List<File> sources = null;
7514ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye            List<File> classes = null;
7524ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye            List<File> libraries = null;
7534ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye
7544ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye            IProject p = getProject(project);
7554ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye            if (p != null) {
7564ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                try {
7574ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                    IJavaProject javaProject = BaseProjectHelper.getJavaProject(p);
7584ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye
7594ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                    // Output path
7604ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                    File file = workspacePathToFile(javaProject.getOutputLocation());
7614ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                    classes = Collections.singletonList(file);
7624ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye
7634ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                    // Source path
7644ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                    IClasspathEntry[] entries = javaProject.getRawClasspath();
7654ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                    sources = new ArrayList<File>(entries.length);
7664ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                    libraries = new ArrayList<File>(entries.length);
7674ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                    for (int i = 0; i < entries.length; i++) {
7684ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                        IClasspathEntry entry = entries[i];
7694ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                        int kind = entry.getEntryKind();
7704ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye
7714ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                        if (kind == IClasspathEntry.CPE_VARIABLE) {
7724ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                            entry = JavaCore.getResolvedClasspathEntry(entry);
773ea7dd912dc8f5b8f3ae8545166657beacb429552Tor Norbye                            if (entry == null) {
774ea7dd912dc8f5b8f3ae8545166657beacb429552Tor Norbye                                // It's possible that the variable is no longer valid; ignore
775ea7dd912dc8f5b8f3ae8545166657beacb429552Tor Norbye                                continue;
776ea7dd912dc8f5b8f3ae8545166657beacb429552Tor Norbye                            }
7774ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                            kind = entry.getEntryKind();
7784ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                        }
7794ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye
7804ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                        if (kind == IClasspathEntry.CPE_SOURCE) {
7814ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                            sources.add(workspacePathToFile(entry.getPath()));
7824ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                        } else if (kind == IClasspathEntry.CPE_LIBRARY) {
7834ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                            libraries.add(entry.getPath().toFile());
7844ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                        }
7854ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                        // Note that we ignore IClasspathEntry.CPE_CONTAINER:
7864ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                        // Normal Android Eclipse projects supply both
7874ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                        //   AdtConstants.CONTAINER_FRAMEWORK
7884ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                        // and
7894ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                        //   AdtConstants.CONTAINER_LIBRARIES
7904ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                        // here. We ignore the framework classes for obvious reasons,
7914ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                        // but we also ignore the library container because lint will
7924ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                        // process the libraries differently. When Eclipse builds a
7934ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                        // project, it gets the .jar output of the library projects
7944ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                        // from this container, which means it doesn't have to process
7954ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                        // the library sources. Lint on the other hand wants to process
7964ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                        // the source code, so instead it actually looks at the
7974ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                        // project.properties file to find the libraries, and then it
7984ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                        // iterates over all the library projects in turn and analyzes
7994ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                        // those separately (but passing the main project for context,
8004ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                        // such that the including project's manifest declarations
8014ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                        // are used for data like minSdkVersion level).
8024ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                        //
8034ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                        // Note that this container will also contain *other*
8044ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                        // libraries (Java libraries, not library projects) that we
8054ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                        // *should* include. However, we can't distinguish these
8064ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                        // class path entries from the library project jars,
8074ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                        // so instead of looking at these, we simply listFiles() in
8084ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                        // the libs/ folder after processing the classpath info
8094ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                    }
8104ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye
8114ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                    // Add in libraries
8124ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                    File libs = new File(project.getDir(), FD_NATIVE_LIBS);
8134ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                    if (libs.isDirectory()) {
8144ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                        File[] jars = libs.listFiles();
8154ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                        if (jars != null) {
8164ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                            for (File jar : jars) {
8171fb460987f7d832adf12290f41448d0c16a95972Siva Velusamy                                if (SdkUtils.endsWith(jar.getPath(), DOT_JAR)) {
8184ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                                    libraries.add(jar);
8194ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                                }
8204ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                            }
8214ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                        }
8224ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                    }
8234ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                } catch (CoreException e) {
8244ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                    AdtPlugin.log(e, null);
8254ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                }
8264ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye            }
8274ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye
8284ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye            if (sources == null) {
8294ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                sources = super.getClassPath(project).getSourceFolders();
8304ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye            }
8314ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye            if (classes == null) {
8324ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                classes = super.getClassPath(project).getClassFolders();
8334ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye            }
8344ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye            if (libraries == null) {
8354ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye                libraries = super.getClassPath(project).getLibraries();
8364ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye            }
8374ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye
8384ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye            info = new ClassPathInfo(sources, classes, libraries);
8394ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye            mProjectInfo.put(project, info);
8404ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye        }
8414ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye
8424ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye        return info;
8434ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye    }
8444ae3297a857c6a0c84511b4c5e727360f6958c89Tor Norbye
845a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye    /**
846229581314076be1b6f82fe1efed2bd00da340899Tor Norbye     * Returns the registry of issues to check from within Eclipse.
847a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye     *
848229581314076be1b6f82fe1efed2bd00da340899Tor Norbye     * @return the issue registry to use to access detectors and issues
849a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye     */
850229581314076be1b6f82fe1efed2bd00da340899Tor Norbye    public static IssueRegistry getRegistry() {
851229581314076be1b6f82fe1efed2bd00da340899Tor Norbye        return new BuiltinIssueRegistry();
8528853b3b92496c5734bbcb75499030bb51a060097Tor Norbye    }
8538853b3b92496c5734bbcb75499030bb51a060097Tor Norbye
854229581314076be1b6f82fe1efed2bd00da340899Tor Norbye    @Override
8557e4b8e9d595e45baa9d87cdb8282f02759e73abcTor Norbye    public @NonNull Class<? extends Detector> replaceDetector(
8567e4b8e9d595e45baa9d87cdb8282f02759e73abcTor Norbye            @NonNull Class<? extends Detector> detectorClass) {
857229581314076be1b6f82fe1efed2bd00da340899Tor Norbye        return detectorClass;
858a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye    }
859a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye
860ab36f4e7488358dea4ab6b54ee2b7bef3da0232bTor Norbye    @Override
8617e4b8e9d595e45baa9d87cdb8282f02759e73abcTor Norbye    public void dispose(@NonNull XmlContext context, @NonNull Document document) {
862c8e42c3bfdef4941696155e5b333dd6d4075bbf0Tor Norbye        IStructuredModel model = (IStructuredModel) context.getProperty(MODEL_PROPERTY);
863c8e42c3bfdef4941696155e5b333dd6d4075bbf0Tor Norbye        assert model != null : context.file;
864c8e42c3bfdef4941696155e5b333dd6d4075bbf0Tor Norbye        if (model != null) {
865c8e42c3bfdef4941696155e5b333dd6d4075bbf0Tor Norbye            model.releaseFromRead();
866c8e42c3bfdef4941696155e5b333dd6d4075bbf0Tor Norbye        }
867a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye    }
8688853b3b92496c5734bbcb75499030bb51a060097Tor Norbye
869b52aeca0f2e150d7e7e8ceffa897a7ae1f938724Tor Norbye    @Override
870b52aeca0f2e150d7e7e8ceffa897a7ae1f938724Tor Norbye    @NonNull
871b52aeca0f2e150d7e7e8ceffa897a7ae1f938724Tor Norbye    public IAndroidTarget[] getTargets() {
872b52aeca0f2e150d7e7e8ceffa897a7ae1f938724Tor Norbye        return Sdk.getCurrent().getTargets();
873b52aeca0f2e150d7e7e8ceffa897a7ae1f938724Tor Norbye    }
874b52aeca0f2e150d7e7e8ceffa897a7ae1f938724Tor Norbye
8753ce45b249f898697ae82e8c6dd045966227f3438Tor Norbye    private static class LazyLocation extends Location implements Location.Handle {
8763ce45b249f898697ae82e8c6dd045966227f3438Tor Norbye        private final IStructuredDocument mDocument;
8778853b3b92496c5734bbcb75499030bb51a060097Tor Norbye        private final IndexedRegion mRegion;
8788853b3b92496c5734bbcb75499030bb51a060097Tor Norbye        private Position mStart;
8798853b3b92496c5734bbcb75499030bb51a060097Tor Norbye        private Position mEnd;
8808853b3b92496c5734bbcb75499030bb51a060097Tor Norbye
8813ce45b249f898697ae82e8c6dd045966227f3438Tor Norbye        public LazyLocation(File file, IStructuredDocument document, IndexedRegion region) {
8828853b3b92496c5734bbcb75499030bb51a060097Tor Norbye            super(file, null /*start*/, null /*end*/);
8833ce45b249f898697ae82e8c6dd045966227f3438Tor Norbye            mDocument = document;
8848853b3b92496c5734bbcb75499030bb51a060097Tor Norbye            mRegion = region;
8858853b3b92496c5734bbcb75499030bb51a060097Tor Norbye        }
8868853b3b92496c5734bbcb75499030bb51a060097Tor Norbye
8878853b3b92496c5734bbcb75499030bb51a060097Tor Norbye        @Override
8888853b3b92496c5734bbcb75499030bb51a060097Tor Norbye        public Position getStart() {
8898853b3b92496c5734bbcb75499030bb51a060097Tor Norbye            if (mStart == null) {
8903ce45b249f898697ae82e8c6dd045966227f3438Tor Norbye                int line = -1;
8913ce45b249f898697ae82e8c6dd045966227f3438Tor Norbye                int column = -1;
8923ce45b249f898697ae82e8c6dd045966227f3438Tor Norbye                int offset = mRegion.getStartOffset();
8933ce45b249f898697ae82e8c6dd045966227f3438Tor Norbye
89402e16b51b3986082e14e6121dde71db1898f6d60Tor Norbye                if (mRegion instanceof org.w3c.dom.Text && mDocument != null) {
89502e16b51b3986082e14e6121dde71db1898f6d60Tor Norbye                    // For text nodes, skip whitespace prefix, if any
89602e16b51b3986082e14e6121dde71db1898f6d60Tor Norbye                    for (int i = offset;
89702e16b51b3986082e14e6121dde71db1898f6d60Tor Norbye                            i < mRegion.getEndOffset() && i < mDocument.getLength(); i++) {
89802e16b51b3986082e14e6121dde71db1898f6d60Tor Norbye                        try {
89902e16b51b3986082e14e6121dde71db1898f6d60Tor Norbye                            char c = mDocument.getChar(i);
90002e16b51b3986082e14e6121dde71db1898f6d60Tor Norbye                            if (!Character.isWhitespace(c)) {
90102e16b51b3986082e14e6121dde71db1898f6d60Tor Norbye                                offset = i;
90202e16b51b3986082e14e6121dde71db1898f6d60Tor Norbye                                break;
90302e16b51b3986082e14e6121dde71db1898f6d60Tor Norbye                            }
90402e16b51b3986082e14e6121dde71db1898f6d60Tor Norbye                        } catch (BadLocationException e) {
90502e16b51b3986082e14e6121dde71db1898f6d60Tor Norbye                            break;
90602e16b51b3986082e14e6121dde71db1898f6d60Tor Norbye                        }
90702e16b51b3986082e14e6121dde71db1898f6d60Tor Norbye                    }
90802e16b51b3986082e14e6121dde71db1898f6d60Tor Norbye                }
90902e16b51b3986082e14e6121dde71db1898f6d60Tor Norbye
9103ce45b249f898697ae82e8c6dd045966227f3438Tor Norbye                if (mDocument != null && offset < mDocument.getLength()) {
9113ce45b249f898697ae82e8c6dd045966227f3438Tor Norbye                    line = mDocument.getLineOfOffset(offset);
9123ce45b249f898697ae82e8c6dd045966227f3438Tor Norbye                    column = -1;
9133ce45b249f898697ae82e8c6dd045966227f3438Tor Norbye                    try {
9143ce45b249f898697ae82e8c6dd045966227f3438Tor Norbye                        int lineOffset = mDocument.getLineOffset(line);
9153ce45b249f898697ae82e8c6dd045966227f3438Tor Norbye                        column = offset - lineOffset;
9163ce45b249f898697ae82e8c6dd045966227f3438Tor Norbye                    } catch (BadLocationException e) {
9173ce45b249f898697ae82e8c6dd045966227f3438Tor Norbye                        AdtPlugin.log(e, null);
9183ce45b249f898697ae82e8c6dd045966227f3438Tor Norbye                    }
9193ce45b249f898697ae82e8c6dd045966227f3438Tor Norbye                }
9203ce45b249f898697ae82e8c6dd045966227f3438Tor Norbye
9213ce45b249f898697ae82e8c6dd045966227f3438Tor Norbye                mStart = new DefaultPosition(line, column, offset);
9228853b3b92496c5734bbcb75499030bb51a060097Tor Norbye            }
9238853b3b92496c5734bbcb75499030bb51a060097Tor Norbye
9248853b3b92496c5734bbcb75499030bb51a060097Tor Norbye            return mStart;
9258853b3b92496c5734bbcb75499030bb51a060097Tor Norbye        }
9268853b3b92496c5734bbcb75499030bb51a060097Tor Norbye
9278853b3b92496c5734bbcb75499030bb51a060097Tor Norbye        @Override
9288853b3b92496c5734bbcb75499030bb51a060097Tor Norbye        public Position getEnd() {
9298853b3b92496c5734bbcb75499030bb51a060097Tor Norbye            if (mEnd == null) {
9303ce45b249f898697ae82e8c6dd045966227f3438Tor Norbye                mEnd = new DefaultPosition(-1, -1, mRegion.getEndOffset());
9318853b3b92496c5734bbcb75499030bb51a060097Tor Norbye            }
9328853b3b92496c5734bbcb75499030bb51a060097Tor Norbye
9338853b3b92496c5734bbcb75499030bb51a060097Tor Norbye            return mEnd;
9348853b3b92496c5734bbcb75499030bb51a060097Tor Norbye        }
93527ed098e750ee9463dfa7a36cef68d219de9eb50Tor Norbye
936ab36f4e7488358dea4ab6b54ee2b7bef3da0232bTor Norbye        @Override
9377e4b8e9d595e45baa9d87cdb8282f02759e73abcTor Norbye        public @NonNull Location resolve() {
9383ce45b249f898697ae82e8c6dd045966227f3438Tor Norbye            return this;
9393ce45b249f898697ae82e8c6dd045966227f3438Tor Norbye        }
94027ed098e750ee9463dfa7a36cef68d219de9eb50Tor Norbye    }
94114312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye
94214312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye    private static class EclipseJavaParser implements IJavaParser {
94314312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye        private static final boolean USE_ECLIPSE_PARSER = true;
94414312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye        private final Parser mParser;
94514312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye
94614312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye        EclipseJavaParser() {
94714312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye            if (USE_ECLIPSE_PARSER) {
94814312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                CompilerOptions options = new CompilerOptions();
94914312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                // Read settings from project? Note that this doesn't really matter because
95014312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                // we will only be parsing, not actually compiling.
95114312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                options.complianceLevel = ClassFileConstants.JDK1_6;
95214312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                options.sourceLevel = ClassFileConstants.JDK1_6;
95314312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                options.targetJDK = ClassFileConstants.JDK1_6;
95414312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                options.parseLiteralExpressionsAsConstants = true;
95514312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                ProblemReporter problemReporter = new ProblemReporter(
95614312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                        DefaultErrorHandlingPolicies.exitOnFirstError(),
95714312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                        options,
95814312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                        new DefaultProblemFactory());
95914312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                mParser = new Parser(problemReporter, options.parseLiteralExpressionsAsConstants);
96014312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                mParser.javadocParser.checkDocComment = false;
96114312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye            } else {
96214312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                mParser = null;
96314312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye            }
96414312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye        }
96514312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye
96614312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye        @Override
9677e4b8e9d595e45baa9d87cdb8282f02759e73abcTor Norbye        public lombok.ast.Node parseJava(@NonNull JavaContext context) {
96814312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye            if (USE_ECLIPSE_PARSER) {
96914312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                // Use Eclipse's compiler
97014312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                EcjTreeConverter converter = new EcjTreeConverter();
97114312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                String code = context.getContents();
97214312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye
97314312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                CompilationUnit sourceUnit = new CompilationUnit(code.toCharArray(),
97414312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                        context.file.getName(), "UTF-8"); //$NON-NLS-1$
97514312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                CompilationResult compilationResult = new CompilationResult(sourceUnit, 0, 0, 0);
97614312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                CompilationUnitDeclaration unit = null;
97714312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                try {
97814312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                    unit = mParser.parse(sourceUnit, compilationResult);
97914312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                } catch (AbortCompilation e) {
980afb7b2c7b91831381e5f1f096738654dcd1bb82dTor Norbye                    // No need to report Java parsing errors while running in Eclipse.
981afb7b2c7b91831381e5f1f096738654dcd1bb82dTor Norbye                    // Eclipse itself will already provide problem markers for these files,
982afb7b2c7b91831381e5f1f096738654dcd1bb82dTor Norbye                    // so all this achieves is creating "multiple annotations on this line"
983afb7b2c7b91831381e5f1f096738654dcd1bb82dTor Norbye                    // tooltips instead.
98414312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                    return null;
98514312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                }
98614312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                if (unit == null) {
98714312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                    return null;
98814312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                }
98914312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye
99014312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                try {
99114312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                    converter.visit(code, unit);
99214312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                    List<? extends lombok.ast.Node> nodes = converter.getAll();
99314312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye
99414312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                    // There could be more than one node when there are errors; pick out the
99514312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                    // compilation unit node
99614312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                    for (lombok.ast.Node node : nodes) {
99737d5a9bbdc2895921cd0f636777efe2d3bad4e5bTor Norbye                        if (node instanceof lombok.ast.CompilationUnit) {
99814312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                            return node;
99914312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                        }
100014312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                    }
100114312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye
100214312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                    return null;
100314312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                } catch (Throwable t) {
100414312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                    AdtPlugin.log(t, "Failed converting ECJ parse tree to Lombok for file %1$s",
100514312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                            context.file.getPath());
100614312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                    return null;
100714312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                }
100814312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye            } else {
100914312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                // Use Lombok for now
101014312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                Source source = new Source(context.getContents(), context.file.getName());
101114312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                List<lombok.ast.Node> nodes = source.getNodes();
101214312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye
101314312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                // Don't analyze files containing errors
101414312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                List<ParseProblem> problems = source.getProblems();
101514312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                if (problems != null && problems.size() > 0) {
1016a2a7d644db6b742fda413600f62c64c1133a6c21Tor Norbye                    /* Silently ignore the errors. There are still some bugs in Lombok/Parboiled
1017a2a7d644db6b742fda413600f62c64c1133a6c21Tor Norbye                     * (triggered if you run lint on the AOSP framework directory for example),
1018a2a7d644db6b742fda413600f62c64c1133a6c21Tor Norbye                     * and having these show up as fatal errors when it's really a tool bug
1019a2a7d644db6b742fda413600f62c64c1133a6c21Tor Norbye                     * is bad. To make matters worse, the error messages aren't clear:
1020a2a7d644db6b742fda413600f62c64c1133a6c21Tor Norbye                     * http://code.google.com/p/projectlombok/issues/detail?id=313
102114312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                    for (ParseProblem problem : problems) {
102214312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                        lombok.ast.Position position = problem.getPosition();
102314312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                        Location location = Location.create(context.file,
102414312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                                context.getContents(), position.getStart(), position.getEnd());
102514312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                        String message = problem.getMessage();
102614312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                        context.report(
102714312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                                IssueRegistry.PARSER_ERROR, location,
102814312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                                message,
102914312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                                null);
103014312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye
103114312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                    }
1032a2a7d644db6b742fda413600f62c64c1133a6c21Tor Norbye                    */
103314312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                    return null;
103414312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                }
103514312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye
103614312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                // There could be more than one node when there are errors; pick out the
103714312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                // compilation unit node
103814312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                for (lombok.ast.Node node : nodes) {
103914312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                    if (node instanceof lombok.ast.CompilationUnit) {
104014312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                        return node;
104114312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                    }
104214312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                }
104314312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                return null;
104414312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye            }
104514312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye        }
104614312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye
104714312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye        @Override
10487e4b8e9d595e45baa9d87cdb8282f02759e73abcTor Norbye        public @NonNull Location getLocation(@NonNull JavaContext context,
10497e4b8e9d595e45baa9d87cdb8282f02759e73abcTor Norbye                @NonNull lombok.ast.Node node) {
105014312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye            lombok.ast.Position position = node.getPosition();
105114312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye            return Location.create(context.file, context.getContents(),
105214312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                    position.getStart(), position.getEnd());
105314312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye        }
105414312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye
105514312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye        @Override
10567e4b8e9d595e45baa9d87cdb8282f02759e73abcTor Norbye        public @NonNull Handle createLocationHandle(@NonNull JavaContext context,
10577e4b8e9d595e45baa9d87cdb8282f02759e73abcTor Norbye                @NonNull lombok.ast.Node node) {
105814312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye            return new LocationHandle(context.file, node);
105914312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye        }
106014312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye
106114312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye        @Override
10627e4b8e9d595e45baa9d87cdb8282f02759e73abcTor Norbye        public void dispose(@NonNull JavaContext context,
10637e4b8e9d595e45baa9d87cdb8282f02759e73abcTor Norbye                @NonNull lombok.ast.Node compilationUnit) {
106414312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye        }
106514312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye
106614312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye        /* Handle for creating positions cheaply and returning full fledged locations later */
106714312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye        private class LocationHandle implements Handle {
106814312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye            private File mFile;
106914312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye            private lombok.ast.Node mNode;
10703e10306ab1812b2c141e75fb36e2f5c92eeefd28Tor Norbye            private Object mClientData;
107114312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye
107214312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye            public LocationHandle(File file, lombok.ast.Node node) {
107314312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                mFile = file;
107414312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                mNode = node;
107514312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye            }
107614312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye
107714312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye            @Override
10787e4b8e9d595e45baa9d87cdb8282f02759e73abcTor Norbye            public @NonNull Location resolve() {
107914312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                lombok.ast.Position pos = mNode.getPosition();
108014312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye                return Location.create(mFile, null /*contents*/, pos.getStart(), pos.getEnd());
108114312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye            }
10823e10306ab1812b2c141e75fb36e2f5c92eeefd28Tor Norbye
10833e10306ab1812b2c141e75fb36e2f5c92eeefd28Tor Norbye            @Override
10843e10306ab1812b2c141e75fb36e2f5c92eeefd28Tor Norbye            public void setClientData(@Nullable Object clientData) {
10853e10306ab1812b2c141e75fb36e2f5c92eeefd28Tor Norbye                mClientData = clientData;
10863e10306ab1812b2c141e75fb36e2f5c92eeefd28Tor Norbye            }
10873e10306ab1812b2c141e75fb36e2f5c92eeefd28Tor Norbye
10883e10306ab1812b2c141e75fb36e2f5c92eeefd28Tor Norbye            @Override
10893e10306ab1812b2c141e75fb36e2f5c92eeefd28Tor Norbye            @Nullable
10903e10306ab1812b2c141e75fb36e2f5c92eeefd28Tor Norbye            public Object getClientData() {
10913e10306ab1812b2c141e75fb36e2f5c92eeefd28Tor Norbye                return mClientData;
10923e10306ab1812b2c141e75fb36e2f5c92eeefd28Tor Norbye            }
109314312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye        }
109414312a9603fab9e711639cfc19d5ea1de17250b1Tor Norbye    }
1095a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye}
1096229581314076be1b6f82fe1efed2bd00da340899Tor Norbye
1097