aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/.settings/org.moreunit.prefs3
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF3
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml28
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtUtils.java6
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidXmlEditor.java4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutActionBar.java6
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/ClearLintMarkersAction.java4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/EclipseLintClient.java (renamed from eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintEclipseContext.java)209
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/GlobalLintConfiguration.java173
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintFix.java7
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintFixGenerator.java75
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintList.java14
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintListDialog.java16
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintRunner.java44
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintViewPart.java50
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/ProjectLintConfiguration.java89
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/RunLintAction.java19
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/UnusedResourceDetector.java4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/AdtPrefs.java1
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/LintPreferencePage.java605
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/.classpath2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/META-INF/MANIFEST.MF2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/build.properties2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/lint/ProjectLintConfigurationTest.java228
-rw-r--r--ide_common/src/com/android/ide/common/resources/configuration/VersionQualifier.java4
-rw-r--r--lint/cli/src/com/android/tools/lint/HtmlReporter.java85
-rw-r--r--lint/cli/src/com/android/tools/lint/Main.java248
-rw-r--r--lint/cli/src/com/android/tools/lint/PositionXmlParser.java24
-rw-r--r--lint/cli/src/com/android/tools/lint/Warning.java14
-rw-r--r--lint/cli/src/com/android/tools/lint/XmlReporter.java107
-rw-r--r--lint/libs/lint_api/src/com/android/tools/lint/api/DetectorRegistry.java169
-rw-r--r--lint/libs/lint_api/src/com/android/tools/lint/client/api/Configuration.java118
-rw-r--r--lint/libs/lint_api/src/com/android/tools/lint/client/api/DefaultConfiguration.java377
-rw-r--r--lint/libs/lint_api/src/com/android/tools/lint/client/api/IDomParser.java (renamed from lint/libs/lint_api/src/com/android/tools/lint/api/IDomParser.java)8
-rw-r--r--lint/libs/lint_api/src/com/android/tools/lint/client/api/IssueRegistry.java204
-rw-r--r--lint/libs/lint_api/src/com/android/tools/lint/client/api/Lint.java (renamed from lint/libs/lint_api/src/com/android/tools/lint/api/Lint.java)286
-rw-r--r--lint/libs/lint_api/src/com/android/tools/lint/client/api/LintClient.java (renamed from lint/libs/lint_api/src/com/android/tools/lint/api/ToolContext.java)77
-rw-r--r--lint/libs/lint_api/src/com/android/tools/lint/client/api/LintListener.java48
-rw-r--r--lint/libs/lint_api/src/com/android/tools/lint/client/api/XmlVisitor.java (renamed from lint/libs/lint_api/src/com/android/tools/lint/api/XmlVisitor.java)65
-rw-r--r--lint/libs/lint_api/src/com/android/tools/lint/detector/api/Category.java139
-rw-r--r--lint/libs/lint_api/src/com/android/tools/lint/detector/api/Context.java17
-rw-r--r--lint/libs/lint_api/src/com/android/tools/lint/detector/api/Detector.java79
-rw-r--r--lint/libs/lint_api/src/com/android/tools/lint/detector/api/Issue.java45
-rw-r--r--lint/libs/lint_api/src/com/android/tools/lint/detector/api/LayoutDetector.java62
-rw-r--r--lint/libs/lint_api/src/com/android/tools/lint/detector/api/LintConstants.java108
-rw-r--r--lint/libs/lint_api/src/com/android/tools/lint/detector/api/LintUtils.java148
-rw-r--r--lint/libs/lint_api/src/com/android/tools/lint/detector/api/Project.java76
-rw-r--r--lint/libs/lint_api/src/com/android/tools/lint/detector/api/ResourceXmlDetector.java2
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/AccessibilityDetector.java21
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/ArraySizeDetector.java19
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/BuiltinDetectorRegistry.java70
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/BuiltinIssueRegistry.java90
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/ChildCountDetector.java30
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/DuplicateIdDetector.java29
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/ExportedServiceDetector.java32
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/GridLayoutDetector.java25
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/HardcodedValuesDetector.java21
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/InefficientWeightDetector.java25
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/LintConstants.java36
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/ManifestOrderDetector.java24
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/MergeRootFrameLayoutDetector.java20
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/NestedScrollingWidgetDetector.java20
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/ProguardDetector.java14
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/PxUsageDetector.java14
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/ScrollViewChildDetector.java25
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/StateListDetector.java17
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/TextFieldDetector.java20
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/TooManyViewsDetector.java22
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/TranslationDetector.java65
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/UnusedResourceDetector.java54
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/UseCompoundDrawableDetector.java25
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/UselessViewDetector.java41
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/MainTest.java25
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/PositionXmlParserTest.java2
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/AbstractCheckTest.java92
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/ExportedServiceDetectorTest.java4
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/TranslationDetectorTest.java15
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/UnusedResourceDetectorTest.java4
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/detector/api/LintUtilsTest.java48
80 files changed, 3484 insertions, 1573 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/.settings/org.moreunit.prefs b/eclipse/plugins/com.android.ide.eclipse.adt/.settings/org.moreunit.prefs
index a1c97ef..7400eff 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/.settings/org.moreunit.prefs
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/.settings/org.moreunit.prefs
@@ -1,4 +1,5 @@
-#Mon Feb 28 13:14:31 PST 2011
+#Sat Nov 05 13:00:35 PDT 2011
eclipse.preferences.version=1
+org.moreunit.prefixes=
org.moreunit.unitsourcefolder=adt\:src\:adt-tests\:unittests\#adt\:src\:adt-tests\:src
org.moreunit.useprojectsettings=true
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF b/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF
index f5982a1..ae9f406 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF
@@ -139,6 +139,9 @@ Export-Package: com.android,
com.android.sdkuilib.internal.widgets;x-friends:="com.android.ide.eclipse.tests",
com.android.sdkuilib.repository;x-friends:="com.android.ide.eclipse.tests",
com.android.sdkuilib.ui;x-friends:="com.android.ide.eclipse.tests",
+ com.android.tools.lint.checks;x-friends:="com.android.ide.eclipse.tests",
+ com.android.tools.lint.client.api;x-friends:="com.android.ide.eclipse.tests",
+ com.android.tools.lint.detector.api;x-friends:="com.android.ide.eclipse.tests",
com.android.util;x-friends:="com.android.ide.eclipse.tests",
org.apache.commons.compress.archivers;x-friends:="com.android.ide.eclipse.tests",
org.apache.commons.compress.archivers.ar;x-friends:="com.android.ide.eclipse.tests",
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml b/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml
index b5030e3..ed103f2 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml
@@ -666,6 +666,18 @@
args="com.android.ide.eclipse.adt.AndroidNature" />
</enabledWhen>
</page>
+ <page
+ class="com.android.ide.eclipse.adt.internal.preferences.LintPreferencePage"
+ id="com.android.ide.eclipse.adt.internal.preferences.LintPreferencePage"
+ name="Android Lint Preferences">
+ <filter
+ name="nature"
+ value="com.android.ide.eclipse.adt.AndroidNature">
+ </filter>
+ <enabledWhen>
+ <adapt type="org.eclipse.core.resources.IProject" />
+ </enabledWhen>
+ </page>
</extension>
<extension point="org.eclipse.ui.actionSets">
<actionSet
@@ -825,6 +837,22 @@
tooltip="Opens the Android SDK Manager">
</action>
</actionSet>
+ <actionSet
+ description="Android Lint"
+ id="adt.actionSet.lint"
+ label="Android Lint"
+ visible="true">
+ <action
+ class="com.android.ide.eclipse.adt.internal.lint.RunLintAction"
+ icon="icons/lintrun.png"
+ id="com.android.ide.eclipse.adt.ui.lintrunner"
+ label="Run Android Lint"
+ menubarPath="Window/additions"
+ style="push"
+ toolbarPath="android_project"
+ tooltip="Runs Android Lint">
+ </action>
+ </actionSet>
</extension>
<extension point="org.eclipse.debug.core.launchDelegates">
<launchDelegate
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtUtils.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtUtils.java
index 80c75e5..05b0853 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtUtils.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtUtils.java
@@ -19,6 +19,7 @@ package com.android.ide.eclipse.adt;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
@@ -297,8 +298,9 @@ public class AdtUtils {
* @return an absolute file system path to the resource
*/
public static IPath getAbsolutePath(IResource resource) {
- IWorkspaceRoot workspace = ResourcesPlugin.getWorkspace().getRoot();
- IPath workspacePath = workspace.getLocation();
+ IWorkspace workspace = ResourcesPlugin.getWorkspace();
+ IWorkspaceRoot root = workspace.getRoot();
+ IPath workspacePath = root.getLocation();
return workspacePath.append(resource.getFullPath());
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidXmlEditor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidXmlEditor.java
index 0753104..ddca596 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidXmlEditor.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidXmlEditor.java
@@ -900,7 +900,9 @@ public abstract class AndroidXmlEditor extends FormEditor implements IResourceCh
* a lock first.
*/
protected void runEditHooks() {
- runLint();
+ if (!mIgnoreXmlUpdate) {
+ runLint();
+ }
}
/**
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java
index 13c7944..13a3fc4 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java
@@ -58,7 +58,7 @@ import com.android.ide.eclipse.adt.internal.editors.manifest.ManifestInfo;
import com.android.ide.eclipse.adt.internal.editors.ui.DecorComposite;
import com.android.ide.eclipse.adt.internal.editors.uimodel.UiDocumentNode;
import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
-import com.android.ide.eclipse.adt.internal.lint.LintEclipseContext;
+import com.android.ide.eclipse.adt.internal.lint.EclipseLintClient;
import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResources;
import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager;
import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
@@ -342,7 +342,7 @@ public class GraphicalEditorPart extends EditorPart
GridData detailsData = new GridData(SWT.FILL, SWT.FILL, false, false, 1, 1);
mActionBar.setLayoutData(detailsData);
if (file != null) {
- mActionBar.updateErrorIndicator(LintEclipseContext.hasMarkers(file));
+ mActionBar.updateErrorIndicator(EclipseLintClient.hasMarkers(file));
}
mSashError = new SashForm(layoutBarAndCanvas, SWT.VERTICAL | SWT.BORDER);
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutActionBar.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutActionBar.java
index 5469d07..1c6c92a 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutActionBar.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutActionBar.java
@@ -28,7 +28,7 @@ import com.android.ide.eclipse.adt.internal.editors.IconFactory;
import com.android.ide.eclipse.adt.internal.editors.layout.configuration.ConfigurationComposite;
import com.android.ide.eclipse.adt.internal.editors.layout.gre.NodeProxy;
import com.android.ide.eclipse.adt.internal.editors.layout.gre.RulesEngine;
-import com.android.ide.eclipse.adt.internal.lint.LintEclipseContext;
+import com.android.ide.eclipse.adt.internal.lint.EclipseLintClient;
import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs;
import com.android.sdkuilib.internal.widgets.ResolutionChooserDialog;
@@ -460,7 +460,7 @@ public class LayoutActionBar extends Composite {
@Override
public void widgetSelected(SelectionEvent e) {
IFile file = mEditor.getLayoutEditor().getInputFile();
- LintEclipseContext.showErrors(getShell(), file);
+ EclipseLintClient.showErrors(getShell(), file);
}
});
@@ -471,7 +471,7 @@ public class LayoutActionBar extends Composite {
* Updates the lint indicator state in the given layout editor
*/
public void updateErrorIndicator() {
- updateErrorIndicator(LintEclipseContext.hasMarkers(mEditor.getEditedFile()));
+ updateErrorIndicator(EclipseLintClient.hasMarkers(mEditor.getEditedFile()));
}
/**
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/ClearLintMarkersAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/ClearLintMarkersAction.java
index 21d90e6..26e5606 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/ClearLintMarkersAction.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/ClearLintMarkersAction.java
@@ -33,8 +33,8 @@ public class ClearLintMarkersAction implements IActionDelegate {
public void run(IAction action) {
IProject project = RunLintAction.getSelectedProject(mSelection);
if (project != null) {
- LintRunner.cancelCurrentJobs();
- LintEclipseContext.clearMarkers(project);
+ LintRunner.cancelCurrentJobs(false);
+ EclipseLintClient.clearMarkers(project);
}
}
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintEclipseContext.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/EclipseLintClient.java
index f4939c6..64d9780 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintEclipseContext.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/EclipseLintClient.java
@@ -21,17 +21,18 @@ import static com.android.ide.eclipse.adt.AdtConstants.MARKER_LINT;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.AdtUtils;
import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditor;
-import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs;
import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
-import com.android.tools.lint.api.DetectorRegistry;
-import com.android.tools.lint.api.IDomParser;
-import com.android.tools.lint.api.ToolContext;
-import com.android.tools.lint.checks.BuiltinDetectorRegistry;
+import com.android.tools.lint.checks.BuiltinIssueRegistry;
+import com.android.tools.lint.client.api.Configuration;
+import com.android.tools.lint.client.api.IDomParser;
+import com.android.tools.lint.client.api.IssueRegistry;
+import com.android.tools.lint.client.api.LintClient;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Detector;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.Location;
import com.android.tools.lint.detector.api.Position;
+import com.android.tools.lint.detector.api.Project;
import com.android.tools.lint.detector.api.Severity;
import com.android.util.Pair;
@@ -42,7 +43,6 @@ import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jdt.core.IJavaProject;
-import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
@@ -64,45 +64,39 @@ import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
/**
* Eclipse implementation for running lint on workspace files and projects.
*/
@SuppressWarnings("restriction") // DOM model
-public class LintEclipseContext extends ToolContext implements IDomParser {
+public class EclipseLintClient extends LintClient implements IDomParser {
static final String MARKER_CHECKID_PROPERTY = "checkid"; //$NON-NLS-1$
private static final String DOCUMENT_PROPERTY = "document"; //$NON-NLS-1$
- private final DetectorRegistry mRegistry;
+ private final IssueRegistry mRegistry;
private final IResource mResource;
private final IDocument mDocument;
- private boolean mFatal;
- private Map<Issue, Severity> mSeverities;
- private String mDisabledIdsList; // String version of map: used to detect when to replace map
- private Set<String> mDisabledIds;
-
+ private boolean mWasFatal;
+ private boolean mFatalOnly;
+ private Configuration mConfiguration;
/**
- * Creates a new {@link LintEclipseContext}.
+ * Creates a new {@link EclipseLintClient}.
*
* @param registry the associated detector registry
* @param resource the associated resource (project, file or null)
* @param document the associated document, or null if the {@code resource}
* param is not a file
+ * @param fatalOnly whether only fatal issues should be reported (and therefore checked)
*/
- public LintEclipseContext(DetectorRegistry registry, IResource resource, IDocument document) {
+ public EclipseLintClient(IssueRegistry registry, IResource resource, IDocument document,
+ boolean fatalOnly) {
mRegistry = registry;
mResource = resource;
mDocument = document;
+ mFatalOnly = fatalOnly;
}
- // ----- Extends ToolContext -----
+ // ----- Extends LintClient -----
@Override
public void log(Throwable exception, String format, Object... args) {
@@ -118,33 +112,6 @@ public class LintEclipseContext extends ToolContext implements IDomParser {
return this;
}
- @Override
- public boolean isEnabled(Issue issue) {
- IPreferenceStore store = AdtPlugin.getDefault().getPreferenceStore();
- String idList = store.getString(AdtPrefs.PREFS_DISABLED_ISSUES);
- if (idList == null) {
- idList = ""; //$NON-NLS-1$
- }
- if (mDisabledIds == null || !mDisabledIdsList.equals(idList)) {
- mDisabledIdsList = idList;
- if (idList.length() > 0) {
- String[] ids = idList.split(","); //$NON-NLS-1$
- mDisabledIds = new HashSet<String>(ids.length);
- for (String s : ids) {
- mDisabledIds.add(s);
- }
- } else {
- mDisabledIds = Collections.emptySet();
- }
- }
-
- if (mDisabledIds.contains(issue.getId())) {
- return false;
- }
-
- return issue.isEnabledByDefault();
- }
-
// ----- Implements IDomParser -----
public Document parse(Context context) {
@@ -215,82 +182,24 @@ public class LintEclipseContext extends ToolContext implements IDomParser {
}
@Override
- public Severity getSeverity(Issue issue) {
- if (mSeverities == null) {
- mSeverities = new HashMap<Issue, Severity>();
- IPreferenceStore store = AdtPlugin.getDefault().getPreferenceStore();
- String assignments = store.getString(AdtPrefs.PREFS_LINT_SEVERITIES);
- if (assignments != null && assignments.length() > 0) {
- for (String assignment : assignments.split(",")) { //$NON-NLS-1$
- String[] s = assignment.split("="); //$NON-NLS-1$
- if (s.length == 2) {
- Issue d = mRegistry.getIssue(s[0]);
- if (d != null) {
- Severity severity = Severity.valueOf(s[1]);
- if (severity != null) {
- mSeverities.put(d, severity);
- }
- }
- }
- }
- }
- }
-
- Severity severity = mSeverities.get(issue);
- if (severity != null) {
- return severity;
- }
-
- return issue.getDefaultSeverity();
- }
-
- /**
- * Sets the custom severity for the given detector to the given new severity
- *
- * @param severities a map from detector to severity to use from now on
- */
- public void setSeverities(Map<Issue, Severity> severities) {
- mSeverities = null;
-
- IPreferenceStore store = AdtPlugin.getDefault().getPreferenceStore();
- if (severities.size() == 0) {
- store.setToDefault(AdtPrefs.PREFS_LINT_SEVERITIES);
- return;
- }
- List<Issue> sortedKeys = new ArrayList<Issue>(severities.keySet());
- Collections.sort(sortedKeys);
-
- StringBuilder sb = new StringBuilder(severities.size() * 20);
- for (Issue issue : sortedKeys) {
- Severity severity = severities.get(issue);
- if (severity != issue.getDefaultSeverity()) {
- if (sb.length() > 0) {
- sb.append(',');
- }
- sb.append(issue.getId());
- sb.append('=');
- sb.append(severity.name());
+ public Configuration getConfiguration(Project project) {
+ if (mConfiguration == null) {
+ if (mResource != null) {
+ IProject eclipseProject = mResource.getProject();
+ mConfiguration = ProjectLintConfiguration.get(this, eclipseProject, mFatalOnly);
+ } else {
+ mConfiguration = GlobalLintConfiguration.get();
}
}
-
- if (sb.length() > 0) {
- store.setValue(AdtPrefs.PREFS_LINT_SEVERITIES, sb.toString());
- } else {
- store.setToDefault(AdtPrefs.PREFS_LINT_SEVERITIES);
- }
+ return mConfiguration;
}
@Override
public void report(Context context, Issue issue, Location location, String message,
Object data) {
- if (!isEnabled(issue)) {
- return;
- }
-
- Severity s = getSeverity(issue);
- if (s == Severity.IGNORE) {
- return;
- }
+ assert context.configuration.isEnabled(issue);
+ assert context.configuration.getSeverity(issue) != Severity.IGNORE;
+ Severity s = context.configuration.getSeverity(issue);
int severity = getMarkerSeverity(s);
IMarker marker = null;
@@ -335,7 +244,7 @@ public class LintEclipseContext extends ToolContext implements IDomParser {
}
if (s == Severity.ERROR) {
- mFatal = true;
+ mWasFatal = true;
}
}
@@ -501,8 +410,8 @@ public class LintEclipseContext extends ToolContext implements IDomParser {
*
* @return true if a fatal error was encountered
*/
- public boolean isFatal() {
- return mFatal;
+ public boolean hasFatalErrors() {
+ return mWasFatal;
}
/**
@@ -512,7 +421,7 @@ public class LintEclipseContext extends ToolContext implements IDomParser {
* @return a full description of the corresponding issue, never null
*/
public static String describe(IMarker marker) {
- DetectorRegistry registry = getRegistry();
+ IssueRegistry registry = getRegistry();
Issue issue = registry.getIssue(getId(marker));
String summary = issue.getDescription();
String explanation = issue.getExplanation();
@@ -649,40 +558,23 @@ public class LintEclipseContext extends ToolContext implements IDomParser {
}
/**
- * Returns the registry of detectors to use from within Eclipse. This method
- * should be used rather than calling
- * {@link BuiltinDetectorRegistry#BuiltinDetectorRegistry} directly since it can replace
- * some detectors with Eclipse-optimized replacements.
+ * Returns the registry of issues to check from within Eclipse.
*
- * @return the detector registry to use to access detectors and issues
+ * @return the issue registry to use to access detectors and issues
*/
- public static DetectorRegistry getRegistry() {
- return new EclipseDetectorRegistry();
+ public static IssueRegistry getRegistry() {
+ return new BuiltinIssueRegistry();
}
- /**
- * Custom Eclipse registry which replaces some builtin checks with
- * Eclipse-optimized versions
- */
- private static class EclipseDetectorRegistry extends BuiltinDetectorRegistry {
- private static final List<Detector> sDetectors;
- static {
- List<Detector> detectors = new ArrayList<Detector>(20);
- for (Detector detector : new BuiltinDetectorRegistry().getDetectors()) {
- // Replace the generic UnusedResourceDetector with an Eclipse optimized one
- // which uses the Java AST
- if (detector instanceof com.android.tools.lint.checks.UnusedResourceDetector) {
- detectors.add(new UnusedResourceDetector());
- } else {
- detectors.add(detector);
- }
- }
- sDetectors = Collections.unmodifiableList(detectors);
- }
- @Override
- public List<? extends Detector> getDetectors() {
- return sDetectors;
+ @Override
+ public Class<? extends Detector> replaceDetector(Class<? extends Detector> detectorClass) {
+ // Replace the generic UnusedResourceDetector with an Eclipse optimized one
+ // which uses the Java AST
+ if (detectorClass == com.android.tools.lint.checks.UnusedResourceDetector.class) {
+ return UnusedResourceDetector.class;
}
+
+ return detectorClass;
}
private static class OffsetPosition extends Position {
@@ -768,15 +660,8 @@ public class LintEclipseContext extends ToolContext implements IDomParser {
}
}
- /** Specialized context which only provides fatal issues as enabled */
- static class FatalContext extends LintEclipseContext {
- public FatalContext(DetectorRegistry registry, IResource resource, IDocument document) {
- super(registry, resource, document);
- }
-
- @Override
- public boolean isEnabled(Issue issue) {
- return super.isEnabled(issue) && getSeverity(issue) == Severity.ERROR;
- }
+ public void dispose(Context context) {
+ // TODO: Consider leaving read-lock on the document in parse() and freeing it here.
}
}
+
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/GlobalLintConfiguration.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/GlobalLintConfiguration.java
new file mode 100644
index 0000000..8da3465
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/GlobalLintConfiguration.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.ide.eclipse.adt.internal.lint;
+
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs;
+import com.android.tools.lint.client.api.Configuration;
+import com.android.tools.lint.client.api.IssueRegistry;
+import com.android.tools.lint.detector.api.Context;
+import com.android.tools.lint.detector.api.Issue;
+import com.android.tools.lint.detector.api.Location;
+import com.android.tools.lint.detector.api.Severity;
+
+import org.eclipse.jface.preference.IPreferenceStore;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/** Global (non-project-specific) configuration for Lint in Eclipse */
+class GlobalLintConfiguration extends Configuration {
+ private static final GlobalLintConfiguration sInstance = new GlobalLintConfiguration();
+
+ private Map<Issue, Severity> mSeverities;
+ private boolean mBulkEditing;
+
+ private GlobalLintConfiguration() {
+ }
+
+ /**
+ * Obtain a reference to the singleton
+ *
+ * @return the singleton configuration
+ */
+ public static GlobalLintConfiguration get() {
+ return sInstance;
+ }
+
+ @Override
+ public Severity getSeverity(Issue issue) {
+ if (mSeverities == null) {
+ IssueRegistry registry = EclipseLintClient.getRegistry();
+ mSeverities = new HashMap<Issue, Severity>();
+ IPreferenceStore store = getStore();
+ String assignments = store.getString(AdtPrefs.PREFS_LINT_SEVERITIES);
+ if (assignments != null && assignments.length() > 0) {
+ for (String assignment : assignments.split(",")) { //$NON-NLS-1$
+ String[] s = assignment.split("="); //$NON-NLS-1$
+ if (s.length == 2) {
+ Issue d = registry.getIssue(s[0]);
+ if (d != null) {
+ Severity severity = Severity.valueOf(s[1]);
+ if (severity != null) {
+ mSeverities.put(d, severity);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ Severity severity = mSeverities.get(issue);
+ if (severity != null) {
+ return severity;
+ }
+
+ if (!issue.isEnabledByDefault()) {
+ return Severity.IGNORE;
+ }
+
+ return issue.getDefaultSeverity();
+ }
+
+ private IPreferenceStore getStore() {
+ IPreferenceStore store = AdtPlugin.getDefault().getPreferenceStore();
+ return store;
+ }
+
+ @Override
+ public void ignore(Context context, Issue issue, Location location, String message,
+ Object data) {
+ throw new UnsupportedOperationException(
+ "Can't ignore() in global configurations"); //$NON-NLS-1$
+ }
+
+ @Override
+ public void setSeverity(Issue issue, Severity severity) {
+ if (mSeverities == null) {
+ // Force initialization
+ getSeverity(issue);
+ }
+
+ if (severity == null) {
+ mSeverities.remove(issue);
+ } else {
+ mSeverities.put(issue, severity);
+ }
+
+ if (!mBulkEditing) {
+ setSeverities(mSeverities);
+ }
+ }
+
+ /**
+ * Sets the custom severities for the given issues, in bulk.
+ *
+ * @param severities a map from detector to severity to use from now on
+ * @return true if something changed from the current settings
+ */
+ private boolean setSeverities(Map<Issue, Severity> severities) {
+ mSeverities = severities;
+
+ String value = "";
+ if (severities.size() > 0) {
+ List<Issue> sortedKeys = new ArrayList<Issue>(severities.keySet());
+ Collections.sort(sortedKeys);
+
+ StringBuilder sb = new StringBuilder(severities.size() * 20);
+ for (Issue issue : sortedKeys) {
+ Severity severity = severities.get(issue);
+ if (severity != issue.getDefaultSeverity()) {
+ if (sb.length() > 0) {
+ sb.append(',');
+ }
+ sb.append(issue.getId());
+ sb.append('=');
+ sb.append(severity.name());
+ }
+ }
+
+ value = sb.toString();
+ }
+
+ IPreferenceStore store = getStore();
+ String previous = store.getString(AdtPrefs.PREFS_LINT_SEVERITIES);
+ boolean changed = !value.equals(previous);
+ if (changed) {
+ if (value.length() == 0) {
+ store.setToDefault(AdtPrefs.PREFS_LINT_SEVERITIES);
+ } else {
+ store.setValue(AdtPrefs.PREFS_LINT_SEVERITIES, value);
+ }
+ }
+
+ return changed;
+ }
+
+ @Override
+ public void startBulkEditing() {
+ mBulkEditing = true;
+ }
+
+ @Override
+ public void finishBulkEditing() {
+ mBulkEditing = false;
+ setSeverities(mSeverities);
+ }
+} \ No newline at end of file
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintFix.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintFix.java
index c1a7169..dcbb6d3 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintFix.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintFix.java
@@ -24,7 +24,6 @@ import static com.android.ide.common.layout.LayoutConstants.ATTR_ORIENTATION;
import static com.android.ide.common.layout.LayoutConstants.VALUE_N_DP;
import static com.android.ide.common.layout.LayoutConstants.VALUE_VERTICAL;
import static com.android.ide.common.layout.LayoutConstants.VALUE_ZERO_DP;
-import static com.android.tools.lint.checks.LintConstants.ATTR_PERMISSION;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.AdtUtils;
@@ -36,7 +35,6 @@ import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
import com.android.ide.eclipse.adt.internal.refactorings.extractstring.ExtractStringRefactoring;
import com.android.ide.eclipse.adt.internal.refactorings.extractstring.ExtractStringWizard;
import com.android.tools.lint.checks.AccessibilityDetector;
-import com.android.tools.lint.checks.BuiltinDetectorRegistry;
import com.android.tools.lint.checks.ExportedServiceDetector;
import com.android.tools.lint.checks.HardcodedValuesDetector;
import com.android.tools.lint.checks.InefficientWeightDetector;
@@ -44,6 +42,7 @@ import com.android.tools.lint.checks.PxUsageDetector;
import com.android.tools.lint.checks.TextFieldDetector;
import com.android.tools.lint.checks.UselessViewDetector;
import com.android.tools.lint.detector.api.Issue;
+import com.android.tools.lint.detector.api.LintConstants;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
@@ -131,7 +130,7 @@ abstract class LintFix implements ICompletionProposal {
}
public String getAdditionalProposalInfo() {
- Issue issue = new BuiltinDetectorRegistry().getIssue(mId);
+ Issue issue = EclipseLintClient.getRegistry().getIssue(mId);
if (issue != null) {
return issue.getExplanation().replace("\n", "<br>"); //$NON-NLS-1$ //$NON-NLS-2$
}
@@ -340,7 +339,7 @@ abstract class LintFix implements ICompletionProposal {
@Override
protected String getAttribute() {
- return ATTR_PERMISSION;
+ return LintConstants.ATTR_PERMISSION;
}
@Override
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintFixGenerator.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintFixGenerator.java
index 59e299c..9ac0b4d 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintFixGenerator.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintFixGenerator.java
@@ -20,14 +20,18 @@ import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.AdtUtils;
import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs;
+import com.android.tools.lint.client.api.Configuration;
+import com.android.tools.lint.client.api.DefaultConfiguration;
+import com.android.tools.lint.client.api.IssueRegistry;
import com.android.tools.lint.detector.api.Issue;
+import com.android.tools.lint.detector.api.Severity;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.dialogs.MessageDialog;
-import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.jface.text.contentassist.IContextInformation;
@@ -43,6 +47,7 @@ import org.eclipse.ui.IMarkerResolutionGenerator2;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.PlatformUI;
+import java.io.File;
import java.util.ArrayList;
import java.util.List;
@@ -103,10 +108,9 @@ public class LintFixGenerator implements IMarkerResolutionGenerator2, IQuickAssi
IResource resource = marker.getResource();
return new IMarkerResolution[] {
new MoreInfoProposal(id, marker.getAttribute(IMarker.MESSAGE, null)),
- new SuppressProposal(id, true /* all */),
- // Not yet implemented
- //new SuppressProposal(id, false),
- new ClearMarkersProposal(resource, false /* all */),
+ new SuppressProposal(resource, id, false),
+ new SuppressProposal(resource.getProject(), id, true /* all */),
+ new SuppressProposal(resource, id, true /* all */),
new ClearMarkersProposal(resource, true /* all */),
};
}
@@ -148,11 +152,10 @@ public class LintFixGenerator implements IMarkerResolutionGenerator2, IQuickAssi
String message = marker.getAttribute(IMarker.MESSAGE, null);
proposals.add(new MoreInfoProposal(id, message));
- proposals.add(new SuppressProposal(id, true /* all */));
- // Not yet implemented
- //proposals.add(new SuppressProposal(id, false));
+ proposals.add(new SuppressProposal(file, id, false));
+ proposals.add(new SuppressProposal(file.getProject(), id, true /* all */));
+ proposals.add(new SuppressProposal(file, id, true /* all */));
- proposals.add(new ClearMarkersProposal(file, false /* all */));
proposals.add(new ClearMarkersProposal(file, true /* all */));
}
}
@@ -169,40 +172,52 @@ public class LintFixGenerator implements IMarkerResolutionGenerator2, IQuickAssi
*
* @param id the id of the detector to be suppressed, or null
* @param updateMarkers if true, update all markers
+ * @param resource the resource associated with the markers
+ * @param thisFileOnly if true, only suppress this issue in this file
*/
- public static void suppressDetector(String id, boolean updateMarkers, IResource resource) {
- // Excluded checks
- IPreferenceStore store = AdtPlugin.getDefault().getPreferenceStore();
- String value = store.getString(AdtPrefs.PREFS_DISABLED_ISSUES);
- assert value == null || !value.contains(id);
- if (value == null || value.length() == 0) {
- value = id;
- } else {
- value = value + ',' + id;
- }
- store.setValue(AdtPrefs.PREFS_DISABLED_ISSUES, value);
+ public static void suppressDetector(String id, boolean updateMarkers, IResource resource,
+ boolean thisFileOnly) {
+ IssueRegistry registry = EclipseLintClient.getRegistry();
+ Issue issue = registry.getIssue(id);
+ if (issue != null) {
+ EclipseLintClient mClient = new EclipseLintClient(registry, resource, null, false);
+ Configuration configuration = mClient.getConfiguration(null);
+ if (thisFileOnly && configuration instanceof DefaultConfiguration) {
+ File file = AdtUtils.getAbsolutePath(resource).toFile();
+ ((DefaultConfiguration) configuration).ignore(issue, file);
+ } else {
+ configuration.setSeverity(issue, Severity.IGNORE);
+ }
+ }
if (updateMarkers) {
- LintEclipseContext.removeMarkers(resource, id);
+ EclipseLintClient.removeMarkers(resource, id);
}
}
private static class SuppressProposal implements ICompletionProposal, IMarkerResolution2 {
private final String mId;
private final boolean mGlobal;
+ private final IResource mResource;
- public SuppressProposal(String check, boolean global) {
- super();
+ private SuppressProposal(IResource resource, String check, boolean global) {
+ mResource = resource;
mId = check;
mGlobal = global;
}
private void perform() {
- suppressDetector(mId, true, null);
+ suppressDetector(mId, true, mResource, !mGlobal);
}
public String getDisplayString() {
- return mGlobal ? "Disable Check" : "Disable Check in this file only";
+ if (mResource instanceof IProject) {
+ return "Disable Check in This Project";
+ } else if (mGlobal) {
+ return "Disable Check";
+ } else {
+ return "Disable Check in This File Only";
+ }
}
// ---- Implements MarkerResolution2 ----
@@ -231,7 +246,9 @@ public class LintFixGenerator implements IMarkerResolutionGenerator2, IQuickAssi
public String getAdditionalProposalInfo() {
StringBuilder sb = new StringBuilder(200);
- if (mGlobal) {
+ if (mResource instanceof IProject) {
+ sb.append("Suppresses this type of lint warning in the current project only.");
+ } else if (mGlobal) {
sb.append("Suppresses this type of lint warning in all files.");
} else {
sb.append("Suppresses this type of lint warning in the current file only.");
@@ -263,7 +280,7 @@ public class LintFixGenerator implements IMarkerResolutionGenerator2, IQuickAssi
private void perform() {
IResource resource = mGlobal ? mResource.getProject() : mResource;
- LintEclipseContext.clearMarkers(resource);
+ EclipseLintClient.clearMarkers(resource);
}
public String getDisplayString() {
@@ -316,7 +333,7 @@ public class LintFixGenerator implements IMarkerResolutionGenerator2, IQuickAssi
public Image getImage() {
ISharedImages sharedImages = PlatformUI.getWorkbench().getSharedImages();
- return sharedImages.getImage(ISharedImages.IMG_OBJS_WARN_TSK);
+ return sharedImages.getImage(ISharedImages.IMG_ELCL_REMOVE);
}
public IContextInformation getContextInformation() {
@@ -334,7 +351,7 @@ public class LintFixGenerator implements IMarkerResolutionGenerator2, IQuickAssi
}
private void perform() {
- Issue issue = LintEclipseContext.getRegistry().getIssue(mId);
+ Issue issue = EclipseLintClient.getRegistry().getIssue(mId);
assert issue != null : mId;
StringBuilder sb = new StringBuilder(300);
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintList.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintList.java
index 546921d..24f7ab9 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintList.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintList.java
@@ -21,7 +21,7 @@ import com.android.ide.eclipse.adt.internal.editors.IconFactory;
import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditor;
import com.android.ide.eclipse.adt.internal.editors.layout.gle2.GraphicalEditorPart;
import com.android.ide.eclipse.adt.internal.editors.layout.gle2.LayoutActionBar;
-import com.android.tools.lint.api.DetectorRegistry;
+import com.android.tools.lint.client.api.IssueRegistry;
import com.android.tools.lint.detector.api.Issue;
import org.eclipse.core.resources.IFile;
@@ -172,7 +172,7 @@ class LintList extends Composite implements IResourceChangeListener, ControlList
List<IMarker> markerList = new ArrayList<IMarker>();
if (mResources != null) {
for (IResource resource : mResources) {
- IMarker[] markers = LintEclipseContext.getMarkers(resource);
+ IMarker[] markers = EclipseLintClient.getMarkers(resource);
for (IMarker marker : markers) {
markerList.add(marker);
int severity = marker.getAttribute(IMarker.SEVERITY, 0);
@@ -184,14 +184,14 @@ class LintList extends Composite implements IResourceChangeListener, ControlList
}
}
- final DetectorRegistry registry = LintEclipseContext.getRegistry();
+ final IssueRegistry registry = EclipseLintClient.getRegistry();
Collections.sort(markerList, new Comparator<IMarker>() {
public int compare(IMarker marker1, IMarker marker2) {
// Sort by priority, then by category, then by id,
// then by file, then by line
- String id1 = LintEclipseContext.getId(marker1);
- String id2 = LintEclipseContext.getId(marker2);
+ String id1 = EclipseLintClient.getId(marker1);
+ String id2 = EclipseLintClient.getId(marker2);
if (id1 == null || id2 == null) {
return marker1.getResource().getName().compareTo(
marker2.getResource().getName());
@@ -299,12 +299,12 @@ class LintList extends Composite implements IResourceChangeListener, ControlList
ISharedImages sharedImages = PlatformUI.getWorkbench().getSharedImages();
switch (severity) {
case IMarker.SEVERITY_ERROR:
- if (LintFix.hasFix(LintEclipseContext.getId(marker))) {
+ if (LintFix.hasFix(EclipseLintClient.getId(marker))) {
return IconFactory.getInstance().getIcon("quickfix_error"); //$NON-NLS-1$
}
return sharedImages.getImage(ISharedImages.IMG_OBJS_ERROR_TSK);
case IMarker.SEVERITY_WARNING:
- if (LintFix.hasFix(LintEclipseContext.getId(marker))) {
+ if (LintFix.hasFix(EclipseLintClient.getId(marker))) {
return IconFactory.getInstance().getIcon("quickfix_warning"); //$NON-NLS-1$
}
return sharedImages.getImage(ISharedImages.IMG_OBJS_WARN_TSK);
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintListDialog.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintListDialog.java
index df41cde..5daa632 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintListDialog.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintListDialog.java
@@ -167,7 +167,7 @@ class LintListDialog extends TitleAreaDialog implements SelectionListener {
return;
}
- mDetailsText.setText(LintEclipseContext.describe(marker));
+ mDetailsText.setText(EclipseLintClient.describe(marker));
}
// ---- Implements SelectionListener ----
@@ -180,12 +180,12 @@ class LintListDialog extends TitleAreaDialog implements SelectionListener {
} else if (source == mShowButton) {
List<IMarker> selection = mList.getSelectedMarkers();
if (selection.size() > 0) {
- LintEclipseContext.showMarker(selection.get(0));
+ EclipseLintClient.showMarker(selection.get(0));
}
} else if (source == mFixButton) {
List<IMarker> selection = mList.getSelectedMarkers();
for (IMarker marker : selection) {
- LintFix fix = LintFix.getFix(LintEclipseContext.getId(marker), marker);
+ LintFix fix = LintFix.getFix(EclipseLintClient.getId(marker), marker);
IEditorPart editor = AdtUtils.getActiveEditor();
if (editor instanceof AndroidXmlEditor) {
IStructuredDocument doc = ((AndroidXmlEditor) editor).getStructuredDocument();
@@ -199,9 +199,9 @@ class LintListDialog extends TitleAreaDialog implements SelectionListener {
}
} else if (source == mIgnoreTypeButton) {
for (IMarker marker : mList.getSelectedMarkers()) {
- String id = LintEclipseContext.getId(marker);
+ String id = EclipseLintClient.getId(marker);
if (id != null) {
- LintFixGenerator.suppressDetector(id, true, mFile);
+ LintFixGenerator.suppressDetector(id, true, mFile, true /*all*/);
}
}
}
@@ -218,14 +218,14 @@ class LintListDialog extends TitleAreaDialog implements SelectionListener {
boolean canFix = selection.size() > 0;
for (IMarker marker : selection) {
- if (!LintFix.hasFix(LintEclipseContext.getId(marker))) {
+ if (!LintFix.hasFix(EclipseLintClient.getId(marker))) {
canFix = false;
break;
}
// Some fixes cannot be run in bulk
if (selection.size() > 1) {
- LintFix fix = LintFix.getFix(LintEclipseContext.getId(marker), marker);
+ LintFix fix = LintFix.getFix(EclipseLintClient.getId(marker), marker);
if (!fix.isBulkCapable()) {
canFix = false;
break;
@@ -242,7 +242,7 @@ class LintListDialog extends TitleAreaDialog implements SelectionListener {
// Jump to editor
List<IMarker> selection = mList.getSelectedMarkers();
if (selection.size() > 0) {
- LintEclipseContext.showMarker(selection.get(0));
+ EclipseLintClient.showMarker(selection.get(0));
close();
}
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintRunner.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintRunner.java
index 495625f..f50f660 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintRunner.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintRunner.java
@@ -21,8 +21,8 @@ import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.AdtUtils;
import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs;
import com.android.sdklib.SdkConstants;
-import com.android.tools.lint.api.DetectorRegistry;
-import com.android.tools.lint.api.Lint;
+import com.android.tools.lint.client.api.IssueRegistry;
+import com.android.tools.lint.client.api.Lint;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.Scope;
@@ -81,7 +81,7 @@ public class LintRunner {
*/
public static Job startLint(IResource resource, IDocument doc, boolean fatalOnly) {
if (resource != null) {
- cancelCurrentJobs();
+ cancelCurrentJobs(false);
CheckFileJob job = new CheckFileJob(resource, doc, fatalOnly);
job.schedule();
@@ -124,12 +124,23 @@ public class LintRunner {
return jobManager.find(CheckFileJob.FAMILY_RUN_LINT);
}
- /** Cancels the current lint jobs, if any */
- static void cancelCurrentJobs() {
+ /** Cancels the current lint jobs, if any, and optionally waits for them to finish */
+ static void cancelCurrentJobs(boolean wait) {
// Cancel any current running jobs first
- for (Job job : getCurrentJobs()) {
+ Job[] currentJobs = getCurrentJobs();
+ for (Job job : currentJobs) {
job.cancel();
}
+
+ if (wait) {
+ for (Job job : currentJobs) {
+ try {
+ job.join();
+ } catch (InterruptedException e) {
+ AdtPlugin.log(e, null);
+ }
+ }
+ }
}
private static final class CheckFileJob extends Job {
@@ -165,7 +176,7 @@ public class LintRunner {
protected IStatus run(IProgressMonitor monitor) {
try {
monitor.beginTask("Looking for errors", IProgressMonitor.UNKNOWN);
- DetectorRegistry registry = LintEclipseContext.getRegistry();
+ IssueRegistry registry = EclipseLintClient.getRegistry();
File file = AdtUtils.getAbsolutePath(mResource).toFile();
EnumSet<Scope> scope;
if (mResource instanceof IProject) {
@@ -182,7 +193,7 @@ public class LintRunner {
"Only XML files are supported for single file lint", null); //$NON-NLS-1$
}
if (scope == Scope.RESOURCE_FILE_SCOPE || scope == EnumSet.of(Scope.MANIFEST)) {
- IMarker[] markers = LintEclipseContext.getMarkers(mResource);
+ IMarker[] markers = EclipseLintClient.getMarkers(mResource);
for (IMarker marker : markers) {
String id = marker.getAttribute(MARKER_CHECKID_PROPERTY, ""); //$NON-NLS-1$
Issue issue = registry.getIssue(id);
@@ -191,19 +202,14 @@ public class LintRunner {
}
}
} else {
- LintEclipseContext.clearMarkers(mResource);
+ EclipseLintClient.clearMarkers(mResource);
}
- LintEclipseContext toolContext;
- if (mFatalOnly) {
- toolContext = new LintEclipseContext.FatalContext(registry, mResource,
- mDocument);
- } else {
- toolContext = new LintEclipseContext(registry, mResource, mDocument);
- }
- mLint = new Lint(registry, toolContext, scope);
- mLint.analyze(Collections.singletonList(file));
- mFatal = toolContext.isFatal();
+ EclipseLintClient client = new EclipseLintClient(registry, mResource,
+ mDocument, mFatalOnly);
+ mLint = new Lint(registry, client);
+ mLint.analyze(Collections.singletonList(file), scope);
+ mFatal = client.hasFatalErrors();
return Status.OK_STATUS;
} catch (Exception e) {
return new Status(Status.ERROR, AdtPlugin.PLUGIN_ID, Status.ERROR,
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintViewPart.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintViewPart.java
index c74d546..f9a78a5 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintViewPart.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintViewPart.java
@@ -126,15 +126,7 @@ public class LintViewPart extends ViewPart implements SelectionListener, IJobCha
// If there are currently running jobs, listen for them such that we can update the
// button state
- Job[] currentJobs = LintRunner.getCurrentJobs();
- if (currentJobs.length > 0) {
- ISharedImages sharedImages = PlatformUI.getWorkbench().getSharedImages();
- mRefreshAction.setImageDescriptor(sharedImages.getImageDescriptor(
- ISharedImages.IMG_ELCL_STOP));
- for (Job job : currentJobs) {
- job.addJobChangeListener(this);
- }
- }
+ refreshStopIcon();
if (sInitialProject != null) {
mLintView.setResources(Collections.<IResource>singletonList(sInitialProject));
@@ -184,11 +176,11 @@ public class LintViewPart extends ViewPart implements SelectionListener, IJobCha
*/
private void initializeToolBar() {
IToolBarManager toolbarManager = getViewSite().getActionBars().getToolBarManager();
+ toolbarManager.add(mRefreshAction);
toolbarManager.add(mFixAction);
toolbarManager.add(mIgnoreAction);
toolbarManager.add(mRemoveAction);
toolbarManager.add(mRemoveAllAction);
- toolbarManager.add(mRefreshAction);
}
@Override
@@ -203,6 +195,25 @@ public class LintViewPart extends ViewPart implements SelectionListener, IJobCha
*/
public void setResources(List<IResource> resources) {
mLintView.setResources(resources);
+
+ // Refresh the stop/refresh icon status
+ refreshStopIcon();
+ }
+
+ private void refreshStopIcon() {
+ Job[] currentJobs = LintRunner.getCurrentJobs();
+ if (currentJobs.length > 0) {
+ ISharedImages sharedImages = PlatformUI.getWorkbench().getSharedImages();
+ mRefreshAction.setImageDescriptor(sharedImages.getImageDescriptor(
+ ISharedImages.IMG_ELCL_STOP));
+ for (Job job : currentJobs) {
+ job.addJobChangeListener(this);
+ }
+ } else {
+ mRefreshAction.setImageDescriptor(
+ IconFactory.getInstance().getImageDescriptor(REFRESH_ICON));
+
+ }
}
// ---- Implements SelectionListener ----
@@ -212,7 +223,7 @@ public class LintViewPart extends ViewPart implements SelectionListener, IJobCha
if (markers.size() != 1) {
mDetailsText.setText(""); //$NON-NLS-1$
} else {
- mDetailsText.setText(LintEclipseContext.describe(markers.get(0)));
+ mDetailsText.setText(EclipseLintClient.describe(markers.get(0)));
}
updateIssueCount();
@@ -224,14 +235,14 @@ public class LintViewPart extends ViewPart implements SelectionListener, IJobCha
boolean hasSelection = markers.size() > 0;
boolean canFix = hasSelection;
for (IMarker marker : markers) {
- if (!LintFix.hasFix(LintEclipseContext.getId(marker))) {
+ if (!LintFix.hasFix(EclipseLintClient.getId(marker))) {
canFix = false;
break;
}
// Some fixes cannot be run in bulk
if (markers.size() > 1) {
- LintFix fix = LintFix.getFix(LintEclipseContext.getId(marker), marker);
+ LintFix fix = LintFix.getFix(EclipseLintClient.getId(marker), marker);
if (!fix.isBulkCapable()) {
canFix = false;
break;
@@ -254,7 +265,7 @@ public class LintViewPart extends ViewPart implements SelectionListener, IJobCha
// Jump to editor
List<IMarker> selection = mLintView.getSelectedMarkers();
if (selection.size() > 0) {
- LintEclipseContext.showMarker(selection.get(0));
+ EclipseLintClient.showMarker(selection.get(0));
}
}
}
@@ -326,7 +337,7 @@ public class LintViewPart extends ViewPart implements SelectionListener, IJobCha
case ACTION_REFRESH: {
Job[] jobs = LintRunner.getCurrentJobs();
if (jobs.length > 0) {
- LintRunner.cancelCurrentJobs();
+ LintRunner.cancelCurrentJobs(false);
} else {
List<IResource> resources = mLintView.getResources();
if (resources == null) {
@@ -348,7 +359,7 @@ public class LintViewPart extends ViewPart implements SelectionListener, IJobCha
case ACTION_FIX: {
List<IMarker> markers = mLintView.getSelectedMarkers();
for (IMarker marker : markers) {
- LintFix fix = LintFix.getFix(LintEclipseContext.getId(marker), marker);
+ LintFix fix = LintFix.getFix(EclipseLintClient.getId(marker), marker);
IEditorPart editor = AdtUtils.getActiveEditor();
if (editor instanceof AndroidXmlEditor) {
IStructuredDocument doc =
@@ -375,15 +386,16 @@ public class LintViewPart extends ViewPart implements SelectionListener, IJobCha
List<IResource> resources = mLintView.getResources();
if (resources != null) {
for (IResource resource : resources) {
- LintEclipseContext.clearMarkers(resource);
+ EclipseLintClient.clearMarkers(resource);
}
}
break;
case ACTION_IGNORE: {
for (IMarker marker : mLintView.getSelectedMarkers()) {
- String id = LintEclipseContext.getId(marker);
+ String id = EclipseLintClient.getId(marker);
if (id != null) {
- LintFixGenerator.suppressDetector(id, true, null);
+ // TODO: Add "ignore in all" button!
+ LintFixGenerator.suppressDetector(id, true, null, true/*all*/);
}
}
break;
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/ProjectLintConfiguration.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/ProjectLintConfiguration.java
new file mode 100644
index 0000000..dd9bd5f
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/ProjectLintConfiguration.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.ide.eclipse.adt.internal.lint;
+
+import com.android.annotations.VisibleForTesting;
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.AdtUtils;
+import com.android.tools.lint.client.api.Configuration;
+import com.android.tools.lint.client.api.DefaultConfiguration;
+import com.android.tools.lint.client.api.LintClient;
+import com.android.tools.lint.detector.api.Issue;
+import com.android.tools.lint.detector.api.Project;
+import com.android.tools.lint.detector.api.Severity;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.QualifiedName;
+
+import java.io.File;
+
+/** Configuration for Lint in Eclipse projects */
+class ProjectLintConfiguration extends DefaultConfiguration {
+ private boolean mFatalOnly;
+
+ private final static QualifiedName CONFIGURATION_NAME = new QualifiedName(AdtPlugin.PLUGIN_ID,
+ "lintconfig"); //$NON-NLS-1$
+
+ @VisibleForTesting
+ ProjectLintConfiguration(LintClient client, Project project,
+ Configuration parent, boolean fatalOnly) {
+ super(client, project, parent);
+ mFatalOnly = fatalOnly;
+ }
+
+ private static ProjectLintConfiguration create(LintClient client, IProject project,
+ Configuration parent, boolean fatalOnly) {
+ File dir = AdtUtils.getAbsolutePath(project).toFile();
+ Project lingProject = new Project(client, dir, dir);
+ return new ProjectLintConfiguration(client, lingProject, parent, fatalOnly);
+ }
+
+ public static ProjectLintConfiguration get(LintClient client, IProject project,
+ boolean fatalOnly) {
+ // Don't cache fatal-only configurations: they're only used occasionally and typically
+ // not repeatedly
+ if (fatalOnly) {
+ return create(client, project, GlobalLintConfiguration.get(), true);
+ }
+
+ ProjectLintConfiguration configuration = null;
+ try {
+ Object value = project.getSessionProperty(CONFIGURATION_NAME);
+ configuration = (ProjectLintConfiguration) value;
+ } catch (CoreException e) {
+ // Not a problem; we will just create a new one
+ }
+ if (configuration == null) {
+ configuration = create(client, project, GlobalLintConfiguration.get(), false);
+ try {
+ project.setSessionProperty(CONFIGURATION_NAME, configuration);
+ } catch (CoreException e) {
+ AdtPlugin.log(e, "Can't store lint configuration");
+ }
+ }
+ return configuration;
+ }
+
+ @Override
+ public Severity getSeverity(Issue issue) {
+ Severity severity = super.getSeverity(issue);
+ if (mFatalOnly && severity != Severity.ERROR) {
+ return Severity.IGNORE;
+ }
+ return severity;
+ }
+} \ No newline at end of file
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/RunLintAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/RunLintAction.java
index 273d8de..24244d0 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/RunLintAction.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/RunLintAction.java
@@ -21,12 +21,15 @@ import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
-import org.eclipse.ui.IActionDelegate;
+import org.eclipse.ui.IObjectActionDelegate;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.IWorkbenchWindowActionDelegate;
/** Action which runs Lint on the current project */
-public class RunLintAction implements IActionDelegate {
+public class RunLintAction implements IWorkbenchWindowActionDelegate, IObjectActionDelegate {
- private ISelection mSelection;
+ private ISelection mSelection;
public void selectionChanged(IAction action, ISelection selection) {
mSelection = selection;
@@ -64,4 +67,14 @@ public class RunLintAction implements IActionDelegate {
return null;
}
+
+ public void setActivePart(IAction action, IWorkbenchPart targetPart) {
+ }
+
+ public void dispose() {
+ // Nothing to dispose
+ }
+
+ public void init(IWorkbenchWindow window) {
+ }
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/UnusedResourceDetector.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/UnusedResourceDetector.java
index c6b8da9..424d39d 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/UnusedResourceDetector.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/UnusedResourceDetector.java
@@ -59,7 +59,7 @@ class UnusedResourceDetector extends com.android.tools.lint.checks.UnusedResourc
try {
javaProject = BaseProjectHelper.getJavaProject(project);
} catch (CoreException e) {
- context.toolContext.log(e, null);
+ context.client.log(e, null);
return;
}
if (javaProject == null) {
@@ -94,7 +94,7 @@ class UnusedResourceDetector extends com.android.tools.lint.checks.UnusedResourc
}
}
} catch (CoreException e) {
- context.toolContext.log(e, null);
+ context.client.log(e, null);
}
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/AdtPrefs.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/AdtPrefs.java
index b35af5c..93326ec 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/AdtPrefs.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/AdtPrefs.java
@@ -64,7 +64,6 @@ public final class AdtPrefs extends AbstractPreferenceInitializer {
public final static String PREFS_LINT_ON_SAVE = AdtPlugin.PLUGIN_ID + ".lintOnSave"; //$NON-NLS-1$
public final static String PREFS_LINT_ON_EXPORT = AdtPlugin.PLUGIN_ID + ".lintOnExport"; //$NON-NLS-1$
public final static String PREFS_ATTRIBUTE_SORT = AdtPlugin.PLUGIN_ID + ".attrSort"; //$NON-NLS-1$
- public final static String PREFS_DISABLED_ISSUES = AdtPlugin.PLUGIN_ID + ".disabedIssues"; //$NON-NLS-1$
public final static String PREFS_LINT_SEVERITIES = AdtPlugin.PLUGIN_ID + ".lintSeverities"; //$NON-NLS-1$
/** singleton instance */
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/LintPreferencePage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/LintPreferencePage.java
index 43ae145..6549e42 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/LintPreferencePage.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/LintPreferencePage.java
@@ -16,75 +16,85 @@
package com.android.ide.eclipse.adt.internal.preferences;
import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.internal.lint.LintEclipseContext;
+import com.android.ide.eclipse.adt.AdtUtils;
+import com.android.ide.eclipse.adt.internal.lint.EclipseLintClient;
import com.android.ide.eclipse.adt.internal.lint.LintRunner;
import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
-import com.android.sdklib.SdkConstants;
-import com.android.tools.lint.api.DetectorRegistry;
+import com.android.tools.lint.client.api.Configuration;
+import com.android.tools.lint.client.api.IssueRegistry;
+import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Issue;
+import com.android.tools.lint.detector.api.Project;
import com.android.tools.lint.detector.api.Severity;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.jface.dialogs.MessageDialog;
-import org.eclipse.jface.preference.IPreferenceStore;
-import org.eclipse.jface.preference.PreferencePage;
-import org.eclipse.jface.viewers.CellEditor;
-import org.eclipse.jface.viewers.CheckboxTableViewer;
-import org.eclipse.jface.viewers.ColumnViewer;
-import org.eclipse.jface.viewers.ComboBoxViewerCellEditor;
-import org.eclipse.jface.viewers.EditingSupport;
-import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.jface.viewers.IColorProvider;
import org.eclipse.jface.viewers.ILabelProviderListener;
-import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.ITableLabelProvider;
-import org.eclipse.jface.viewers.TableViewerColumn;
-import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.TreeNodeContentProvider;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.jface.viewers.TreeViewerColumn;
+import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.ControlAdapter;
import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.ControlListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
-import org.eclipse.swt.widgets.Shell;
-import org.eclipse.swt.widgets.Table;
-import org.eclipse.swt.widgets.TableColumn;
-import org.eclipse.swt.widgets.TableItem;
+import org.eclipse.swt.widgets.Link;
import org.eclipse.swt.widgets.Text;
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.swt.widgets.TreeColumn;
+import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPreferencePage;
import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.dialogs.PreferencesUtil;
+import org.eclipse.ui.dialogs.PropertyPage;
+import java.io.File;
+import java.util.ArrayList;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.List;
import java.util.Map;
-import java.util.Set;
/** Preference page for configuring Lint preferences */
-public class LintPreferencePage extends PreferencePage implements IWorkbenchPreferencePage {
- private static final int CATEGORY_COLUMN_WIDTH = 60;
- private static final int SEVERITY_COLUMN_WIDTH = 80;
- private static final int ID_COLUMN_WIDTH = 80;
-
+public class LintPreferencePage extends PropertyPage implements IWorkbenchPreferencePage,
+ SelectionListener, ControlListener {
+ private static final String ID =
+ "com.android.ide.eclipse.common.preferences.LintPreferencePage"; //$NON-NLS-1$
+ private static final int ID_COLUMN_WIDTH = 150;
+
+ private EclipseLintClient mClient;
+ private IssueRegistry mRegistry;
+ private Configuration mConfiguration;
+ private IProject mProject;
private Map<Issue, Severity> mSeverities = new HashMap<Issue, Severity>();
- private LintEclipseContext mContext;
- private DetectorRegistry mRegistry;
+ private boolean mIgnoreEvent;
- private Table mTable;
+ private Tree mTree;
+ private TreeViewer mTreeViewer;
private Text mDetailsText;
- private Text mSuppressedText;
private Button mCheckFileCheckbox;
private Button mCheckExportCheckbox;
+ private Link mWorkspaceLink;
+ private TreeColumn mNameColumn;
+ private TreeColumn mIdColumn;
+ private Combo mSeverityCombo;
/**
* Create the preference page.
@@ -95,131 +105,109 @@ public class LintPreferencePage extends PreferencePage implements IWorkbenchPref
@Override
public Control createContents(Composite parent) {
+ IAdaptable resource = getElement();
+ if (resource != null) {
+ mProject = (IProject) resource.getAdapter(IProject.class);
+ }
+
Composite container = new Composite(parent, SWT.NULL);
container.setLayout(new GridLayout(2, false));
- mCheckFileCheckbox = new Button(container, SWT.CHECK);
- mCheckFileCheckbox.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1));
- mCheckFileCheckbox.setSelection(true);
- mCheckFileCheckbox.setText("When saving files, check for errors");
-
- mCheckExportCheckbox = new Button(container, SWT.CHECK);
- mCheckExportCheckbox.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1));
- mCheckExportCheckbox.setSelection(true);
- mCheckExportCheckbox.setText("Run full error check when exporting app");
-
- Label label = new Label(container, SWT.SEPARATOR | SWT.HORIZONTAL);
- label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 2, 1));
-
- Label checkListLabel = new Label(container, SWT.NONE);
- checkListLabel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1));
- checkListLabel.setText("Enabled checks:");
-
- CheckboxTableViewer checkboxTableViewer = CheckboxTableViewer.newCheckList(
- container, SWT.BORDER | SWT.FULL_SELECTION);
- mTable = checkboxTableViewer.getTable();
- mTable.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
-
- TableViewerColumn column1 = new TableViewerColumn(checkboxTableViewer, SWT.NONE);
- final TableColumn idColumn = column1.getColumn();
- idColumn.setWidth(100);
- idColumn.setText("Id");
-
- TableViewerColumn column2 = new TableViewerColumn(checkboxTableViewer, SWT.FILL);
- final TableColumn nameColumn = column2.getColumn();
- nameColumn.setWidth(100);
- nameColumn.setText("Name");
-
- TableViewerColumn column3 = new TableViewerColumn(checkboxTableViewer, SWT.NONE);
- final TableColumn categoryColumn = column3.getColumn();
- categoryColumn.setWidth(100);
- categoryColumn.setText("Category");
-
- TableViewerColumn column4 = new TableViewerColumn(checkboxTableViewer, SWT.NONE);
- final TableColumn severityColumn = column4.getColumn();
- severityColumn.setWidth(100);
- severityColumn.setText("Severity");
- column4.setEditingSupport(new SeverityEditingSupport(column4.getViewer()));
-
- checkboxTableViewer.setContentProvider(new ContentProvider());
- checkboxTableViewer.setLabelProvider(new LabelProvider());
-
- mDetailsText = new Text(container, SWT.BORDER | SWT.READ_ONLY | SWT.WRAP |
- SWT.V_SCROLL | SWT.MULTI);
- GridData gdText = new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1);
+ Project project = null;
+ if (mProject != null) {
+ File dir = AdtUtils.getAbsolutePath(mProject).toFile();
+ project = new Project(mClient, dir, dir);
+
+ Label projectLabel = new Label(container, SWT.CHECK);
+ projectLabel.setLayoutData(new GridData(SWT.LEFT, SWT.BOTTOM, false, false, 1,
+ 1));
+ projectLabel.setText("Project-specific configuration:");
+
+ mWorkspaceLink = new Link(container, SWT.NONE);
+ mWorkspaceLink.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
+ mWorkspaceLink.setText("<a>Configure Workspace Settings...</a>");
+ mWorkspaceLink.addSelectionListener(this);
+ } else {
+ mCheckFileCheckbox = new Button(container, SWT.CHECK);
+ mCheckFileCheckbox.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false,
+ 2, 1));
+ mCheckFileCheckbox.setSelection(true);
+ mCheckFileCheckbox.setText("When saving files, check for errors");
+
+ mCheckExportCheckbox = new Button(container, SWT.CHECK);
+ mCheckExportCheckbox.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false,
+ 2, 1));
+ mCheckExportCheckbox.setSelection(true);
+ mCheckExportCheckbox.setText("Run full error check when exporting app");
+
+ Label separator = new Label(container, SWT.SEPARATOR | SWT.HORIZONTAL);
+ separator.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 2, 1));
+
+ Label checkListLabel = new Label(container, SWT.NONE);
+ checkListLabel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1));
+ checkListLabel.setText("Issues:");
+ }
+
+ mRegistry = EclipseLintClient.getRegistry();
+ mClient = new EclipseLintClient(mRegistry, mProject, null, false);
+ mConfiguration = mClient.getConfiguration(project);
+
+ mTreeViewer = new TreeViewer(container, SWT.BORDER | SWT.FULL_SELECTION);
+ mTree = mTreeViewer.getTree();
+ GridData gdTable = new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1);
+ gdTable.widthHint = 500;
+ gdTable.heightHint = 160;
+ mTree.setLayoutData(gdTable);
+ mTree.setLinesVisible(true);
+ mTree.setHeaderVisible(true);
+
+ TreeViewerColumn column1 = new TreeViewerColumn(mTreeViewer, SWT.NONE);
+ mIdColumn = column1.getColumn();
+ mIdColumn.setWidth(100);
+ mIdColumn.setText("Id");
+
+ TreeViewerColumn column2 = new TreeViewerColumn(mTreeViewer, SWT.FILL);
+ mNameColumn = column2.getColumn();
+ mNameColumn.setWidth(100);
+ mNameColumn.setText("Name");
+
+ mTreeViewer.setContentProvider(new ContentProvider());
+ mTreeViewer.setLabelProvider(new LabelProvider());
+
+ mDetailsText = new Text(container, SWT.BORDER | SWT.READ_ONLY | SWT.WRAP |SWT.V_SCROLL
+ | SWT.MULTI);
+ GridData gdText = new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 2);
gdText.heightHint = 80;
mDetailsText.setLayoutData(gdText);
- mRegistry = LintEclipseContext.getRegistry();
- mContext = new LintEclipseContext(mRegistry, null, null);
+ Label severityLabel = new Label(container, SWT.NONE);
+ severityLabel.setText("Severity:");
+
+ mSeverityCombo = new Combo(container, SWT.READ_ONLY);
+ mSeverityCombo.setItems(new String[] {
+ "(Default)", "Error", "Warning", "Information", "Ignore"
+ });
+ GridData gdSeverityCombo = new GridData(SWT.FILL, SWT.TOP, false, false, 1, 1);
+ gdSeverityCombo.widthHint = 90;
+ mSeverityCombo.setLayoutData(gdSeverityCombo);
+ mSeverityCombo.setText("");
+ mSeverityCombo.addSelectionListener(this);
List<Issue> issues = mRegistry.getIssues();
for (Issue issue : issues) {
- mSeverities.put(issue, mContext.getSeverity(issue));
+ Severity severity = mConfiguration.getSeverity(issue);
+ mSeverities.put(issue, severity);
}
- checkboxTableViewer.setInput(issues);
-
- mTable.setLinesVisible(true);
- mTable.setHeaderVisible(true);
-
- Label suppressLabel = new Label(container, SWT.NONE);
- suppressLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
- suppressLabel.setText("Suppressed Warnings:");
- mSuppressedText = new Text(container, SWT.BORDER);
- // Default path relative to the project
- mSuppressedText.setText("${project}/lint-suppressed.xml"); //$NON-NLS-1$
- mSuppressedText.setEnabled(false);
- mSuppressedText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
-
- mTable.addSelectionListener(new SelectionListener() {
- public void widgetDefaultSelected(SelectionEvent e) {
- widgetSelected(e);
- }
-
- public void widgetSelected(SelectionEvent e) {
- TableItem item = (TableItem) e.item;
- Issue issue = (Issue) item.getData();
- String summary = issue.getDescription();
- String explanation = issue.getExplanation();
-
- StringBuilder sb = new StringBuilder(summary.length() + explanation.length() + 20);
- sb.append(summary);
- sb.append('\n').append('\n');
- sb.append(explanation);
- mDetailsText.setText(sb.toString());
- }
- });
+ mTreeViewer.setInput(mRegistry);
+ mTree.addSelectionListener(this);
// Add a listener to resize the column to the full width of the table
- mTable.addControlListener(new ControlAdapter() {
- @Override
- public void controlResized(ControlEvent e) {
- Rectangle r = mTable.getClientArea();
- int availableWidth = r.width;
-
- // On the Mac, the width of the checkbox column is not included (and checkboxes
- // are shown if mAllowSelection=true). Subtract this size from the available
- // width to be distributed among the columns.
- if (SdkConstants.CURRENT_PLATFORM == SdkConstants.PLATFORM_DARWIN) {
- availableWidth -= getCheckboxWidth(e.display.getActiveShell());
- }
+ mTree.addControlListener(this);
- idColumn.setWidth(ID_COLUMN_WIDTH);
- availableWidth -= ID_COLUMN_WIDTH;
+ loadSettings(false);
- severityColumn.setWidth(SEVERITY_COLUMN_WIDTH);
- availableWidth -= SEVERITY_COLUMN_WIDTH;
-
- categoryColumn.setWidth(CATEGORY_COLUMN_WIDTH);
- availableWidth -= CATEGORY_COLUMN_WIDTH;
-
- // Name absorbs everything else
- nameColumn.setWidth(availableWidth);
- }
- });
-
- loadSettings();
+ mTreeViewer.expandAll();
return container;
}
@@ -236,19 +224,20 @@ public class LintPreferencePage extends PreferencePage implements IWorkbenchPref
super.dispose();
}
- /** Cache for {@link #getCheckboxWidth()} */
- private static int sCheckboxWidth = -1;
+ @Override
+ protected void performDefaults() {
+ super.performDefaults();
+
+ mConfiguration.startBulkEditing();
- /** Computes the width of a checkbox */
- private int getCheckboxWidth(Shell shell) {
- if (sCheckboxWidth == -1) {
- Shell tempShell = new Shell(shell, SWT.NO_TRIM);
- Button checkBox = new Button(tempShell, SWT.CHECK);
- sCheckboxWidth = checkBox.computeSize(SWT.DEFAULT, SWT.DEFAULT).x;
- tempShell.dispose();
+ List<Issue> issues = mRegistry.getIssues();
+ for (Issue issue : issues) {
+ mConfiguration.setSeverity(issue, null);
}
- return sCheckboxWidth;
+ mConfiguration.finishBulkEditing();
+
+ loadSettings(true);
}
@Override
@@ -257,62 +246,53 @@ public class LintPreferencePage extends PreferencePage implements IWorkbenchPref
return true;
}
- private void loadSettings() {
- AdtPrefs prefs = AdtPrefs.getPrefs();
- mCheckFileCheckbox.setSelection(prefs.isLintOnSave());
- mCheckExportCheckbox.setSelection(prefs.isLintOnExport());
-
- IPreferenceStore store = getPreferenceStore();
- Set<String> excluded = new HashSet<String>();
- String ids = store.getString(AdtPrefs.PREFS_DISABLED_ISSUES);
- if (ids != null && ids.length() > 0) {
- for (String s : ids.split(",")) { //$NON-NLS-1$
- excluded.add(s);
- }
+ private void loadSettings(boolean refresh) {
+ if (mCheckExportCheckbox != null) {
+ AdtPrefs prefs = AdtPrefs.getPrefs();
+ mCheckFileCheckbox.setSelection(prefs.isLintOnSave());
+ mCheckExportCheckbox.setSelection(prefs.isLintOnExport());
+ }
+
+ mSeverities.clear();
+ List<Issue> issues = mRegistry.getIssues();
+ for (Issue issue : issues) {
+ Severity severity = mConfiguration.getSeverity(issue);
+ mSeverities.put(issue, severity);
}
- TableItem[] itemList = mTable.getItems();
- for (int i = 0; i < itemList.length; i++) {
- Issue issue = (Issue) itemList[i].getData();
- itemList[i].setChecked(!excluded.contains(issue.getId()));
+
+ if (refresh) {
+ mTreeViewer.refresh();
}
}
private void storeSettings() {
// Lint on Save, Lint on Export
- AdtPrefs prefs = AdtPrefs.getPrefs();
- prefs.setLintOnExport(mCheckExportCheckbox.getSelection());
- prefs.setLintOnSave(mCheckFileCheckbox.getSelection());
-
- // Severities
- mContext.setSeverities(mSeverities);
-
- // Excluded checks
- StringBuilder sb = new StringBuilder();
- TableItem[] itemList = mTable.getItems();
- for (int i = 0; i < itemList.length; i++) {
- if (!itemList[i].getChecked()) {
- Issue check = (Issue) itemList[i].getData();
- if (sb.length() > 0) {
- sb.append(',');
- }
- sb.append(check.getId());
- }
- }
- String value = sb.toString();
- if (value.length() == 0) {
- value = null;
+ if (mCheckExportCheckbox != null) {
+ AdtPrefs prefs = AdtPrefs.getPrefs();
+ prefs.setLintOnExport(mCheckExportCheckbox.getSelection());
+ prefs.setLintOnSave(mCheckFileCheckbox.getSelection());
}
- IPreferenceStore store = getPreferenceStore();
- String previous = store.getString(AdtPrefs.PREFS_DISABLED_ISSUES);
- boolean unchanged = (previous != null && previous.equals(value)) || (previous == value);
- if (!unchanged) {
- if (value == null) {
- store.setToDefault(AdtPrefs.PREFS_DISABLED_ISSUES);
- } else {
- store.setValue(AdtPrefs.PREFS_DISABLED_ISSUES, value);
+ mConfiguration.startBulkEditing();
+ boolean changed = false;
+ try {
+ // Severities
+ for (Map.Entry<Issue, Severity> entry : mSeverities.entrySet()) {
+ Issue issue = entry.getKey();
+ Severity severity = entry.getValue();
+ if (mConfiguration.getSeverity(issue) != severity) {
+ if (severity == issue.getDefaultSeverity()) {
+ severity = null;
+ }
+ mConfiguration.setSeverity(issue, severity);
+ changed = true;
+ }
}
+ } finally {
+ mConfiguration.finishBulkEditing();
+ }
+ if (changed) {
// Ask user whether we should re-run the rules.
MessageDialog dialog = new MessageDialog(
null, "Lint Settings Have Changed", null,
@@ -337,20 +317,136 @@ public class LintPreferencePage extends PreferencePage implements IWorkbenchPref
}
}
- private static class ContentProvider implements IStructuredContentProvider {
+ // ---- Implements SelectionListener ----
+
+ public void widgetSelected(SelectionEvent e) {
+ if (mIgnoreEvent) {
+ return;
+ }
+
+ Object source = e.getSource();
+ if (source == mTree) {
+ TreeItem item = (TreeItem) e.item;
+ Object data = item.getData();
+ if (data instanceof Issue) {
+ Issue issue = (Issue) data;
+ String summary = issue.getDescription();
+ String explanation = issue.getExplanation();
+
+ StringBuilder sb = new StringBuilder(summary.length() + explanation.length() + 20);
+ sb.append(summary);
+ sb.append('\n').append('\n');
+ sb.append(explanation);
+ mDetailsText.setText(sb.toString());
+ try {
+ mIgnoreEvent = true;
+ Severity severity = getSeverity(issue);
+ mSeverityCombo.select(severity.ordinal() + 1); // Skip the default option
+ mSeverityCombo.setEnabled(true);
+ } finally {
+ mIgnoreEvent = false;
+ }
+ } else {
+ mDetailsText.setText("");
+ try {
+ mIgnoreEvent = true;
+ mSeverityCombo.setText("");
+ mSeverityCombo.setEnabled(false);
+ } finally {
+ mIgnoreEvent = false;
+ }
+ }
+ } else if (source == mWorkspaceLink) {
+ int result = PreferencesUtil.createPreferenceDialogOn(getShell(), ID,
+ new String[] { ID }, null).open();
+ if (result == Window.OK) {
+ loadSettings(true);
+ }
+ } else if (source == mSeverityCombo) {
+ int index = mSeverityCombo.getSelectionIndex();
+ Issue issue = (Issue) mTree.getSelection()[0].getData();
+ Severity severity;
+ if (index == -1 || index == 0) {
+ // "(Default)"
+ severity = issue.getDefaultSeverity();
+ } else {
+ // -1: Skip the "(Default)"
+ severity = Severity.values()[index - 1];
+ }
+ mSeverities.put(issue, severity);
+ mTreeViewer.refresh();
+ }
+ }
+
+ private Severity getSeverity(Issue issue) {
+ Severity severity = mSeverities.get(issue);
+ if (severity != null) {
+ return severity;
+ }
+
+ return mConfiguration.getSeverity(issue);
+ }
+
+ public void widgetDefaultSelected(SelectionEvent e) {
+ if (e.getSource() == mTree) {
+ widgetSelected(e);
+ }
+ }
+
+ // ---- Implements ControlListener ----
+
+ public void controlMoved(ControlEvent e) {
+ }
+
+ public void controlResized(ControlEvent e) {
+ Rectangle r = mTree.getClientArea();
+ int availableWidth = r.width;
+
+ mIdColumn.setWidth(ID_COLUMN_WIDTH);
+ availableWidth -= ID_COLUMN_WIDTH;
+
+ // Name absorbs everything else
+ mNameColumn.setWidth(availableWidth);
+ }
+
+ private class ContentProvider extends TreeNodeContentProvider {
+ private Map<Category, List<Issue>> mCategoryToIssues;
+
+ @Override
public Object[] getElements(Object inputElement) {
- @SuppressWarnings("unchecked")
- List<Issue> issues = (List<Issue>) inputElement;
- return issues.toArray();
+ return mRegistry.getCategories().toArray();
}
- public void dispose() {
+
+ @Override
+ public boolean hasChildren(Object element) {
+ return element instanceof Category;
}
- public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+
+ @Override
+ public Object[] getChildren(Object parentElement) {
+ if (mCategoryToIssues == null) {
+ mCategoryToIssues = new HashMap<Category, List<Issue>>();
+ List<Issue> issues = mRegistry.getIssues();
+ for (Issue issue : issues) {
+ List<Issue> list = mCategoryToIssues.get(issue.getCategory());
+ if (list == null) {
+ list = new ArrayList<Issue>();
+ mCategoryToIssues.put(issue.getCategory(), list);
+ }
+ list.add(issue);
+ }
+ }
+
+ return mCategoryToIssues.get(parentElement).toArray();
+ }
+
+ @Override
+ public Object getParent(Object element) {
+ return super.getParent(element);
}
}
- private class LabelProvider implements ITableLabelProvider {
- // TODO: add IColorProvider ?
+ private class LabelProvider implements ITableLabelProvider, IColorProvider {
public void addListener(ILabelProviderListener listener) {
}
@@ -366,7 +462,11 @@ public class LintPreferencePage extends PreferencePage implements IWorkbenchPref
}
public Image getColumnImage(Object element, int columnIndex) {
- if (columnIndex == 3) {
+ if (element instanceof Category) {
+ return null;
+ }
+
+ if (columnIndex == 1) {
Issue issue = (Issue) element;
Severity severity = mSeverities.get(issue);
if (severity == null) {
@@ -382,7 +482,6 @@ public class LintPreferencePage extends PreferencePage implements IWorkbenchPref
case INFORMATIONAL:
return sharedImages.getImage(ISharedImages.IMG_OBJS_INFO_TSK);
case IGNORE:
- // TBD: Is this icon okay?
return sharedImages.getImage(ISharedImages.IMG_ELCL_REMOVE_DISABLED);
}
}
@@ -390,104 +489,48 @@ public class LintPreferencePage extends PreferencePage implements IWorkbenchPref
}
public String getColumnText(Object element, int columnIndex) {
+ if (element instanceof Category) {
+ if (columnIndex == 0) {
+ return ((Category) element).getName();
+ } else {
+ return ((Category) element).getExplanation();
+ }
+ }
+
Issue issue = (Issue) element;
switch (columnIndex) {
case 0:
return issue.getId();
case 1:
return issue.getDescription();
- case 2:
- return issue.getCategory();
- case 3: {
- Severity severity = mSeverities.get(issue);
- if (severity == null) {
- return null;
- }
- return severity.getDescription();
- }
}
return null;
}
- }
- /** Editing support for the severity column */
- private class SeverityEditingSupport extends EditingSupport
- implements ILabelProvider, IStructuredContentProvider {
- private final ComboBoxViewerCellEditor mCellEditor;
-
- @SuppressWarnings("deprecation") // Can't use the new form of setContentProvider until 3.7
- private SeverityEditingSupport(ColumnViewer viewer) {
- super(viewer);
- Composite control = (Composite) getViewer().getControl();
- mCellEditor = new ComboBoxViewerCellEditor(control, SWT.READ_ONLY);
- mCellEditor.setLabelProvider(this);
- mCellEditor.setContenProvider(this);
- mCellEditor.setInput(Severity.values());
- }
+ // ---- IColorProvider ----
- @Override
- protected boolean canEdit(Object element) {
- return true;
- }
+ public Color getForeground(Object element) {
+ if (element instanceof Category) {
+ return mTree.getDisplay().getSystemColor(SWT.COLOR_INFO_FOREGROUND);
+ }
- @Override
- protected Object getValue(Object element) {
if (element instanceof Issue) {
Issue issue = (Issue) element;
Severity severity = mSeverities.get(issue);
- return severity;
+ if (severity == Severity.IGNORE) {
+ return mTree.getDisplay().getSystemColor(SWT.COLOR_DARK_GRAY);
+ }
}
return null;
}
- @Override
- protected void setValue(Object element, Object value) {
- if (element instanceof Issue && value instanceof Severity) {
- Issue issue = (Issue) element;
- Severity newValue = (Severity) value;
- mSeverities.put(issue, newValue);
- getViewer().update(element, null);
+ public Color getBackground(Object element) {
+ if (element instanceof Category) {
+ return mTree.getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND);
}
- }
-
- @Override
- protected CellEditor getCellEditor(Object element) {
- return mCellEditor;
- }
-
- // ---- Implements ILabelProvider ----
-
- public String getText(Object element) {
- return ((Severity) element).getDescription();
- }
-
- public void addListener(ILabelProviderListener listener) {
- }
-
- public void dispose() {
- }
-
- public boolean isLabelProperty(Object element, String property) {
- return false;
- }
-
- public void removeListener(ILabelProviderListener listener) {
- }
-
- public Image getImage(Object element) {
return null;
}
-
- // ---- Implements IStructuredContentProvider ----
-
- public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
- }
-
- public Object[] getElements(Object inputElement) {
- return Severity.values();
- }
}
-
} \ No newline at end of file
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/.classpath b/eclipse/plugins/com.android.ide.eclipse.tests/.classpath
index 7bfdfc4..6aeeb1f 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/.classpath
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/.classpath
@@ -15,5 +15,7 @@
<classpathentry kind="lib" path="/adt/libs/sdkuilib.jar" sourcepath="/SdkUiLib"/>
<classpathentry kind="lib" path="/adt/libs/rule_api.jar" sourcepath="/rule_api"/>
<classpathentry kind="lib" path="/adt/libs/common.jar"/>
+ <classpathentry kind="lib" path="/adt/libs/lint_api.jar" sourcepath="/lint-api"/>
+ <classpathentry kind="lib" path="/adt/libs/lint_checks.jar" sourcepath="/lint-checks"/>
<classpathentry kind="output" path="bin"/>
</classpath>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/META-INF/MANIFEST.MF b/eclipse/plugins/com.android.ide.eclipse.tests/META-INF/MANIFEST.MF
index b85af50..905fd83 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/META-INF/MANIFEST.MF
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/META-INF/MANIFEST.MF
@@ -10,4 +10,6 @@ Bundle-RequiredExecutionEnvironment: J2SE-1.5
Bundle-ClassPath: kxml2-2.3.0.jar,
.,
layoutlib.jar,
+ lint_api.jar,
+ lint_checks.jar,
easymock.jar
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/build.properties b/eclipse/plugins/com.android.ide.eclipse.tests/build.properties
index eece9f2..a79421c 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/build.properties
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/build.properties
@@ -8,6 +8,8 @@ bin.includes = META-INF/,\
unittest.xml,\
kxml2-2.3.0.jar,\
layoutlib.jar,\
+ lint_api.jar,
+ lint_checks.jar,
unittests/com/android/sdklib/testdata/,\
unittests/com/android/layoutlib/testdata/,\
unittests/com/android/ide/eclipse/testdata/,\
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/lint/ProjectLintConfigurationTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/lint/ProjectLintConfigurationTest.java
new file mode 100644
index 0000000..52ab9d7
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/lint/ProjectLintConfigurationTest.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.ide.eclipse.adt.internal.lint;
+
+import com.android.ide.eclipse.adt.internal.editors.layout.refactoring.AdtProjectTest;
+import com.android.tools.lint.checks.DuplicateIdDetector;
+import com.android.tools.lint.checks.UnusedResourceDetector;
+import com.android.tools.lint.client.api.Configuration;
+import com.android.tools.lint.client.api.IDomParser;
+import com.android.tools.lint.client.api.LintClient;
+import com.android.tools.lint.detector.api.Context;
+import com.android.tools.lint.detector.api.Issue;
+import com.android.tools.lint.detector.api.Location;
+import com.android.tools.lint.detector.api.Project;
+import com.android.tools.lint.detector.api.Severity;
+
+import org.eclipse.core.resources.IProject;
+
+import java.io.File;
+import java.util.Calendar;
+
+@SuppressWarnings("javadoc")
+public class ProjectLintConfigurationTest extends AdtProjectTest {
+ public void testBasic() {
+ Configuration parent = null;
+ LintClient client = new TestClient();
+
+ File dir = getTargetDir();
+ if (!dir.exists()) {
+ boolean ok = dir.mkdirs();
+ assertTrue(dir.getPath(), ok);
+ }
+ Project project = new Project(client, dir, dir);
+
+ ProjectLintConfiguration config =
+ new ProjectLintConfiguration(client, project, parent, false /*fatalOnly*/);
+
+ Issue usuallyEnabledIssue = DuplicateIdDetector.WITHIN_LAYOUT;
+ Issue usuallyDisabledIssue = UnusedResourceDetector.ISSUE_IDS;
+
+ assertTrue(config.isEnabled(usuallyEnabledIssue));
+ assertFalse(config.isEnabled(usuallyDisabledIssue));
+
+ config.setSeverity(usuallyEnabledIssue, Severity.IGNORE);
+ config.setSeverity(usuallyDisabledIssue, Severity.ERROR);
+ assertFalse(config.isEnabled(usuallyEnabledIssue));
+ assertTrue(config.isEnabled(usuallyDisabledIssue));
+
+ // Make a NEW config object to ensure the state is persisted properly, not just
+ // kept on the config object!
+ config = new ProjectLintConfiguration(client, project, parent, false /*fatalOnly*/);
+ assertFalse(config.isEnabled(usuallyEnabledIssue));
+ assertTrue(config.isEnabled(usuallyDisabledIssue));
+ }
+
+ public void testInheritance() {
+ Configuration parent = null;
+ LintClient client = new TestClient();
+
+ File dir = getTargetDir();
+ assertTrue(dir.mkdirs());
+ Project project = new Project(client, dir, dir);
+
+ File otherDir = new File(dir, "otherConfig");
+ assertTrue(otherDir.mkdir());
+ Project otherProject = new Project(client, otherDir, otherDir);
+
+ ProjectLintConfiguration otherConfig =
+ new ProjectLintConfiguration(client, otherProject, parent, false);
+
+ ProjectLintConfiguration config =
+ new ProjectLintConfiguration(client, project, otherConfig, false);
+
+ Issue usuallyEnabledIssue = DuplicateIdDetector.WITHIN_LAYOUT;
+ Issue usuallyDisabledIssue = UnusedResourceDetector.ISSUE_IDS;
+
+ assertTrue(config.isEnabled(usuallyEnabledIssue));
+ assertFalse(config.isEnabled(usuallyDisabledIssue));
+
+ otherConfig.setSeverity(usuallyEnabledIssue, Severity.IGNORE);
+ otherConfig.setSeverity(usuallyDisabledIssue, Severity.ERROR);
+
+ // Ensure inheritance works
+ assertFalse(config.isEnabled(usuallyEnabledIssue));
+ assertTrue(config.isEnabled(usuallyDisabledIssue));
+
+ // Revert
+ otherConfig.setSeverity(usuallyEnabledIssue, Severity.ERROR);
+ otherConfig.setSeverity(usuallyDisabledIssue, Severity.IGNORE);
+ assertTrue(config.isEnabled(usuallyEnabledIssue));
+ assertFalse(config.isEnabled(usuallyDisabledIssue));
+
+ // Now override in child
+ config.setSeverity(usuallyEnabledIssue, Severity.ERROR);
+ config.setSeverity(usuallyDisabledIssue, Severity.IGNORE);
+ assertTrue(config.isEnabled(usuallyEnabledIssue));
+ assertFalse(config.isEnabled(usuallyDisabledIssue));
+
+ // Now change in parent: no change in child
+ otherConfig.setSeverity(usuallyEnabledIssue, Severity.IGNORE);
+ otherConfig.setSeverity(usuallyDisabledIssue, Severity.ERROR);
+ assertTrue(config.isEnabled(usuallyEnabledIssue));
+ assertFalse(config.isEnabled(usuallyDisabledIssue));
+ assertFalse(otherConfig.isEnabled(usuallyEnabledIssue));
+ assertTrue(otherConfig.isEnabled(usuallyDisabledIssue));
+
+ // Clear override in child
+ config.setSeverity(usuallyEnabledIssue, null);
+ config.setSeverity(usuallyDisabledIssue, null);
+ assertFalse(config.isEnabled(usuallyEnabledIssue));
+ assertTrue(config.isEnabled(usuallyDisabledIssue));
+ }
+
+ public void testBulkEditing() {
+ Configuration parent = null;
+ LintClient client = new TestClient();
+
+ File dir = getTargetDir();
+ assertTrue(dir.mkdirs());
+ Project project = new Project(client, dir, dir);
+
+ ProjectLintConfiguration config =
+ new ProjectLintConfiguration(client, project, parent, false /*fatalOnly*/);
+
+ Issue usuallyEnabledIssue = DuplicateIdDetector.WITHIN_LAYOUT;
+ Issue usuallyDisabledIssue = UnusedResourceDetector.ISSUE_IDS;
+
+ assertTrue(config.isEnabled(usuallyEnabledIssue));
+ assertFalse(config.isEnabled(usuallyDisabledIssue));
+
+ config.setSeverity(usuallyEnabledIssue, Severity.IGNORE);
+ assertFalse(config.isEnabled(usuallyEnabledIssue));
+ assertFalse(config.isEnabled(usuallyDisabledIssue));
+
+ File configFile = new File(dir, "lint.xml");
+ assertTrue(configFile.getPath(), configFile.exists());
+ long lastModified = configFile.lastModified();
+
+ // We need to make sure that the timestamp of the file is a couple of seconds
+ // after the last update or we can't tell whether the file was updated or not
+
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ System.err.println("Sleep interrupted, test may not work.");
+ }
+ config.startBulkEditing();
+ assertFalse(lastModified < configFile.lastModified());
+ assertEquals(lastModified, configFile.lastModified());
+ config.setSeverity(usuallyDisabledIssue, Severity.ERROR);
+ config.finishBulkEditing();
+ assertTrue(lastModified < configFile.lastModified());
+
+ assertTrue(config.isEnabled(usuallyDisabledIssue));
+ }
+
+ public void testPersistence() {
+ // Ensure that we use the same configuration object repeatedly for a
+ // single project, such that we don't recompute and parse XML for each and
+ // every lint run!
+ IProject project = getProject();
+ TestClient client = new TestClient();
+ ProjectLintConfiguration config1 = ProjectLintConfiguration.get(client, project, false);
+ ProjectLintConfiguration config2 = ProjectLintConfiguration.get(client, project, false);
+ assertSame(config1, config2);
+ }
+
+ private static File sTempDir = null;
+ @Override
+ protected File getTempDir() {
+ if (sTempDir == null) {
+ File base = new File(System.getProperty("java.io.tmpdir")); //$NON-NLS-1$
+ String os = System.getProperty("os.name"); //$NON-NLS-1$
+ if (os.startsWith("Mac OS")) { //$NON-NLS-1$
+ base = new File("/tmp");
+ }
+ Calendar c = Calendar.getInstance();
+ String name = String.format("lintTests/%1$tF_%1$tT", c).replace(':', '-'); //$NON-NLS-1$
+ File tmpDir = new File(base, name);
+ if (!tmpDir.exists() && tmpDir.mkdirs()) {
+ sTempDir = tmpDir;
+ } else {
+ sTempDir = base;
+ }
+ }
+
+ return sTempDir;
+ }
+
+ @Override
+ protected File getTargetDir() {
+ return new File(getTempDir(), getClass().getSimpleName() + "_" + getName());
+ }
+
+ private static class TestClient extends LintClient {
+ @Override
+ public void report(Context context, Issue issue, Location location, String message,
+ Object data) {
+ }
+
+ @Override
+ public void log(Throwable exception, String format, Object... args) {
+ }
+
+ @Override
+ public IDomParser getParser() {
+ return null;
+ }
+
+ @Override
+ public String readFile(File file) {
+ return null;
+ }
+ }
+}
diff --git a/ide_common/src/com/android/ide/common/resources/configuration/VersionQualifier.java b/ide_common/src/com/android/ide/common/resources/configuration/VersionQualifier.java
index c7cef97..2adf9a4 100644
--- a/ide_common/src/com/android/ide/common/resources/configuration/VersionQualifier.java
+++ b/ide_common/src/com/android/ide/common/resources/configuration/VersionQualifier.java
@@ -26,7 +26,7 @@ public final class VersionQualifier extends ResourceQualifier {
/** Default pixel density value. This means the property is not set. */
private final static int DEFAULT_VERSION = -1;
- private final static Pattern sCountryCodePattern = Pattern.compile("^v(\\d+)$");//$NON-NLS-1$
+ private final static Pattern sVersionPattern = Pattern.compile("^v(\\d+)$");//$NON-NLS-1$
private int mVersion = DEFAULT_VERSION;
@@ -39,7 +39,7 @@ public final class VersionQualifier extends ResourceQualifier {
* @return a new {@link VersionQualifier} object or <code>null</code>
*/
public static VersionQualifier getQualifier(String segment) {
- Matcher m = sCountryCodePattern.matcher(segment);
+ Matcher m = sVersionPattern.matcher(segment);
if (m.matches()) {
String v = m.group(1);
diff --git a/lint/cli/src/com/android/tools/lint/HtmlReporter.java b/lint/cli/src/com/android/tools/lint/HtmlReporter.java
index 0d8335e..c4b8602 100644
--- a/lint/cli/src/com/android/tools/lint/HtmlReporter.java
+++ b/lint/cli/src/com/android/tools/lint/HtmlReporter.java
@@ -16,6 +16,7 @@
package com.android.tools.lint;
+import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.Location;
import com.android.tools.lint.detector.api.Severity;
@@ -66,6 +67,11 @@ class HtmlReporter extends Reporter {
" font-weight: bold;\n" + //$NON-NLS-1$
" margin: 5px 0px 5px 0px;\n" + //$NON-NLS-1$
"}\n" + //$NON-NLS-1$
+ ".category {\n" + //$NON-NLS-1$
+ " font-size: 18pt;\n" + //$NON-NLS-1$
+ " font-weight: bold;\n" + //$NON-NLS-1$
+ " margin: 10px 0px 5px 0px;\n" + //$NON-NLS-1$
+ "}\n" + //$NON-NLS-1$
// The issue summary line
//".summary {\n" + //$NON-NLS-1$
//" font-weight: bold;\n" + //$NON-NLS-1$
@@ -93,6 +99,9 @@ class HtmlReporter extends Reporter {
" max-width: 200px;\n" + //$NON-NLS-1$
" max-height: 200px;\n" + //$NON-NLS-1$
"}\n" + //$NON-NLS-1$
+ // Image labels
+ "th { font-weight: normal; }\n" + //$NON-NLS-1$
+ "table { border: none; }\n" + //$NON-NLS-1$
// The Priority/Category section
".metadata { }\n" + //$NON-NLS-1$
// Each error message
@@ -132,28 +141,59 @@ class HtmlReporter extends Reporter {
mWriter.write(String.format("Check performed at %1$s.",
new Date().toString()));
- mWriter.write("<br/>"); //$NON-NLS-1$
+ mWriter.write("<br/><br/>"); //$NON-NLS-1$
mWriter.write(String.format("%1$d errors and %2$d warnings found:",
errorCount, warningCount));
mWriter.write("<br/>"); //$NON-NLS-1$
// Write issue id summary
mWriter.write("<ul>\n"); //$NON-NLS-1$
+ Category previousCategory = null;
for (List<Warning> warnings : related) {
- mWriter.write("<li> <a href=\"#" //$NON-NLS-1$
- + warnings.get(0).issue.getId()
- +"\">"); //$NON-NLS-1$
+ Issue issue = warnings.get(0).issue;
+
+ if (issue.getCategory() != previousCategory) {
+ if (previousCategory != null) {
+ mWriter.write("</ul>\n"); //$NON-NLS-1$
+ }
+ previousCategory = issue.getCategory();
+ String categoryName = issue.getCategory().getFullName();
+ mWriter.write("<li> <a href=\"#"); //$NON-NLS-1$
+ mWriter.write(categoryName);
+ mWriter.write("\">"); //$NON-NLS-1$
+ mWriter.write(categoryName);
+ mWriter.write("</a>\n"); //$NON-NLS-1$
+ mWriter.write("\n<ul>\n"); //$NON-NLS-1$
+ }
+
+ mWriter.write("<li> <a href=\"#"); //$NON-NLS-1$
+ mWriter.write(issue.getId());
+ mWriter.write("\">"); //$NON-NLS-1$
mWriter.write(String.format("%1$3d %2$s", //$NON-NLS-1$
- warnings.size(), warnings.get(0).issue.getId()));
+ warnings.size(), issue.getId()));
mWriter.write("</a>\n"); //$NON-NLS-1$
}
+ if (previousCategory != null) {
+ mWriter.write("</ul>\n"); //$NON-NLS-1$
+ }
mWriter.write("</ul>\n"); //$NON-NLS-1$
mWriter.write("<br/>"); //$NON-NLS-1$
+ previousCategory = null;
for (List<Warning> warnings : related) {
Warning first = warnings.get(0);
Issue issue = first.issue;
+ if (issue.getCategory() != previousCategory) {
+ previousCategory = issue.getCategory();
+ mWriter.write("\n<a name=\""); //$NON-NLS-1$
+ mWriter.write(issue.getCategory().getFullName());
+ mWriter.write("\">\n"); //$NON-NLS-1$
+ mWriter.write("<div class=\"category\">"); //$NON-NLS-1$
+ mWriter.write(issue.getCategory().getFullName());
+ mWriter.write("</div>\n"); //$NON-NLS-1$
+ }
+
mWriter.write("<a name=\"" + issue.getId() + "\">\n"); //$NON-NLS-1$ //$NON-NLS-2$
mWriter.write("<div class=\"issue\">\n"); //$NON-NLS-1$
@@ -225,7 +265,7 @@ class HtmlReporter extends Reporter {
mWriter.write(issue.getPriority());
mWriter.write("<br/>\n"); //$NON-NLS-1$
mWriter.write("Category: ");
- mWriter.write(issue.getCategory());
+ mWriter.write(issue.getCategory().getFullName());
mWriter.write("</div>\n"); //$NON-NLS-1$
mWriter.write("Severity: ");
@@ -300,7 +340,22 @@ class HtmlReporter extends Reporter {
return getDpiRank(s1) - getDpiRank(s2);
}
});
- mWriter.write("<table normal\" border=\"0\"><tr>"); //$NON-NLS-1$
+ mWriter.write("<table>"); //$NON-NLS-1$
+ mWriter.write("<tr>"); //$NON-NLS-1$
+ for (String linkedUrl : urls) {
+ // Image series: align top
+ mWriter.write("<td>"); //$NON-NLS-1$
+ mWriter.write("<a href=\""); //$NON-NLS-1$
+ mWriter.write(linkedUrl);
+ mWriter.write("\">"); //$NON-NLS-1$
+ mWriter.write("<img border=\"0\" align=\"top\" src=\""); //$NON-NLS-1$
+ mWriter.write(linkedUrl);
+ mWriter.write("\" /></a>\n"); //$NON-NLS-1$
+ mWriter.write("</td>"); //$NON-NLS-1$
+ }
+ mWriter.write("</tr>"); //$NON-NLS-1$
+
+ mWriter.write("<tr>"); //$NON-NLS-1$
for (String linkedUrl : urls) {
mWriter.write("<th>"); //$NON-NLS-1$
int index = linkedUrl.lastIndexOf("drawable-"); //$NON-NLS-1$
@@ -313,19 +368,9 @@ class HtmlReporter extends Reporter {
}
mWriter.write("</th>"); //$NON-NLS-1$
}
- mWriter.write("</tr>\n<tr>"); //$NON-NLS-1$
- for (String linkedUrl : urls) {
- // Image series: align top
- mWriter.write("<td>"); //$NON-NLS-1$
- mWriter.write("<a href=\""); //$NON-NLS-1$
- mWriter.write(linkedUrl);
- mWriter.write("\">"); //$NON-NLS-1$
- mWriter.write("<img border=\"0\" align=\"top\" src=\""); //$NON-NLS-1$
- mWriter.write(linkedUrl);
- mWriter.write("\" /></a>\n"); //$NON-NLS-1$
- mWriter.write("</td>"); //$NON-NLS-1$
- }
- mWriter.write("</tr></table>"); //$NON-NLS-1$
+ mWriter.write("</tr>\n"); //$NON-NLS-1$
+
+ mWriter.write("</table>\n"); //$NON-NLS-1$
}
} else {
// Just this image: float to the right
diff --git a/lint/cli/src/com/android/tools/lint/Main.java b/lint/cli/src/com/android/tools/lint/Main.java
index 11d2af1..0fe1fcb 100644
--- a/lint/cli/src/com/android/tools/lint/Main.java
+++ b/lint/cli/src/com/android/tools/lint/Main.java
@@ -16,15 +16,20 @@
package com.android.tools.lint;
-import com.android.tools.lint.api.DetectorRegistry;
-import com.android.tools.lint.api.IDomParser;
-import com.android.tools.lint.api.Lint;
-import com.android.tools.lint.api.ToolContext;
-import com.android.tools.lint.checks.BuiltinDetectorRegistry;
+import com.android.tools.lint.checks.BuiltinIssueRegistry;
+import com.android.tools.lint.client.api.Configuration;
+import com.android.tools.lint.client.api.DefaultConfiguration;
+import com.android.tools.lint.client.api.IDomParser;
+import com.android.tools.lint.client.api.IssueRegistry;
+import com.android.tools.lint.client.api.Lint;
+import com.android.tools.lint.client.api.LintClient;
+import com.android.tools.lint.client.api.LintListener;
+import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.Location;
import com.android.tools.lint.detector.api.Position;
+import com.android.tools.lint.detector.api.Project;
import com.android.tools.lint.detector.api.Severity;
import java.io.BufferedReader;
@@ -51,20 +56,21 @@ import java.util.Set;
* <li>Offer suppressing violations
* </ul>
*/
-public class Main extends ToolContext {
+public class Main extends LintClient {
private static final int MAX_LINE_WIDTH = 70;
private static final String ARG_ENABLE = "--enable"; //$NON-NLS-1$
private static final String ARG_DISABLE = "--disable"; //$NON-NLS-1$
private static final String ARG_CHECK = "--check"; //$NON-NLS-1$
- private static final String ARG_SUPPRESS = "--suppress"; //$NON-NLS-1$
private static final String ARG_IGNORE = "--ignore"; //$NON-NLS-1$
private static final String ARG_LISTIDS = "--list"; //$NON-NLS-1$
private static final String ARG_SHOW = "--show"; //$NON-NLS-1$
+ private static final String ARG_QUIET = "--quiet"; //$NON-NLS-1$
private static final String ARG_FULLPATH = "--fullpath"; //$NON-NLS-1$
private static final String ARG_HELP = "--help"; //$NON-NLS-1$
private static final String ARG_NOLINES = "--nolines"; //$NON-NLS-1$
private static final String ARG_HTML = "--html"; //$NON-NLS-1$
- private static final String ARG_URL = "--url"; //$NON-NLS-1$
+ private static final String ARG_XML = "--xml"; //$NON-NLS-1$
+ private static final String ARG_URL = "--url"; //$NON-NLS-1$
private static final int ERRNO_ERRORS = -1;
private static final int ERRNO_USAGE = -2;
private static final int ERRNO_EXISTS = -3;
@@ -82,6 +88,7 @@ public class Main extends ToolContext {
private int mWarningCount;
private boolean mShowLines = true;
private Reporter mReporter;
+ private boolean mQuiet;
/** Creates a CLI driver */
public Main() {
@@ -107,7 +114,7 @@ public class Main extends ToolContext {
System.exit(ERRNO_USAGE);
}
- DetectorRegistry registry = new BuiltinDetectorRegistry();
+ IssueRegistry registry = new BuiltinIssueRegistry();
// Mapping from file path prefix to URL. Applies only to HTML reports
String urlMap = null;
@@ -141,6 +148,8 @@ public class Main extends ToolContext {
} else if (arg.equals(ARG_FULLPATH)
|| arg.equals(ARG_FULLPATH + "s")) { // allow "--fullpaths" too
mFullPath = true;
+ } else if (arg.equals(ARG_QUIET) || arg.equals("-q")) {
+ mQuiet = true;
} else if (arg.equals(ARG_NOLINES)) {
mShowLines = false;
} else if (arg.equals(ARG_URL)) {
@@ -178,21 +187,44 @@ public class Main extends ToolContext {
log(e, null);
System.exit(ERRNO_INVALIDARGS);
}
- } else if (arg.equals(ARG_SUPPRESS) || arg.equals(ARG_DISABLE)
- || arg.equals(ARG_IGNORE)) {
+ } else if (arg.equals(ARG_XML)) {
+ if (index == args.length - 1) {
+ System.err.println("Missing XML output file name");
+ System.exit(ERRNO_INVALIDARGS);
+ }
+ File output = new File(args[++index]);
+ if (output.exists()) {
+ boolean delete = output.delete();
+ if (!delete) {
+ System.err.println("Could not delete old " + output);
+ System.exit(ERRNO_EXISTS);
+ }
+ }
+ if (output.canWrite()) {
+ System.err.println("Cannot write XML output file " + output);
+ System.exit(ERRNO_EXISTS);
+ }
+ try {
+ mReporter = new XmlReporter(output);
+ } catch (IOException e) {
+ log(e, null);
+ System.exit(ERRNO_INVALIDARGS);
+ }
+ } else if (arg.equals(ARG_DISABLE) || arg.equals(ARG_IGNORE)) {
if (index == args.length - 1) {
System.err.println("Missing categories or id's to disable");
System.exit(ERRNO_INVALIDARGS);
}
String[] ids = args[++index].split(",");
for (String id : ids) {
- if (registry.isCategory(id)) {
+ if (registry.isCategoryName(id)) {
// Suppress all issues with the given category
String category = id;
for (Issue issue : registry.getIssues()) {
// Check prefix such that filtering on the "Usability" category
// will match issue category "Usability:Icons" etc.
- if (category.startsWith(issue.getCategory())) {
+ if (issue.getCategory().getName().startsWith(category) ||
+ issue.getCategory().getFullName().startsWith(category)) {
mSuppress.add(issue.getId());
}
}
@@ -211,11 +243,12 @@ public class Main extends ToolContext {
}
String[] ids = args[++index].split(",");
for (String id : ids) {
- if (registry.isCategory(id)) {
+ if (registry.isCategoryName(id)) {
// Enable all issues with the given category
String category = id;
for (Issue issue : registry.getIssues()) {
- if (category.startsWith(issue.getCategory())) {
+ if (issue.getCategory().getName().startsWith(category) ||
+ issue.getCategory().getFullName().startsWith(category)) {
mEnabled.add(issue.getId());
}
}
@@ -235,13 +268,14 @@ public class Main extends ToolContext {
mCheck = new HashSet<String>();
String[] ids = args[++index].split(",");
for (String id : ids) {
- if (registry.isCategory(id)) {
+ if (registry.isCategoryName(id)) {
// Suppress all issues with the given category
String category = id;
for (Issue issue : registry.getIssues()) {
// Check prefix such that filtering on the "Usability" category
// will match issue category "Usability:Icons" etc.
- if (category.startsWith(issue.getCategory())) {
+ if (issue.getCategory().getName().startsWith(category) ||
+ issue.getCategory().getFullName().startsWith(category)) {
mCheck.add(issue.getId());
}
}
@@ -302,8 +336,13 @@ public class Main extends ToolContext {
((HtmlReporter) mReporter).setUrlMap(map);
}
- Lint analyzer = new Lint(new BuiltinDetectorRegistry(), this, null);
- analyzer.analyze(files);
+ Lint analyzer = new Lint(registry, this);
+
+ if (!mQuiet) {
+ analyzer.addLintListener(new ProgressPrinter());
+ }
+
+ analyzer.analyze(files, null /* scope */);
Collections.sort(mWarnings);
@@ -317,11 +356,11 @@ public class Main extends ToolContext {
System.exit(mFatal ? ERRNO_ERRORS : 0);
}
- private void displayValidIds(DetectorRegistry registry, PrintStream out) {
- List<String> categories = registry.getCategories();
+ private void displayValidIds(IssueRegistry registry, PrintStream out) {
+ List<Category> categories = registry.getCategories();
out.println("Valid issue categories:");
- for (String category : categories) {
- out.println(" " + category);
+ for (Category category : categories) {
+ out.println(" " + category.getFullName());
}
out.println();
List<Issue> issues = registry.getIssues();
@@ -331,7 +370,7 @@ public class Main extends ToolContext {
}
}
- private void showIssues(DetectorRegistry registry) {
+ private void showIssues(IssueRegistry registry) {
List<Issue> issues = registry.getIssues();
List<Issue> sorted = new ArrayList<Issue>(issues);
Collections.sort(sorted, new Comparator<Issue>() {
@@ -350,12 +389,13 @@ public class Main extends ToolContext {
});
System.out.println("Available issues:\n");
- String previousCategory = null;
+ Category previousCategory = null;
for (Issue issue : sorted) {
- String category = issue.getCategory();
+ Category category = issue.getCategory();
if (!category.equals(previousCategory)) {
- System.out.println(category);
- for (int i = 0, n = category.length(); i < n; i++) {
+ String name = category.getFullName();
+ System.out.println(name);
+ for (int i = 0, n = name.length(); i < n; i++) {
System.out.print('=');
}
System.out.println('\n');
@@ -393,11 +433,17 @@ public class Main extends ToolContext {
}
}
+ static String wrapArg(String explanation) {
+ // Wrap arguments such that the wrapped lines are not showing up in the left column
+ return wrap(explanation, MAX_LINE_WIDTH, " ");
+ }
+
+
static String wrap(String explanation) {
- return wrap(explanation, MAX_LINE_WIDTH);
+ return wrap(explanation, MAX_LINE_WIDTH, "");
}
- static String wrap(String explanation, int max) {
+ static String wrap(String explanation, int lineWidth, String hangingIndent) {
int explanationLength = explanation.length();
StringBuilder sb = new StringBuilder(explanationLength * 2);
int index = 0;
@@ -406,12 +452,12 @@ public class Main extends ToolContext {
int lineEnd = explanation.indexOf('\n', index);
int next;
- if (lineEnd != -1 && (lineEnd - index) < max) {
+ if (lineEnd != -1 && (lineEnd - index) < lineWidth) {
next = lineEnd + 1;
} else {
// Line is longer than available width; grab as much as we can
- lineEnd = Math.min(index + max, explanationLength);
- if (lineEnd - index < max) {
+ lineEnd = Math.min(index + lineWidth, explanationLength);
+ if (lineEnd - index < lineWidth) {
next = explanationLength;
} else {
// then back up to the last space
@@ -427,6 +473,12 @@ public class Main extends ToolContext {
}
}
+ if (sb.length() > 0) {
+ sb.append(hangingIndent);
+ } else {
+ lineWidth -= hangingIndent.length();
+ }
+
sb.append(explanation.substring(index, lineEnd));
sb.append('\n');
index = next;
@@ -441,19 +493,31 @@ public class Main extends ToolContext {
out.println("Usage: " + command + " [flags] <project directories>\n");
out.println("Flags:");
- out.println(ARG_SUPPRESS + " <list>: Suppress a list of categories or specific issue id's");
- out.println(ARG_CHECK + " <list>: Only check the specific list of issues (categories or id's)");
- out.println(ARG_DISABLE + " <list>: Disable the list of categories or specific issue id's");
- out.println(ARG_ENABLE + " <list>: Enable the specific list of issues (plus default enabled)");
- out.println(ARG_FULLPATH + " : Use full paths in the error output");
- out.println(ARG_NOLINES + " : Do not include the source file lines with errors in the output");
- out.println(ARG_HTML + " <filename>: Create an HTML report instead");
- out.println(ARG_URL + " filepath=url: Add links to HTML report, replacing local path prefixes with url prefix");
+ out.print(wrapArg(ARG_HELP + ": This message."));
+ out.print(wrapArg(ARG_DISABLE + " <list>: Disable the list of categories or " +
+ "specific issue id's. The list should be a comma-separated list of issue " +
+ "id's or categories."));
+ out.print(wrapArg(ARG_ENABLE + " <list>: Enable the specific list of issues. " +
+ "This checks all the default issues plus the specifically enabled issues. The " +
+ "list should be a comma-separated list of issue id's or categories."));
+ out.print(wrapArg(ARG_CHECK + " <list>: Only check the specific list of issues. " +
+ "This will disable everything and re-enable the given list of issues. " +
+ "The list should be a comma-separated list of issue id's or categories."));
+ out.print(wrapArg(ARG_FULLPATH + " : Use full paths in the error output."));
+ out.print(wrapArg(ARG_NOLINES + " : Do not include the source file lines with errors " +
+ "in the output. By default, the error output includes snippets of source code " +
+ "on the line containing the error, but this flag turns it off."));
+ out.print(wrapArg(ARG_HTML + " <filename>: Create an HTML report instead."));
+ out.print(wrapArg(ARG_URL + " filepath=url: Add links to HTML report, replacing local " +
+ "path prefixes with url prefix. The mapping can be a comma-separated list of " +
+ "path prefixes to corresponding URL prefixes, such as " +
+ "C:\\temp\\Proj1=http://buildserver/sources/temp/Proj1"));
+ out.print(wrapArg(ARG_XML + " <filename>: Create an XML report instead."));
out.println();
- out.println(ARG_LISTIDS + ": List the available issue id's and exit.");
- out.println(ARG_SHOW + ": List available issues along with full explanations");
- out.println(ARG_SHOW + " <ids>: Show full explanations for the given list of issue id's");
- out.println("Id lists should be comma separated with no spaces. ");
+ out.print(wrapArg(ARG_LISTIDS + ": List the available issue id's and exit."));
+ out.print(wrapArg(ARG_SHOW + ": List available issues along with full explanations."));
+ out.print(wrapArg(ARG_SHOW + " <ids>: Show full explanations for the given list of issue id's."));
+ out.print(wrapArg(ARG_QUIET + ": Don't show progress."));
}
@Override
@@ -472,31 +536,16 @@ public class Main extends ToolContext {
}
@Override
- public boolean isEnabled(Issue issue) {
- String id = issue.getId();
- if (mSuppress.contains(id)) {
- return false;
- }
-
- if (mEnabled.contains(id)) {
- return true;
- }
-
- if (mCheck != null) {
- return mCheck.contains(id);
- }
-
- return issue.isEnabledByDefault();
+ public Configuration getConfiguration(Project project) {
+ return new CliConfiguration(null, project);
}
@Override
public void report(Context context, Issue issue, Location location, String message,
Object data) {
- if (!isEnabled(issue)) {
- return;
- }
+ assert context.configuration.isEnabled(issue);
- Severity severity = getSeverity(issue);
+ Severity severity = context.configuration.getSeverity(issue);
if (severity == Severity.IGNORE) {
return;
}
@@ -533,7 +582,7 @@ public class Main extends ToolContext {
warning.line = line;
warning.offset = startPosition.getOffset();
if (line >= 0) {
- warning.fileContents = context.toolContext.readFile(location.getFile());
+ warning.fileContents = context.client.readFile(location.getFile());
if (mShowLines) {
// Compute error line contents
@@ -595,18 +644,6 @@ public class Main extends ToolContext {
}
@Override
- public boolean isSuppressed(Context context, Issue issue, Location range, String message,
- Severity severity, Object data) {
- // Not yet supported
- return false;
- }
-
- @Override
- public Severity getSeverity(Issue issue) {
- return issue.getDefaultSeverity();
- }
-
- @Override
public String readFile(File file) {
BufferedReader reader = null;
try {
@@ -634,4 +671,61 @@ public class Main extends ToolContext {
return ""; //$NON-NLS-1$
}
+
+ /**
+ * Consult the lint.xml file, but override with the --enable and --disable
+ * flags supplied on the command line
+ */
+ private class CliConfiguration extends DefaultConfiguration {
+ CliConfiguration(Configuration parent, Project project) {
+ super(Main.this, project, parent);
+ }
+
+ @Override
+ public Severity getSeverity(Issue issue) {
+ Severity severity = super.getSeverity(issue);
+
+ String id = issue.getId();
+ if (mSuppress.contains(id)) {
+ return Severity.IGNORE;
+ }
+
+ if (mEnabled.contains(id) || (mCheck != null && mCheck.contains(id))) {
+ // Overriding default
+ // Detectors shouldn't be returning ignore as a default severity,
+ // but in case they do, force it up to warning here to ensure that
+ // it's run
+ if (severity == Severity.IGNORE) {
+ return Severity.WARNING;
+ } else {
+ return severity;
+ }
+ }
+
+ if (mCheck != null) {
+ return Severity.IGNORE;
+ }
+
+ return severity;
+ }
+ }
+
+ private class ProgressPrinter implements LintListener {
+ public void update(EventType type, Context context) {
+ switch (type) {
+ case SCANNING_PROJECT:
+ System.out.print(String.format(
+ "Scanning %1$s: ",
+ context.project.getDir().getName()));
+ break;
+ case SCANNING_FILE:
+ System.out.print('.');
+ break;
+ case CANCELED:
+ case COMPLETED:
+ System.out.println();
+ break;
+ }
+ }
+ }
}
diff --git a/lint/cli/src/com/android/tools/lint/PositionXmlParser.java b/lint/cli/src/com/android/tools/lint/PositionXmlParser.java
index 06c195b..aa7f9c9 100644
--- a/lint/cli/src/com/android/tools/lint/PositionXmlParser.java
+++ b/lint/cli/src/com/android/tools/lint/PositionXmlParser.java
@@ -16,13 +16,11 @@
package com.android.tools.lint;
-import com.android.tools.lint.api.IDomParser;
+import com.android.tools.lint.client.api.IDomParser;
+import com.android.tools.lint.client.api.IssueRegistry;
import com.android.tools.lint.detector.api.Context;
-import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.Location;
import com.android.tools.lint.detector.api.Position;
-import com.android.tools.lint.detector.api.Scope;
-import com.android.tools.lint.detector.api.Severity;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
@@ -36,7 +34,6 @@ import org.xml.sax.helpers.DefaultHandler;
import java.io.StringReader;
import java.util.ArrayList;
-import java.util.EnumSet;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -74,25 +71,19 @@ public class PositionXmlParser implements IDomParser {
parser.parse(input, handler);
return handler.getDocument();
} catch (ParserConfigurationException e) {
- context.toolContext.log(e, null);
+ context.client.log(e, null);
} catch (SAXException e) {
- context.toolContext.report(
+ context.client.report(
context,
// Must provide an issue since API guarantees that the issue parameter
// is valid
- Issue.create("fatal", "", "", "", 10, Severity.ERROR, //$NON-NLS-1$
- EnumSet.noneOf(Scope.class)),
+ IssueRegistry.PARSER_ERROR,
new Location(context.file, null, null),
e.getCause() != null ? e.getCause().getLocalizedMessage() :
e.getLocalizedMessage(),
null);
-
- context.toolContext.log(null, String.format("Failed parsing %1$s: %2$s",
- context.file.getName(),
- e.getCause() != null ? e.getCause().getLocalizedMessage() :
- e.getLocalizedMessage()));
} catch (Throwable t) {
- context.toolContext.log(t, null);
+ context.client.log(t, null);
}
return null;
}
@@ -378,4 +369,7 @@ public class PositionXmlParser implements IDomParser {
return mColumn;
}
}
+
+ public void dispose(Context context) {
+ }
}
diff --git a/lint/cli/src/com/android/tools/lint/Warning.java b/lint/cli/src/com/android/tools/lint/Warning.java
index 0cc3d81..e6ca111 100644
--- a/lint/cli/src/com/android/tools/lint/Warning.java
+++ b/lint/cli/src/com/android/tools/lint/Warning.java
@@ -16,7 +16,7 @@
package com.android.tools.lint;
-import com.android.tools.lint.api.ToolContext;
+import com.android.tools.lint.client.api.LintClient;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.Location;
import com.android.tools.lint.detector.api.Severity;
@@ -24,7 +24,7 @@ import com.android.tools.lint.detector.api.Severity;
import java.io.File;
/**
- * A {@link Warning} represents a specific warning that a {@link ToolContext}
+ * A {@link Warning} represents a specific warning that a {@link LintClient}
* has been told about. The context stores these as they are reported into a
* list of warnings such that it can sort them all before presenting them all at
* the end.
@@ -52,7 +52,7 @@ class Warning implements Comparable<Warning> {
// ---- Implements Comparable<Warning> ----
public int compareTo(Warning other) {
- // Sort by priority, then by category, then by id,
+ // Sort by category, then by priority, then by id,
// then by file, then by line
String id1 = issue.getId();
String id2 = other.issue.getId();
@@ -60,15 +60,15 @@ class Warning implements Comparable<Warning> {
return file.getName().compareTo(
other.file.getName());
}
+ int categoryDelta = issue.getCategory().compareTo(other.issue.getCategory());
+ if (categoryDelta != 0) {
+ return categoryDelta;
+ }
// DECREASING priority order
int priorityDelta = other.issue.getPriority() - issue.getPriority();
if (priorityDelta != 0) {
return priorityDelta;
}
- int categoryDelta = issue.getCategory().compareTo(other.issue.getCategory());
- if (categoryDelta != 0) {
- return categoryDelta;
- }
int idDelta = id1.compareTo(id2);
if (idDelta != -1) {
return idDelta;
diff --git a/lint/cli/src/com/android/tools/lint/XmlReporter.java b/lint/cli/src/com/android/tools/lint/XmlReporter.java
new file mode 100644
index 0000000..39044eb
--- /dev/null
+++ b/lint/cli/src/com/android/tools/lint/XmlReporter.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.lint;
+
+import com.android.tools.lint.detector.api.Position;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.Writer;
+import java.util.List;
+
+/**
+ * A reporter which emits lint results into an XML report.
+ */
+class XmlReporter extends Reporter {
+ private final File mOutput;
+
+ XmlReporter(File output) throws IOException {
+ super(new BufferedWriter(new FileWriter(output)));
+ mOutput = output;
+ }
+
+ @Override
+ void write(int errorCount, int warningCount, List<Warning> issues) throws IOException {
+ mWriter.write(
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + //$NON-NLS-1$
+ "<issues>\n"); //$NON-NLS-1$
+
+ if (issues.size() > 0) {
+ for (Warning warning : issues) {
+ mWriter.write("\n <issue");
+ writeAttribute(mWriter, "id", warning.issue.getId()); //$NON-NLS-1$
+ writeAttribute(mWriter, "severity", warning.severity.getDescription()); //$NON-NLS-1$
+ writeAttribute(mWriter, "message", warning.issue.getId()); //$NON-NLS-1$
+ if (warning.file != null) {
+ writeAttribute(mWriter, "file", warning.file.getPath()); //$NON-NLS-1$
+ if (warning.location != null) {
+ Position start = warning.location.getStart();
+ if (start != null) {
+ int line = start.getLine();
+ int column = start.getColumn();
+ if (line >= 0) {
+ writeAttribute(mWriter, "line", Integer.toString(line)); //$NON-NLS-1$
+ if (column >= 0) {
+ writeAttribute(mWriter, "column", Integer.toString(column)); //$NON-NLS-1$
+ }
+ }
+ }
+ }
+ }
+ mWriter.write("\n />\n");
+ }
+ }
+
+ mWriter.write(
+ "\n</issues>\n"); //$NON-NLS-1$
+ mWriter.close();
+
+ String path = mOutput.getAbsolutePath();
+ System.out.println(String.format("Wrote HTML report to %1$s", path));
+ }
+
+ private static void writeAttribute(Writer writer, String name, String value)
+ throws IOException {
+ writer.write("\n "); //$NON-NLS-1$
+ writer.write(name);
+ writer.write('=');
+ writer.write('"');
+ for (int i = 0, n = value.length(); i < n; i++) {
+ char c = value.charAt(i);
+ switch (c) {
+ case '"':
+ writer.write("&quot;"); //$NON-NLS-1$
+ break;
+ case '\'':
+ writer.write("&apos;"); //$NON-NLS-1$
+ break;
+ case '&':
+ writer.write("&amp;"); //$NON-NLS-1$
+ break;
+ case '<':
+ writer.write("&lt;"); //$NON-NLS-1$
+ break;
+ default:
+ writer.write(c);
+ break;
+ }
+ }
+ writer.write('"');
+ }
+} \ No newline at end of file
diff --git a/lint/libs/lint_api/src/com/android/tools/lint/api/DetectorRegistry.java b/lint/libs/lint_api/src/com/android/tools/lint/api/DetectorRegistry.java
deleted file mode 100644
index 441601b..0000000
--- a/lint/libs/lint_api/src/com/android/tools/lint/api/DetectorRegistry.java
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.tools.lint.api;
-
-import com.android.tools.lint.detector.api.Detector;
-import com.android.tools.lint.detector.api.Issue;
-import com.android.tools.lint.detector.api.Severity;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/** Registry which provides a list of checks to be performed on an Android project */
-public abstract class DetectorRegistry {
- private static List<Issue> sIssues;
- private static List<String> sCategories;
- private static Map<String, Issue> sIdToIssue;
-
- /**
- * Returns the list of detectors to be run.
- *
- * @return the list of checks to be performed (including those that may be
- * disabled!)
- */
- public abstract List<? extends Detector> getDetectors();
-
- /**
- * Returns true if the given id represents a valid issue id
- *
- * @param id the id to be checked
- * @return true if the given id is valid
- */
- public boolean isIssueId(String id) {
- return getIssue(id) != null;
- }
-
- /**
- * Returns true if the given category is a valid category
- *
- * @param category the category to be checked
- * @return true if the given string is a valid category
- */
- public boolean isCategory(String category) {
- for (String c : getCategories()) {
- if (c.equals(category)) {
- return true;
- }
- }
-
- return false;
- }
-
- /**
- * Returns the available categories
- *
- * @return an iterator for all the categories, never null
- */
- public List<String> getCategories() {
- if (sCategories == null) {
- // Compute the categories from the available issues. The order of categories
- // will be determined by finding the maximum severity and maximum priority of
- // the issues in each category and then sorting in descending order, using
- // alphabetical order if the others are the same.
- final Map<String, Integer> maxPriority = new HashMap<String, Integer>();
- final Map<String, Severity> maxSeverity = new HashMap<String, Severity>();
- for (Issue issue : getIssues()) {
- String category = issue.getCategory();
- Integer priority = maxPriority.get(category);
- if (priority == null || priority.intValue() < issue.getPriority()) {
- maxPriority.put(category, issue.getPriority());
- }
- Severity severity = maxSeverity.get(category);
- if (severity == null || severity.compareTo(issue.getDefaultSeverity()) < 0) {
- maxSeverity.put(category, issue.getDefaultSeverity());
- }
- }
- List<String> categories = new ArrayList<String>(maxPriority.keySet());
- Collections.sort(categories, new Comparator<String>() {
- public int compare(String category1, String category2) {
- Severity severity1 = maxSeverity.get(category1);
- Severity severity2 = maxSeverity.get(category2);
- if (severity1 != severity2) {
- return severity2.compareTo(severity1);
- }
-
- Integer priority1 = maxPriority.get(category1);
- Integer priority2 = maxPriority.get(category2);
- int compare = priority2.compareTo(priority1);
- if (compare != 0) {
- return compare;
- }
-
- return category1.compareTo(category2);
- }
- });
-
- sCategories = Collections.unmodifiableList(categories);
- }
-
- return sCategories;
- }
-
- /**
- * Returns the issue for the given id, or null if it's not a valid id
- *
- * @param id the id to be checked
- * @return the corresponding issue, or null
- */
- public Issue getIssue(String id) {
- getIssues(); // Ensure initialized
- return sIdToIssue.get(id);
- }
-
- /**
- * Returns the list of issues that can be found by all known detectors.
- *
- * @return the list of issues to be checked (including those that may be
- * disabled!)
- */
- @SuppressWarnings("all") // Turn off warnings for the intentional assertion side effect below
- public List<Issue> getIssues() {
- if (sIssues == null) {
- sIdToIssue = new HashMap<String, Issue>();
-
- List<Issue> issues = new ArrayList<Issue>();
- for (Detector detector : getDetectors()) {
- for (Issue issue : detector.getIssues()) {
- issues.add(issue);
- sIdToIssue.put(issue.getId(), issue);
- }
- }
-
- sIssues = Collections.unmodifiableList(issues);
-
- // Check that ids are unique
- boolean assertionsEnabled = false;
- assert assertionsEnabled = true; // Intentional side-effect
- if (assertionsEnabled) {
- Set<String> ids = new HashSet<String>();
- for (Issue issue : sIssues) {
- String id = issue.getId();
- assert !ids.contains(id) : "Duplicate id " + id; //$NON-NLS-1$
- ids.add(id);
- }
- }
- }
-
- return sIssues;
- }
-}
diff --git a/lint/libs/lint_api/src/com/android/tools/lint/client/api/Configuration.java b/lint/libs/lint_api/src/com/android/tools/lint/client/api/Configuration.java
new file mode 100644
index 0000000..2cf7598
--- /dev/null
+++ b/lint/libs/lint_api/src/com/android/tools/lint/client/api/Configuration.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.lint.client.api;
+
+import com.android.tools.lint.detector.api.Context;
+import com.android.tools.lint.detector.api.Issue;
+import com.android.tools.lint.detector.api.Location;
+import com.android.tools.lint.detector.api.Severity;
+
+/**
+ * Lint configuration for an Android project such as which specific rules to include,
+ * which specific rules to exclude, and which specific errors to ignore.
+ * <p/>
+ * <b>NOTE: This is not a public or final API; if you rely on this be prepared
+ * to adjust your code for the next tools release.</b>
+ */
+public abstract class Configuration {
+ /**
+ * Checks whether this issue should be ignored because the user has already
+ * suppressed the error? Note that this refers to individual issues being
+ * suppressed/ignored, not a whole detector being disabled via something
+ * like {@link #isEnabled(Issue)}.
+ *
+ * @param context the context used by the detector when the issue was found
+ * @param issue the issue that was found
+ * @param location the location of the issue
+ * @param message the associated user message
+ * @param data additional information about an issue (see
+ * {@link LintClient#report(Context, Issue, Location, String, Object)} for
+ * more information
+ * @return true if this issue should be suppressed
+ */
+ public boolean isIgnored(Context context, Issue issue, Location location,
+ String message, Object data) {
+ return false;
+ }
+
+ /**
+ * Returns false if the given issue has been disabled. This is just
+ * a convenience method for {@code getSeverity(issue) != Severity.IGNORE}.
+ *
+ * @param issue the issue to check
+ * @return false if the issue has been disabled
+ */
+ public boolean isEnabled(Issue issue) {
+ return getSeverity(issue) != Severity.IGNORE;
+ }
+
+ /**
+ * Returns the severity for a given issue. This is the same as the
+ * {@link Issue#getDefaultSeverity()} unless the user has selected a custom
+ * severity (which is tool context dependent).
+ *
+ * @param issue the issue to look up the severity from
+ * @return the severity use for issues for the given detector
+ */
+ public Severity getSeverity(Issue issue) {
+ return issue.getDefaultSeverity();
+ }
+
+ // Editing configurations
+
+ /**
+ * Marks the given warning as "ignored".
+ *
+ * @param context The scanning context
+ * @param issue the issue to be ignored
+ * @param location The location to ignore the warning at
+ * @param message The message for the warning
+ * @param data The corresponding data, or null
+ */
+ public abstract void ignore(Context context, Issue issue, Location location,
+ String message, Object data);
+
+ /**
+ * Sets the severity to be used for this issue.
+ *
+ * @param issue the issue to set the severity for
+ * @param severity the severity to associate with this issue, or null to
+ * reset the severity to the default
+ */
+ public abstract void setSeverity(Issue issue, Severity severity);
+
+ // Bulk editing support
+
+ /**
+ * Marks the beginning of a "bulk" editing operation with repeated calls to
+ * {@link #setSeverity} or {@link #ignore}. After all the values haver been
+ * set, the client <b>must</b> call {@link #finishBulkEditing()}. This
+ * allows configurations to avoid doing expensive I/O (such as writing out a
+ * config XML file) for each and every editing operation when they are
+ * applied in bulk, such as from a configuration dialog's "Apply" action.
+ */
+ public void startBulkEditing() {
+ }
+
+ /**
+ * Marks the end of a "bulk" editing operation, where values should be
+ * committed to persistent storage. See {@link #startBulkEditing()} for
+ * details.
+ */
+ public void finishBulkEditing() {
+ }
+}
diff --git a/lint/libs/lint_api/src/com/android/tools/lint/client/api/DefaultConfiguration.java b/lint/libs/lint_api/src/com/android/tools/lint/client/api/DefaultConfiguration.java
new file mode 100644
index 0000000..deb8b02
--- /dev/null
+++ b/lint/libs/lint_api/src/com/android/tools/lint/client/api/DefaultConfiguration.java
@@ -0,0 +1,377 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.lint.client.api;
+
+import com.android.tools.lint.detector.api.Context;
+import com.android.tools.lint.detector.api.Issue;
+import com.android.tools.lint.detector.api.Location;
+import com.android.tools.lint.detector.api.Project;
+import com.android.tools.lint.detector.api.Severity;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+/**
+ * Default implementation of a {@link Configuration} which reads and writes
+ * configuration data into {@code lint.xml} in the project directory.
+ *
+ * <p/>
+ * <b>NOTE: This is not a public or final API; if you rely on this be prepared
+ * to adjust your code for the next tools release.</b>
+ */
+public class DefaultConfiguration extends Configuration {
+ private final LintClient mClient;
+ private static final String CONFIG_FILE_NAME = "lint.xml"; //$NON-NLS-1$
+
+ // Lint XML File
+ private static final String TAG_ISSUE = "issue"; //$NON-NLS-1$
+ private static final String ATTR_ID = "id"; //$NON-NLS-1$
+ private static final String ATTR_SEVERITY = "severity"; //$NON-NLS-1$
+ private static final String ATTR_PATH = "path"; //$NON-NLS-1$
+ private static final String TAG_IGNORE = "ignore"; //$NON-NLS-1$
+
+ private final Configuration mParent;
+ protected final Project mProject;
+ private final File mConfigFile;
+ private boolean mBulkEditing;
+
+ /** Map from id to list of project-relative paths for suppressed warnings */
+ private Map<String, List<String>> mSuppressed;
+
+ /**
+ * Map from id to custom {@link Severity} override
+ */
+ private Map<String, Severity> mSeverity;
+
+ protected DefaultConfiguration(LintClient client, Project project, Configuration parent) {
+ mClient = client;
+ mProject = project;
+ mParent = parent;
+ mConfigFile = new File(project.getDir(), CONFIG_FILE_NAME);
+ }
+
+ /**
+ * Creates a new {@link DefaultConfiguration}
+ *
+ * @param client the client to report errors to etc
+ * @param project the associated project
+ * @param parent the parent/fallback configuration or null
+ * @return a new configuration
+ */
+ public static DefaultConfiguration create(LintClient client, Project project,
+ Configuration parent) {
+ return new DefaultConfiguration(client, project, parent);
+ }
+
+ @Override
+ public boolean isIgnored(Context context, Issue issue, Location location, String message,
+ Object data) {
+ ensureInitialized();
+
+ String id = issue.getId();
+ List<String> paths = mSuppressed.get(id);
+ if (paths != null && location != null) {
+ File file = location.getFile();
+ String relativePath = context.project.getRelativePath(file);
+ for (String suppressedPath : paths) {
+ if (suppressedPath.equals(relativePath)) {
+ return true;
+ }
+ }
+ }
+
+ if (mParent != null) {
+ return mParent.isIgnored(context, issue, location, message, data);
+ }
+
+ return false;
+ }
+
+ @Override
+ public Severity getSeverity(Issue issue) {
+ ensureInitialized();
+
+ Severity severity = mSeverity.get(issue.getId());
+ if (severity != null) {
+ return severity;
+ }
+
+ if (mParent != null) {
+ return mParent.getSeverity(issue);
+ }
+
+ if (!issue.isEnabledByDefault()) {
+ return Severity.IGNORE;
+ }
+
+ return issue.getDefaultSeverity();
+ }
+
+ private void ensureInitialized() {
+ if (mSuppressed == null) {
+ readConfig();
+ }
+ }
+
+ private void readConfig() {
+ mSuppressed = new HashMap<String, List<String>>();
+ mSeverity = new HashMap<String, Severity>();
+
+ if (!mConfigFile.exists()) {
+ return;
+ }
+
+ try {
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ BufferedInputStream input = new BufferedInputStream(new FileInputStream(mConfigFile));
+ InputSource source = new InputSource(input);
+ factory.setNamespaceAware(false);
+ factory.setValidating(false);
+ DocumentBuilder builder = factory.newDocumentBuilder();
+ Document document = builder.parse(source);
+ NodeList issues = document.getElementsByTagName(TAG_ISSUE);
+ for (int i = 0, count = issues.getLength(); i < count; i++) {
+ Node node = issues.item(i);
+ Element element = (Element) node;
+ String id = element.getAttribute(ATTR_ID);
+ if (id.length() == 0) {
+ mClient.log(null,
+ "Invalid lint config file: Missing required issue id attribute");
+ continue;
+ }
+
+ NamedNodeMap attributes = node.getAttributes();
+ for (int j = 0, n = attributes.getLength(); j < n; j++) {
+ Node attribute = attributes.item(j);
+ String name = attribute.getNodeName();
+ String value = attribute.getNodeValue();
+ if (ATTR_ID.equals(name)) {
+ // already handled
+ } else if (ATTR_SEVERITY.equals(name)) {
+ for (Severity severity : Severity.values()) {
+ if (value.equalsIgnoreCase(severity.name())) {
+ mSeverity.put(id, severity);
+ break;
+ }
+ }
+ } else {
+ mClient.log(null, "Unexpected attribute %1$s", name);
+ }
+ }
+
+ // Look up ignored errors
+ NodeList childNodes = element.getChildNodes();
+ if (childNodes.getLength() > 0) {
+ for (int j = 0, n = childNodes.getLength(); j < n; j++) {
+ Node child = childNodes.item(j);
+ if (child.getNodeType() == Node.ELEMENT_NODE) {
+ Element ignore = (Element) child;
+ String path = ignore.getAttribute(ATTR_PATH);
+ if (path.length() == 0) {
+ mClient.log(null, "Missing required %1$s attribute under %2$s",
+ ATTR_PATH, id);
+ } else {
+ List<String> paths = mSuppressed.get(id);
+ if (paths == null) {
+ paths = new ArrayList<String>(n / 2 + 1);
+ mSuppressed.put(id, paths);
+ }
+ paths.add(path);
+ }
+ }
+ }
+ }
+ }
+ } catch (Exception e) {
+ mClient.log(e, null);
+ }
+ }
+
+ private void writeConfig() {
+ try {
+ // Write the contents to a new file first such that we don't clobber the
+ // existing file if some I/O error occurs.
+ File file = new File(mConfigFile.getParentFile(),
+ mConfigFile.getName() + ".new"); //$NON-NLS-1$
+
+ Writer writer = new BufferedWriter(new FileWriter(file));
+ writer.write(
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + //$NON-NLS-1$
+ "<lint>\n"); //$NON-NLS-1$
+
+ if (mSuppressed.size() > 0 || mSeverity.size() > 0) {
+ // Process the maps in a stable sorted order such that if the
+ // files are checked into version control with the project,
+ // there are no random diffs just because hashing algorithms
+ // differ:
+ Set<String> idSet = new HashSet<String>();
+ for (String id : mSuppressed.keySet()) {
+ idSet.add(id);
+ }
+ for (String id : mSeverity.keySet()) {
+ idSet.add(id);
+ }
+ List<String> ids = new ArrayList<String>(idSet);
+ Collections.sort(ids);
+
+ for (String id : ids) {
+ writer.write(" <"); //$NON-NLS-1$
+ writer.write(TAG_ISSUE);
+ writeAttribute(writer, ATTR_ID, id);
+ Severity severity = mSeverity.get(id);
+ if (severity != null) {
+ writeAttribute(writer, ATTR_SEVERITY, severity.name().toLowerCase());
+ }
+
+ List<String> paths = mSuppressed.get(id);
+ if (paths != null && paths.size() > 0) {
+ writer.write('>');
+ writer.write('\n');
+ // The paths are already kept in sorted order when they are modified
+ // by ignore(...)
+ for (String path : paths) {
+ writer.write(" <"); //$NON-NLS-1$
+ writer.write(TAG_IGNORE);
+ writeAttribute(writer, ATTR_PATH, path);
+ writer.write(" />\n"); //$NON-NLS-1$
+ }
+ writer.write(" </"); //$NON-NLS-1$
+ writer.write(TAG_ISSUE);
+ writer.write('>');
+ writer.write('\n');
+ } else {
+ writer.write(" />\n"); //$NON-NLS-1$
+ }
+ }
+ }
+
+ writer.write("</lint>"); //$NON-NLS-1$
+ writer.close();
+
+ // Move file into place: move current version to lint.xml~ (removing the old ~ file
+ // if it exists), then move the new version to lint.xml.
+ File oldFile = new File(mConfigFile.getParentFile(),
+ mConfigFile.getName() + "~"); //$NON-NLS-1$
+ if (oldFile.exists()) {
+ oldFile.delete();
+ }
+ if (mConfigFile.exists()) {
+ mConfigFile.renameTo(oldFile);
+ }
+ boolean ok = file.renameTo(mConfigFile);
+ if (ok && oldFile.exists()) {
+ oldFile.delete();
+ }
+ } catch (Exception e) {
+ mClient.log(e, null);
+ }
+ }
+
+ private static void writeAttribute(Writer writer, String name, String value)
+ throws IOException {
+ writer.write(' ');
+ writer.write(name);
+ writer.write('=');
+ writer.write('"');
+ writer.write(value);
+ writer.write('"');
+ }
+
+ @Override
+ public void ignore(Context context, Issue issue, Location location, String message,
+ Object data) {
+ // This configuration only supports suppressing warnings on a per-file basis
+ if (location != null) {
+ ignore(issue, location.getFile());
+ }
+ }
+
+ /**
+ * Marks the given issue and file combination as being ignored.
+ *
+ * @param issue the issue to be ignored in the given file
+ * @param file the file to ignore the issue in
+ */
+ public void ignore(Issue issue, File file) {
+ ensureInitialized();
+
+ String path = mProject.getRelativePath(file);
+
+ List<String> paths = mSuppressed.get(issue.getId());
+ if (paths == null) {
+ paths = new ArrayList<String>();
+ mSuppressed.put(issue.getId(), paths);
+ }
+ paths.add(path);
+
+ // Keep paths sorted alphabetically; makes XML output stable
+ Collections.sort(paths);
+
+ if (!mBulkEditing) {
+ writeConfig();
+ }
+ }
+
+ @Override
+ public void setSeverity(Issue issue, Severity severity) {
+ ensureInitialized();
+
+ String id = issue.getId();
+ if (severity == null) {
+ mSeverity.remove(id);
+ } else {
+ mSeverity.put(id, severity);
+ }
+
+ if (!mBulkEditing) {
+ writeConfig();
+ }
+ }
+
+ @Override
+ public void startBulkEditing() {
+ mBulkEditing = true;
+ }
+
+ @Override
+ public void finishBulkEditing() {
+ mBulkEditing = false;
+ writeConfig();
+ }
+}
diff --git a/lint/libs/lint_api/src/com/android/tools/lint/api/IDomParser.java b/lint/libs/lint_api/src/com/android/tools/lint/client/api/IDomParser.java
index 41e0449..d1d9461 100644
--- a/lint/libs/lint_api/src/com/android/tools/lint/api/IDomParser.java
+++ b/lint/libs/lint_api/src/com/android/tools/lint/client/api/IDomParser.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.tools.lint.api;
+package com.android.tools.lint.client.api;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Location;
@@ -72,4 +72,10 @@ public interface IDomParser {
* @return a location for the given node
*/
public Location getLocation(Context context, Node node);
+
+ /**
+ * Dispose any data structures held for the given context.
+ * @param context information about the file previously parsed
+ */
+ public void dispose(Context context);
}
diff --git a/lint/libs/lint_api/src/com/android/tools/lint/client/api/IssueRegistry.java b/lint/libs/lint_api/src/com/android/tools/lint/client/api/IssueRegistry.java
new file mode 100644
index 0000000..c01f7c3
--- /dev/null
+++ b/lint/libs/lint_api/src/com/android/tools/lint/client/api/IssueRegistry.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.lint.client.api;
+
+import com.android.tools.lint.detector.api.Category;
+import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Issue;
+import com.android.tools.lint.detector.api.Scope;
+import com.android.tools.lint.detector.api.Severity;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/** Registry which provides a list of checks to be performed on an Android project */
+public abstract class IssueRegistry {
+ private static List<Category> sCategories;
+ private static Map<String, Issue> sIdToIssue;
+
+ /**
+ * Issue reported by lint (not a specific detector) when it cannot even
+ * parse an XML file prior to analysis
+ */
+ public static final Issue PARSER_ERROR = Issue.create(
+ "XmlParserError", //$NON-NLS-1$
+ "Finds XML files that contain fatal parser errors",
+ "XML files must be parsable.",
+ Category.CORRECTNESS,
+ 10,
+ Severity.ERROR,
+ null,
+ Scope.RESOURCE_FILE_SCOPE);
+
+ /**
+ * Returns the list of issues that can be found by all known detectors.
+ *
+ * @return the list of issues to be checked (including those that may be
+ * disabled!)
+ */
+ public abstract List<Issue> getIssues();
+
+ /**
+ * Creates a list of detectors applicable to the given cope, and with the
+ * given configuration.
+ *
+ * @param client the client to report errors to
+ * @param configuration the configuration to look up which issues are
+ * enabled etc from
+ * @param scope the scope for the analysis, to filter out detectors that
+ * require wider analysis than is currently being performed
+ * @param scopeToDetectors an optional map which (if not null) will be
+ * filled by this method to contain mappings from each scope to
+ * the applicable detectors for that scope
+ * @return a list of new detector instances
+ */
+ final List<? extends Detector> createDetectors(
+ LintClient client,
+ Configuration configuration,
+ EnumSet<Scope> scope,
+ Map<Scope, List<Detector>> scopeToDetectors) {
+ List<Issue> issues = getIssues();
+ Set<Class<? extends Detector>> detectorClasses = new HashSet<Class<? extends Detector>>();
+ Map<Class<? extends Detector>, EnumSet<Scope>> detectorToScope =
+ new HashMap<Class<? extends Detector>, EnumSet<Scope>>();
+ for (Issue issue : issues) {
+ Class<? extends Detector> detectorClass = issue.getDetectorClass();
+ if (detectorClasses.contains(detectorClass)) {
+ continue;
+ }
+
+ // Determine if the issue is enabled
+ if (!configuration.isEnabled(issue)) {
+ continue;
+ }
+
+ // Determine if the scope matches
+ if (!scope.containsAll(issue.getScope())) {
+ continue;
+ }
+
+ detectorClass = client.replaceDetector(detectorClass);
+
+ if (scopeToDetectors != null) {
+ EnumSet<Scope> s = detectorToScope.get(detectorClass);
+ if (s == null) {
+ detectorToScope.put(detectorClass, issue.getScope());
+ } else {
+ EnumSet<Scope> union = EnumSet.copyOf(s);
+ union.addAll(issue.getScope());
+ detectorToScope.put(detectorClass, union);
+ }
+ }
+
+ assert detectorClass != null : issue.getId();
+ detectorClasses.add(detectorClass);
+ }
+
+ List<Detector> detectors = new ArrayList<Detector>(detectorClasses.size());
+ for (Class<? extends Detector> clz : detectorClasses) {
+ try {
+ Detector detector = clz.newInstance();
+ detectors.add(detector);
+
+ if (scopeToDetectors != null) {
+ EnumSet<Scope> union = detectorToScope.get(clz);
+ for (Scope s : union) {
+ List<Detector> list = scopeToDetectors.get(s);
+ if (list == null) {
+ list = new ArrayList<Detector>();
+ scopeToDetectors.put(s, list);
+ }
+ list.add(detector);
+ }
+
+ }
+ } catch (Throwable t) {
+ client.log(t, "Can't initialize detector %1$s", clz.getName()); //$NON-NLS-1$
+ }
+ }
+
+ return detectors;
+ }
+
+ /**
+ * Returns true if the given id represents a valid issue id
+ *
+ * @param id the id to be checked
+ * @return true if the given id is valid
+ */
+ public final boolean isIssueId(String id) {
+ return getIssue(id) != null;
+ }
+
+ /**
+ * Returns true if the given category is a valid category
+ *
+ * @param name the category name to be checked
+ * @return true if the given string is a valid category
+ */
+ public final boolean isCategoryName(String name) {
+ for (Category c : getCategories()) {
+ if (c.getName().equals(name) || c.getFullName().equals(name)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns the available categories
+ *
+ * @return an iterator for all the categories, never null
+ */
+ public List<Category> getCategories() {
+ if (sCategories == null) {
+ final Set<Category> categories = new HashSet<Category>();
+ for (Issue issue : getIssues()) {
+ categories.add(issue.getCategory());
+ }
+ List<Category> sorted = new ArrayList<Category>(categories);
+ Collections.sort(sorted);
+ sCategories = Collections.unmodifiableList(sorted);
+ }
+
+ return sCategories;
+ }
+
+ /**
+ * Returns the issue for the given id, or null if it's not a valid id
+ *
+ * @param id the id to be checked
+ * @return the corresponding issue, or null
+ */
+ public final Issue getIssue(String id) {
+ if (sIdToIssue == null) {
+ List<Issue> issues = getIssues();
+ sIdToIssue = new HashMap<String, Issue>(issues.size());
+ for (Issue issue : issues) {
+ sIdToIssue.put(issue.getId(), issue);
+ }
+ }
+ return sIdToIssue.get(id);
+ }
+}
diff --git a/lint/libs/lint_api/src/com/android/tools/lint/api/Lint.java b/lint/libs/lint_api/src/com/android/tools/lint/client/api/Lint.java
index f6be5d2..4d8671a 100644
--- a/lint/libs/lint_api/src/com/android/tools/lint/api/Lint.java
+++ b/lint/libs/lint_api/src/com/android/tools/lint/client/api/Lint.java
@@ -14,12 +14,15 @@
* limitations under the License.
*/
-package com.android.tools.lint.api;
+package com.android.tools.lint.client.api;
import com.android.resources.ResourceFolderType;
+import com.android.tools.lint.client.api.LintListener.EventType;
+import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Detector;
import com.android.tools.lint.detector.api.Issue;
+import com.android.tools.lint.detector.api.LintUtils;
import com.android.tools.lint.detector.api.Location;
import com.android.tools.lint.detector.api.Project;
import com.android.tools.lint.detector.api.ResourceXmlDetector;
@@ -44,27 +47,24 @@ public class Lint {
private static final String ANDROID_MANIFEST_XML = "AndroidManifest.xml"; //$NON-NLS-1$
private static final String RES_FOLDER_NAME = "res"; //$NON-NLS-1$
- private final ToolContext mToolContext;
+ private final LintClient mClient;
private volatile boolean mCanceled;
- private DetectorRegistry mRegistry;
+ private IssueRegistry mRegistry;
private EnumSet<Scope> mScope;
- private Map<Scope, List<Detector>> mApplicableDetectors = new HashMap<Scope, List<Detector>>();
+ private List<? extends Detector> mApplicableDetectors;
+ private Map<Scope, List<Detector>> mScopeDetectors;
+ private List<LintListener> mListeners;
/**
* Creates a new {@link Lint}
*
- * @param registry The registry containing rules to be run
- * @param toolContext a context for the tool wrapping the analyzer, such as
- * an IDE or a CLI
- * @param scope the scope of the analysis; detectors with a wider scope will
- * not be run. If null, the scope will be inferred from the files
- * passed to {@link #analyze(List)}.
+ * @param registry The registry containing issues to be checked
+ * @param client the tool wrapping the analyzer, such as an IDE or a CLI
*/
- public Lint(DetectorRegistry registry, ToolContext toolContext, EnumSet<Scope> scope) {
- assert toolContext != null;
+ public Lint(IssueRegistry registry, LintClient client) {
+ assert client != null;
mRegistry = registry;
- mToolContext = toolContext;
- mScope = scope;
+ mClient = new LintClientWrapper(client);
}
/** Cancels the current lint run as soon as possible */
@@ -74,14 +74,19 @@ public class Lint {
/**
* Analyze the given file (which can point to an Android project). Issues found
- * are reported to the associated {@link ToolContext}.
+ * are reported to the associated {@link LintClient}.
*
* @param files the files and directories to be analyzed
+ * @param scope the scope of the analysis; detectors with a wider scope will
+ * not be run. If null, the scope will be inferred from the files.
*/
- public void analyze(List<File> files) {
+ public void analyze(List<File> files, EnumSet<Scope> scope) {
+ mCanceled = false;
+ mScope = scope;
+
Collection<Project> projects = computeProjects(files);
if (projects.size() == 0) {
- mToolContext.log(null, "No projects found for %1$s", files.toString());
+ mClient.log(null, "No projects found for %1$s", files.toString());
return;
}
if (mCanceled) {
@@ -119,52 +124,28 @@ public class Lint {
}
}
- List<? extends Detector> availableChecks = mRegistry.getDetectors();
- EnumSet<Scope> missingScopes = EnumSet.complementOf(mScope);
-
- // Filter out disabled checks
- List<Detector> checks = new ArrayList<Detector>(availableChecks.size());
- for (Detector detector : availableChecks) {
- boolean hasValidScope = false;
- for (Issue issue : detector.getIssues()) {
- if (mScope.containsAll(issue.getScope())) {
- hasValidScope = true;
- break;
- }
- }
- if (!hasValidScope) {
- continue;
- }
- // A detector is enabled if at least one of its issues is enabled
- EnumSet<Scope> scope = EnumSet.noneOf(Scope.class);
- for (Issue issue : detector.getIssues()) {
- if (mToolContext.isEnabled(issue)) {
- scope.addAll(issue.getScope());
- }
- }
- // Only run those detectors whose scope are matched by the current analysis context:
- scope.removeAll(missingScopes);
- if (scope.size() > 0) {
- checks.add(detector);
- for (Scope s : scope) {
- List<Detector> detectors = mApplicableDetectors.get(s);
- if (detectors == null) {
- detectors = new ArrayList<Detector>();
- mApplicableDetectors.put(s, detectors);
- }
- detectors.add(detector);
- }
- }
- }
-
- validateScopeList();
+ fireEvent(EventType.STARTING, null);
for (Project project : projects) {
- checkProject(project, checks);
+ // The set of available detectors varies between projects
+ computeDetectors(project);
+
+ checkProject(project);
if (mCanceled) {
- return;
+ break;
}
}
+
+ fireEvent(mCanceled ? EventType.CANCELED : EventType.COMPLETED, null);
+ }
+
+ private void computeDetectors(Project project) {
+ Configuration configuration = project.getConfiguration();
+ mScopeDetectors = new HashMap<Scope, List<Detector>>();
+ mApplicableDetectors = mRegistry.createDetectors(mClient, configuration,
+ mScope, mScopeDetectors);
+
+ validateScopeList();
}
/** Development diagnostics only, run with assertions on */
@@ -173,33 +154,33 @@ public class Lint {
boolean assertionsEnabled = false;
assert assertionsEnabled = true; // Intentional side-effect
if (assertionsEnabled) {
- List<Detector> resourceFileDetectors = mApplicableDetectors.get(Scope.RESOURCE_FILE);
+ List<Detector> resourceFileDetectors = mScopeDetectors.get(Scope.RESOURCE_FILE);
if (resourceFileDetectors != null) {
for (Detector detector : resourceFileDetectors) {
assert detector instanceof ResourceXmlDetector : detector;
}
}
- List<Detector> manifestDetectors = mApplicableDetectors.get(Scope.MANIFEST);
+ List<Detector> manifestDetectors = mScopeDetectors.get(Scope.MANIFEST);
if (manifestDetectors != null) {
for (Detector detector : manifestDetectors) {
assert detector instanceof Detector.XmlScanner : detector;
}
}
- List<Detector> javaCodeDetectors = mApplicableDetectors.get(Scope.ALL_JAVA_FILES);
+ List<Detector> javaCodeDetectors = mScopeDetectors.get(Scope.ALL_JAVA_FILES);
if (javaCodeDetectors != null) {
for (Detector detector : javaCodeDetectors) {
assert detector instanceof Detector.JavaScanner : detector;
}
}
- List<Detector> javaFileDetectors = mApplicableDetectors.get(Scope.JAVA_FILE);
+ List<Detector> javaFileDetectors = mScopeDetectors.get(Scope.JAVA_FILE);
if (javaFileDetectors != null) {
for (Detector detector : javaFileDetectors) {
assert detector instanceof Detector.JavaScanner : detector;
}
}
- List<Detector> classDetectors = mApplicableDetectors.get(Scope.CLASS_FILE);
+ List<Detector> classDetectors = mScopeDetectors.get(Scope.CLASS_FILE);
if (classDetectors != null) {
for (Detector detector : classDetectors) {
assert detector instanceof Detector.ClassScanner : detector;
@@ -212,7 +193,8 @@ public class Lint {
File projectDir, File rootDir) {
Project project = fileToProject.get(projectDir);
if (project == null) {
- project = new Project(mToolContext, projectDir, rootDir);
+ project = new Project(mClient, projectDir, rootDir);
+ project.setConfiguration(mClient.getConfiguration(project));
}
fileToProject.put(file, project);
}
@@ -304,11 +286,14 @@ public class Lint {
return new File(dir, ANDROID_MANIFEST_XML).exists();
}
- private void checkProject(Project project, List<Detector> checks) {
+ private void checkProject(Project project) {
+
File projectDir = project.getDir();
- Context projectContext = new Context(mToolContext, project, projectDir, mScope);
- for (Detector check : checks) {
+ Context projectContext = new Context(mClient, project, projectDir, mScope);
+ fireEvent(EventType.SCANNING_PROJECT, projectContext);
+
+ for (Detector check : mApplicableDetectors) {
check.beforeCheckProject(projectContext);
if (mCanceled) {
return;
@@ -317,7 +302,7 @@ public class Lint {
runFileDetectors(project, projectDir);
- for (Detector check : checks) {
+ for (Detector check : mApplicableDetectors) {
check.afterCheckProject(projectContext);
if (mCanceled) {
return;
@@ -325,12 +310,12 @@ public class Lint {
}
if (mCanceled) {
- mToolContext.report(
+ mClient.report(
projectContext,
// Must provide an issue since API guarantees that the issue parameter
// is valid
- Issue.create("dummy", "", "", "", 0, Severity.INFORMATIONAL, //$NON-NLS-1$
- EnumSet.noneOf(Scope.class)),
+ Issue.create("Lint", "", "", Category.PERFORMANCE, 0, Severity.INFORMATIONAL, //$NON-NLS-1$
+ null, EnumSet.noneOf(Scope.class)),
null /*range*/,
"Lint canceled by user", null);
}
@@ -339,13 +324,14 @@ public class Lint {
private void runFileDetectors(Project project, File projectDir) {
// Look up manifest information
if (mScope.contains(Scope.MANIFEST)) {
- List<Detector> detectors = mApplicableDetectors.get(Scope.MANIFEST);
+ List<Detector> detectors = mScopeDetectors.get(Scope.MANIFEST);
if (detectors != null) {
File file = new File(project.getDir(), ANDROID_MANIFEST_XML);
if (file.exists()) {
- Context context = new Context(mToolContext, project, file, mScope);
+ Context context = new Context(mClient, project, file, mScope);
context.location = new Location(file, null, null);
- XmlVisitor v = new XmlVisitor(mToolContext.getParser(), detectors);
+ XmlVisitor v = new XmlVisitor(mClient.getParser(), detectors);
+ fireEvent(EventType.SCANNING_FILE, context);
v.visitFile(context, file);
}
}
@@ -354,8 +340,8 @@ public class Lint {
// Process both Scope.RESOURCE_FILE and Scope.ALL_RESOURCE_FILES detectors together
// in a single pass through the resource directories.
if (mScope.contains(Scope.ALL_RESOURCE_FILES) || mScope.contains(Scope.RESOURCE_FILE)) {
- List<Detector> checks = union(mApplicableDetectors.get(Scope.RESOURCE_FILE),
- mApplicableDetectors.get(Scope.ALL_RESOURCE_FILES));
+ List<Detector> checks = union(mScopeDetectors.get(Scope.RESOURCE_FILE),
+ mScopeDetectors.get(Scope.ALL_RESOURCE_FILES));
if (checks.size() > 0) {
List<ResourceXmlDetector> xmlDetectors =
new ArrayList<ResourceXmlDetector>(checks.size());
@@ -377,29 +363,42 @@ public class Lint {
}
}
+ if (mCanceled) {
+ return;
+ }
+
if (mScope.contains(Scope.JAVA_FILE) || mScope.contains(Scope.ALL_JAVA_FILES)) {
- List<Detector> checks = union(mApplicableDetectors.get(Scope.JAVA_FILE),
- mApplicableDetectors.get(Scope.ALL_JAVA_FILES));
+ List<Detector> checks = union(mScopeDetectors.get(Scope.JAVA_FILE),
+ mScopeDetectors.get(Scope.ALL_JAVA_FILES));
if (checks.size() > 0) {
List<File> sourceFolders = project.getJavaSourceFolders();
checkJava(project, sourceFolders, checks);
}
}
+ if (mCanceled) {
+ return;
+ }
+
if (mScope.contains(Scope.CLASS_FILE)) {
- List<Detector> detectors = mApplicableDetectors.get(Scope.CLASS_FILE);
+ List<Detector> detectors = mScopeDetectors.get(Scope.CLASS_FILE);
if (detectors != null) {
List<File> binFolders = project.getJavaClassFolders();
checkClasses(project, binFolders, detectors);
}
}
+ if (mCanceled) {
+ return;
+ }
+
if (mScope.contains(Scope.PROGUARD)) {
- List<Detector> detectors = mApplicableDetectors.get(Scope.PROGUARD);
+ List<Detector> detectors = mScopeDetectors.get(Scope.PROGUARD);
if (detectors != null) {
File file = new File(project.getDir(), PROGUARD_CFG);
if (file.exists()) {
- Context context = new Context(mToolContext, project, file, mScope);
+ Context context = new Context(mClient, project, file, mScope);
+ fireEvent(EventType.SCANNING_FILE, context);
context.location = new Location(file, null, null);
for (Detector detector : detectors) {
if (detector.appliesTo(context, file)) {
@@ -430,7 +429,8 @@ public class Lint {
}
private void checkClasses(Project project, List<File> binFolders, List<Detector> checks) {
- Context context = new Context(mToolContext, project, project.getDir(), mScope);
+ Context context = new Context(mClient, project, project.getDir(), mScope);
+ fireEvent(EventType.SCANNING_FILE, context);
for (Detector detector : checks) {
((Detector.ClassScanner) detector).checkJavaClasses(context);
@@ -441,7 +441,8 @@ public class Lint {
}
private void checkJava(Project project, List<File> sourceFolders, List<Detector> checks) {
- Context context = new Context(mToolContext, project, project.getDir(), mScope);
+ Context context = new Context(mClient, project, project.getDir(), mScope);
+ fireEvent(EventType.SCANNING_FILE, context);
for (Detector detector : checks) {
((Detector.JavaScanner) detector).checkJavaSources(context, sourceFolders);
@@ -479,7 +480,7 @@ public class Lint {
return null;
}
- mCurrentVisitor = new XmlVisitor(mToolContext.getParser(), applicableChecks);
+ mCurrentVisitor = new XmlVisitor(mClient.getParser(), applicableChecks);
}
return mCurrentVisitor;
@@ -517,9 +518,10 @@ public class Lint {
XmlVisitor visitor = getVisitor(type, checks);
if (visitor != null) { // if not, there are no applicable rules in this folder
for (File file : xmlFiles) {
- if (ResourceXmlDetector.isXmlFile(file)) {
- Context context = new Context(mToolContext, project, file,
+ if (LintUtils.isXmlFile(file)) {
+ Context context = new Context(mClient, project, file,
mScope);
+ fireEvent(EventType.SCANNING_FILE, context);
visitor.visitFile(context, file);
if (mCanceled) {
return;
@@ -544,18 +546,19 @@ public class Lint {
// Yes
checkResFolder(project, file, xmlDetectors);
} else {
- mToolContext.log(null, "Unexpected folder %1$s; should be project, " +
+ mClient.log(null, "Unexpected folder %1$s; should be project, " +
"\"res\" folder or resource folder", file.getPath());
continue;
}
- } else if (file.isFile() && ResourceXmlDetector.isXmlFile(file)) {
+ } else if (file.isFile() && LintUtils.isXmlFile(file)) {
// Yes, find out its resource type
String folderName = file.getParentFile().getName();
ResourceFolderType type = ResourceFolderType.getFolderType(folderName);
if (type != null) {
XmlVisitor visitor = getVisitor(type, xmlDetectors);
if (visitor != null) {
- Context context = new Context(mToolContext, project, file, mScope);
+ Context context = new Context(mClient, project, file, mScope);
+ fireEvent(EventType.SCANNING_FILE, context);
visitor.visitFile(context, file);
}
}
@@ -564,12 +567,107 @@ public class Lint {
}
/**
- * Returns the associated tool context for the surrounding tool that is
- * embedding lint analysis
+ * Adds a listener to be notified of lint progress
*
- * @return the surrounding tool context
+ * @param listener the listener to be added
*/
- public ToolContext getToolContext() {
- return mToolContext;
+ public void addLintListener(LintListener listener) {
+ if (mListeners == null) {
+ mListeners = new ArrayList<LintListener>(1);
+ }
+ mListeners.add(listener);
+ }
+
+ /**
+ * Removes a listener such that it is no longer notified of progress
+ *
+ * @param listener the listener to be removed
+ */
+ public void removeLintListener(LintListener listener) {
+ mListeners.remove(listener);
+ if (mListeners.size() == 0) {
+ mListeners = null;
+ }
+ }
+
+ /** Notifies listeners, if any, that the given event has occurred */
+ private void fireEvent(LintListener.EventType type, Context context) {
+ if (mListeners != null) {
+ for (int i = 0, n = mListeners.size(); i < n; i++) {
+ LintListener listener = mListeners.get(i);
+ listener.update(type, context);
+ }
+ }
+ }
+
+ /**
+ * Wrapper around the lint client. This sits in the middle between a
+ * detector calling for example
+ * {@link LintClient#report(Context, Issue, Location, String, Object)} and
+ * the actual embedding tool, and performs filtering etc such that detectors
+ * and lint clients don't have to make sure they check for ignored issues or
+ * filtered out warnings.
+ */
+ private static class LintClientWrapper extends LintClient {
+ private LintClient mDelegate;
+
+ public LintClientWrapper(LintClient delegate) {
+ mDelegate = delegate;
+ }
+
+ @Override
+ public void report(Context context, Issue issue, Location location, String message,
+ Object data) {
+ Configuration configuration = context.configuration;
+ if (!configuration.isEnabled(issue)) {
+ mDelegate.log(null, "Incorrect detector reported disabled issue %1$s",
+ issue.toString());
+ return;
+ }
+
+ if (configuration.isIgnored(context, issue, location, message, data)) {
+ return;
+ }
+
+ Severity severity = configuration.getSeverity(issue);
+ if (severity == Severity.IGNORE) {
+ return;
+ }
+
+ mDelegate.report(context, issue, location, message, data);
+ }
+
+ // Everything else just delegates to the embedding lint client
+
+ @Override
+ public Configuration getConfiguration(Project project) {
+ return mDelegate.getConfiguration(project);
+ }
+
+
+ @Override
+ public void log(Throwable exception, String format, Object... args) {
+ mDelegate.log(exception, format, args);
+ }
+
+ @Override
+ public IDomParser getParser() {
+ return mDelegate.getParser();
+ }
+
+ @Override
+ public String readFile(File file) {
+ return mDelegate.readFile(file);
+ }
+
+ @Override
+ public List<File> getJavaSourceFolders(Project project) {
+ return mDelegate.getJavaSourceFolders(project);
+ }
+
+ @Override
+ public List<File> getJavaClassFolders(Project project) {
+ return mDelegate.getJavaClassFolders(project);
+ }
}
}
diff --git a/lint/libs/lint_api/src/com/android/tools/lint/api/ToolContext.java b/lint/libs/lint_api/src/com/android/tools/lint/client/api/LintClient.java
index 9e7441c..cae89b6 100644
--- a/lint/libs/lint_api/src/com/android/tools/lint/api/ToolContext.java
+++ b/lint/libs/lint_api/src/com/android/tools/lint/client/api/LintClient.java
@@ -14,13 +14,13 @@
* limitations under the License.
*/
-package com.android.tools.lint.api;
+package com.android.tools.lint.client.api;
import com.android.tools.lint.detector.api.Context;
+import com.android.tools.lint.detector.api.Detector;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.Location;
import com.android.tools.lint.detector.api.Project;
-import com.android.tools.lint.detector.api.Severity;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
@@ -43,9 +43,27 @@ import javax.xml.parsers.DocumentBuilderFactory;
* <b>NOTE: This is not a public or final API; if you rely on this be prepared
* to adjust your code for the next tools release.</b>
*/
-public abstract class ToolContext {
+public abstract class LintClient {
/**
- * Report the given issue.
+ * Returns a configuration for use by the given project. The configuration
+ * provides information about which issues are enabled, any customizations
+ * to the severity of an issue, etc.
+ * <p>
+ * By default this method returns a {@link DefaultConfiguration}.
+ *
+ * @param project the project to obtain a configuration for
+ * @return a configuration, never null.
+ */
+ public Configuration getConfiguration(Project project) {
+ return DefaultConfiguration.create(this, project, null);
+ }
+
+ /**
+ * Report the given issue. This method will only be called if the configuration
+ * provided by {@link #getConfiguration(Project)} has reported the corresponding
+ * issue as enabled and has not filtered out the issue with its
+ * {@link Configuration#ignore(Context, Issue, Location, String, Object)} method.
+ * <p>
*
* @param context the context used by the detector when the issue was found
* @param issue the issue that was found
@@ -62,27 +80,6 @@ public abstract class ToolContext {
Object data);
/**
- * Checks whether this issue should be ignored because the user has already
- * suppressed the error? Note that this refers to individual issues being
- * suppressed/ignored, not a whole detector being disabled via something
- * like {@link #isEnabled(Issue)}.
- *
- * @param context the context used by the detector when the issue was found
- * @param issue the issue that was found
- * @param location the location of the issue
- * @param message the associated user message
- * @param severity the severity of the issue
- * @param data additional information about an issue (see
- * {@link #report(Context, Issue, Location, String, Object)} for
- * more information
- * @return true if this issue should be suppressed
- */
- public boolean isSuppressed(Context context, Issue issue, Location location,
- String message, Severity severity, Object data) {
- return false;
- }
-
- /**
* Send an exception to the log
*
* @param exception the exception, possibly null
@@ -92,33 +89,23 @@ public abstract class ToolContext {
public abstract void log(Throwable exception, String format, Object... args);
/**
- * Returns a {@link IDomParser} to use to parse XML
- *
- * @return a new {@link IDomParser}
- */
- public abstract IDomParser getParser();
-
- /**
- * Returns false if the given issue has been disabled
+ * Returns an optimal detector, if applicable. By default, just returns the
+ * original detector, but tools can replace detectors using this hook with a version
+ * that takes advantage of native capabilities of the tool.
*
- * @param issue the issue to check
- * @return false if the issue has been disabled
+ * @param detectorClass the class of the detector to be replaced
+ * @return the new detector class, or just the original detector (not null)
*/
- public boolean isEnabled(Issue issue) {
- return true;
+ public Class<? extends Detector> replaceDetector(Class<? extends Detector> detectorClass) {
+ return detectorClass;
}
/**
- * Returns the severity for a given issue. This is the same as the
- * {@link Issue#getDefaultSeverity()} unless the user has selected a custom
- * severity (which is tool context dependent).
+ * Returns a {@link IDomParser} to use to parse XML
*
- * @param issue the issue to look up the severity from
- * @return the severity use for issues for the given detector
+ * @return a new {@link IDomParser}
*/
- public Severity getSeverity(Issue issue) {
- return issue.getDefaultSeverity();
- }
+ public abstract IDomParser getParser();
/**
* Reads the given text file and returns the content as a string
diff --git a/lint/libs/lint_api/src/com/android/tools/lint/client/api/LintListener.java b/lint/libs/lint_api/src/com/android/tools/lint/client/api/LintListener.java
new file mode 100644
index 0000000..1852f87
--- /dev/null
+++ b/lint/libs/lint_api/src/com/android/tools/lint/client/api/LintListener.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.lint.client.api;
+
+import com.android.tools.lint.detector.api.Context;
+
+/** Interface implemented by listeners to be notified of lint events */
+public interface LintListener {
+ /** The various types of events provided to lint listeners */
+ public enum EventType {
+ /** A lint check is about to begin */
+ STARTING,
+ /** Lint is about to check the given project, see {@link Context#project} */
+ SCANNING_PROJECT,
+ /** Lint is about to check the given file, see {@link Context#file} */
+ SCANNING_FILE,
+ /** The lint check was canceled */
+ CANCELED,
+ /** The lint check is done */
+ COMPLETED,
+ };
+
+ /**
+ * Notifies listeners that the event of the given type has occurred. Additional
+ * information, such as the file being scanned, or the project being scanned,
+ * is available in the {@link Context} object (except for the {@link EventType#STARTING},
+ * {@link EventType#CANCELED} or {@link EventType#COMPLETED} events which are fired
+ * outside of project contexts.)
+ *
+ * @param type the type of event that occurred
+ * @param context the context providing additional information
+ */
+ public void update(EventType type, Context context);
+}
diff --git a/lint/libs/lint_api/src/com/android/tools/lint/api/XmlVisitor.java b/lint/libs/lint_api/src/com/android/tools/lint/client/api/XmlVisitor.java
index b469f9a..2035bca 100644
--- a/lint/libs/lint_api/src/com/android/tools/lint/api/XmlVisitor.java
+++ b/lint/libs/lint_api/src/com/android/tools/lint/client/api/XmlVisitor.java
@@ -14,16 +14,12 @@
* limitations under the License.
*/
-package com.android.tools.lint.api;
+package com.android.tools.lint.client.api;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Detector;
import com.android.tools.lint.detector.api.Detector.XmlScanner;
-import com.android.tools.lint.detector.api.Issue;
-import com.android.tools.lint.detector.api.Location;
-import com.android.tools.lint.detector.api.ResourceXmlDetector;
-import com.android.tools.lint.detector.api.Scope;
-import com.android.tools.lint.detector.api.Severity;
+import com.android.tools.lint.detector.api.LintUtils;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
@@ -34,7 +30,6 @@ import org.w3c.dom.NodeList;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -118,45 +113,43 @@ class XmlVisitor {
}
void visitFile(Context context, File file) {
- assert ResourceXmlDetector.isXmlFile(file);
+ assert LintUtils.isXmlFile(file);
context.location = null;
context.parser = mParser;
- if (context.document == null) {
- context.document = mParser.parse(context);
+ try {
if (context.document == null) {
- context.toolContext.report(
- context,
- // Must provide an issue since API guarantees that the issue parameter
- // is valid
- Issue.create("dummy", "", "", "", 0, Severity.ERROR, //$NON-NLS-1$
- EnumSet.noneOf(Scope.class)),
- new Location(file, null, null),
- "Skipped file because it contains parsing errors", null);
- return;
- }
- if (context.document.getDocumentElement() == null) {
- // Ignore empty documents
- return;
+ context.document = mParser.parse(context);
+ if (context.document == null) {
+ context.client.log(
+ null, "Skipped file because it contains parsing errors");
+ return;
+ }
+ if (context.document.getDocumentElement() == null) {
+ // Ignore empty documents
+ return;
+ }
}
- }
- for (Detector check : mAllDetectors) {
- check.beforeCheckFile(context);
- }
+ for (Detector check : mAllDetectors) {
+ check.beforeCheckFile(context);
+ }
- for (Detector.XmlScanner check : mDocumentDetectors) {
- check.visitDocument(context, context.document);
- }
+ for (Detector.XmlScanner check : mDocumentDetectors) {
+ check.visitDocument(context, context.document);
+ }
- if (mElementToCheck.size() > 0 || mAttributeToCheck.size() > 0
- || mAllAttributeDetectors.size() > 0 || mAllElementDetectors.size() > 0) {
- visitElement(context, context.document.getDocumentElement());
- }
+ if (mElementToCheck.size() > 0 || mAttributeToCheck.size() > 0
+ || mAllAttributeDetectors.size() > 0 || mAllElementDetectors.size() > 0) {
+ visitElement(context, context.document.getDocumentElement());
+ }
- for (Detector check : mAllDetectors) {
- check.afterCheckFile(context);
+ for (Detector check : mAllDetectors) {
+ check.afterCheckFile(context);
+ }
+ } finally {
+ mParser.dispose(context);
}
}
diff --git a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Category.java b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Category.java
new file mode 100644
index 0000000..41eac96
--- /dev/null
+++ b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Category.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.lint.detector.api;
+
+/**
+ * A category is a container for related issues.
+ * <p/>
+ * <b>NOTE: This is not a public or final API; if you rely on this be prepared
+ * to adjust your code for the next tools release.</b>
+ */
+public final class Category implements Comparable<Category> {
+ private final String mName;
+ private final String mExplanation;
+ private final int mPriority;
+ private final Category mParent;
+
+ /**
+ * Creates a new {@link Category}.
+ *
+ * @param parent the name of a parent category, or null
+ * @param name the name of the category
+ * @param explanation an optional explanation of the category
+ * @param priority a sorting priority, with higher being more important
+ */
+ private Category(Category parent, String name, String explanation, int priority) {
+ mParent = parent;
+ mName = name;
+ mExplanation = explanation;
+ mPriority = priority;
+ }
+
+ /**
+ * Creates a new top level {@link Category} with the given sorting priority.
+ *
+ * @param name the name of the category
+ * @param priority a sorting priority, with higher being more important
+ * @return a new category
+ */
+ public static Category create(String name, int priority) {
+ return new Category(null, name, null, priority);
+ }
+
+ /**
+ * Creates a new top level {@link Category} with the given sorting priority.
+ *
+ * @param parent the name of a parent category, or null
+ * @param name the name of the category
+ * @param explanation an optional explanation of the category
+ * @param priority a sorting priority, with higher being more important
+ * @return a new category
+ */
+ public static Category create(Category parent, String name, String explanation, int priority) {
+ return new Category(parent, name, null, priority);
+ }
+
+ /**
+ * Returns the parent category, or null if this is a top level category
+ *
+ * @return the parent category, or null if this is a top level category
+ */
+ public Category getParent() {
+ return mParent;
+ }
+
+ /**
+ * Returns the name of this category
+ *
+ * @return the name of this category
+ */
+ public String getName() {
+ return mName;
+ }
+
+ /**
+ * Returns an explanation for this category, or null
+ *
+ * @return an explanation for this category, or null
+ */
+ public String getExplanation() {
+ return mExplanation;
+ }
+
+ /**
+ * Returns a full name for this category. For a top level category, this is just
+ * the {@link #getName()} value, but for nested categories it will include the parent
+ * names as well.
+ *
+ * @return a full name for this category
+ */
+ public String getFullName() {
+ if (mParent != null) {
+ return mParent.getFullName() + ':' + mName;
+ } else {
+ return mName;
+ }
+ }
+
+ public int compareTo(Category other) {
+ if (other.mPriority == mPriority) {
+ if (mParent == other) {
+ return 1;
+ } else if (other.mParent == this) {
+ return -1;
+ }
+ }
+ return other.mPriority - mPriority;
+ }
+
+ /** Issues related to correctness */
+ public static final Category CORRECTNESS = Category.create("Correctness", 10);
+ /** Issues related to security */
+ public static final Category SECURITY = Category.create("Security", 9);
+ /** Issues related to performance */
+ public static final Category PERFORMANCE = Category.create("Performance", 8);
+ /** Issues related to usability */
+ public static final Category USABILITY = Category.create("Usability", 7);
+ /** Issues related to accessibility */
+ public static final Category A11Y = Category.create("Accessibility", 6);
+ /** Issues related to internationalization */
+ public static final Category I18N = Category.create("Internationalization", 5);
+
+ // Sub categories
+ /** Issues related to icons */
+ public static final Category ICONS = Category.create(USABILITY, "Icons", null, 7);
+}
diff --git a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Context.java b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Context.java
index 59345c8..d80144a 100644
--- a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Context.java
+++ b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Context.java
@@ -16,8 +16,9 @@
package com.android.tools.lint.detector.api;
-import com.android.tools.lint.api.IDomParser;
-import com.android.tools.lint.api.ToolContext;
+import com.android.tools.lint.client.api.Configuration;
+import com.android.tools.lint.client.api.IDomParser;
+import com.android.tools.lint.client.api.LintClient;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
@@ -44,7 +45,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
public class Context {
public final Project project;
public final File file;
- public final ToolContext toolContext;
+ public final LintClient client;
+ public final Configuration configuration;
public final EnumSet<Scope> scope;
public Document document;
public Location location;
@@ -60,11 +62,14 @@ public class Context {
private Map<String, Object> properties;
- public Context(ToolContext toolContext, Project project, File file, EnumSet<Scope> scope) {
- this.toolContext = toolContext;
+ public Context(LintClient client, Project project, File file,
+ EnumSet<Scope> scope) {
+ this.client = client;
this.project = project;
this.file = file;
this.scope = scope;
+
+ this.configuration = project.getConfiguration();
}
public Location getLocation(Node node) {
@@ -87,7 +92,7 @@ public class Context {
// TODO: This should be delegated to the tool context!
public String getContents() {
if (contents == null) {
- contents = toolContext.readFile(file);
+ contents = client.readFile(file);
}
return contents;
diff --git a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Detector.java b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Detector.java
index 539a454..b48af6a 100644
--- a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Detector.java
+++ b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Detector.java
@@ -19,8 +19,6 @@ package com.android.tools.lint.detector.api;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
import java.io.File;
import java.util.ArrayList;
@@ -124,7 +122,6 @@ public abstract class Detector {
/** Concrete implementation of a detector that is a {@link Detector.XmlScanner} */
public static abstract class XmlDetectorAdapter extends Detector
implements Detector.XmlScanner {
- private static final String XML_SUFFIX = ".xml"; //$NON-NLS-1$
@Override
public void run(Context context) {
@@ -164,67 +161,11 @@ public abstract class Detector {
public Collection<String> getApplicableAttributes() {
return null;
}
-
- /**
- * Returns true if the given file represents an XML file
- *
- * @param file the file to be checked
- * @return true if the given file is an xml file
- */
- public static boolean isXmlFile(File file) {
- String string = file.getName();
- return string.regionMatches(true, string.length() - XML_SUFFIX.length(),
- XML_SUFFIX, 0, XML_SUFFIX.length());
- }
-
- /**
- * Returns the children elements of the given node
- *
- * @param node the parent node
- * @return a list of element children, never null
- */
- public static List<Element> getChildren(Node node) {
- NodeList childNodes = node.getChildNodes();
- List<Element> children = new ArrayList<Element>(childNodes.getLength());
- for (int i = 0, n = childNodes.getLength(); i < n; i++) {
- Node child = childNodes.item(i);
- if (child.getNodeType() == Node.ELEMENT_NODE) {
- children.add((Element) child);
- }
- }
-
- return children;
- }
-
- /**
- * Returns the <b>number</b> of children of the given node
- *
- * @param node the parent node
- * @return the count of element children
- */
- public static int getChildCount(Node node) {
- NodeList childNodes = node.getChildNodes();
- int childCount = 0;
- for (int i = 0, n = childNodes.getLength(); i < n; i++) {
- Node child = childNodes.item(i);
- if (child.getNodeType() == Node.ELEMENT_NODE) {
- childCount++;
- }
- }
-
- return childCount;
- }
}
/**
- * Returns a list of issues detected by this detector.
- *
- * @return a list of issues detected by this detector, never null.
- */
- public abstract Issue[] getIssues();
-
- /**
* Runs the detector
+ *
* @param context the context describing the work to be done
*/
public abstract void run(Context context);
@@ -232,7 +173,12 @@ public abstract class Detector {
/** Returns true if this detector applies to the given file */
public abstract boolean appliesTo(Context context, File file);
- /** Analysis is about to begin, perform any setup steps. */
+ /**
+ * Analysis is about to begin, perform any setup steps.
+ * <p>
+ * TODO: Rename "check" to "scan" here? beforeScanProject, beforeScanFile
+ * etc?
+ */
public void beforeCheckProject(Context context) {
}
@@ -260,15 +206,4 @@ public abstract class Detector {
* @return the expected speed of this detector
*/
public abstract Speed getSpeed();
-
- /** Namespace used in XML files for Android attributes */
- protected final static String ANDROID_URI =
- "http://schemas.android.com/apk/res/android"; //$NON-NLS-1$
-
- protected static final String CATEGORY_CORRECTNESS = "Correctness";
- protected static final String CATEGORY_PERFORMANCE = "Performance";
- protected static final String CATEGORY_USABILITY = "Usability";
- protected static final String CATEGORY_I18N = "Internationalization";
- protected static final String CATEGORY_A11Y = "Accessibility";
- protected static final String CATEGORY_SECURITY = "Security";
}
diff --git a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Issue.java b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Issue.java
index 3308bd8..5320362 100644
--- a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Issue.java
+++ b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Issue.java
@@ -16,8 +16,9 @@
package com.android.tools.lint.detector.api;
-import java.util.EnumSet;
+import com.android.tools.lint.client.api.Configuration;
+import java.util.EnumSet;
/**
@@ -36,16 +37,17 @@ public final class Issue implements Comparable<Issue> {
private final String mId;
private final String mDescription;
private final String mExplanation;
- private final String mCategory;
+ private final Category mCategory;
private final int mPriority;
private final Severity mSeverity;
private String mMoreInfoUrl;
private boolean mEnabledByDefault = true;
private final EnumSet<Scope> mScope;
+ private final Class<? extends Detector> mClass;
// Use factory methods
- private Issue(String id, String description, String explanation, String category, int priority,
- Severity severity, EnumSet<Scope> scope) {
+ private Issue(String id, String description, String explanation, Category category, int priority,
+ Severity severity, Class<? extends Detector> detectorClass, EnumSet<Scope> scope) {
super();
mId = id;
mDescription = description;
@@ -53,6 +55,7 @@ public final class Issue implements Comparable<Issue> {
mCategory = category;
mPriority = priority;
mSeverity = severity;
+ mClass = detectorClass;
mScope = scope;
}
@@ -67,12 +70,15 @@ public final class Issue implements Comparable<Issue> {
* @param priority the priority, a number from 1 to 10 with 10 being most
* important/severe
* @param severity the default severity of the issue
+ * @param detectorClass the class of the detector to find this issue
* @param scope the scope of files required to analyze this issue
* @return a new {@link Issue}
*/
- public static Issue create(String id, String description, String explanation, String category,
- int priority, Severity severity, EnumSet<Scope> scope) {
- return new Issue(id, description, explanation, category, priority, severity, scope);
+ public static Issue create(String id, String description, String explanation,
+ Category category, int priority, Severity severity,
+ Class<? extends Detector> detectorClass, EnumSet<Scope> scope) {
+ return new Issue(id, description, explanation, category, priority, severity,
+ detectorClass, scope);
}
/**
@@ -112,7 +118,7 @@ public final class Issue implements Comparable<Issue> {
*
* @return the category, or null if no category has been assigned
*/
- public String getCategory() {
+ public Category getCategory() {
return mCategory;
}
@@ -129,6 +135,15 @@ public final class Issue implements Comparable<Issue> {
/**
* Returns the default severity of the issues found by this detector (some
* tools may allow the user to specify custom severities for detectors).
+ * <p>
+ * Note that even though the normal way for an issue to be disabled is for
+ * the {@link Configuration} to return {@link Severity#IGNORE}, there is a
+ * {@link #isEnabledByDefault()} method which can be used to turn off issues
+ * by default. This is done rather than just having the severity as the only
+ * attribute on the issue such that an issue can be configured with an
+ * appropriate severity (such as {@link Severity#ERROR}) even when issues
+ * are disabled by default for example because they are experimental or not
+ * yet stable.
*
* @return the severity of the issues found by this detector
*/
@@ -199,4 +214,18 @@ public final class Issue implements Comparable<Issue> {
mEnabledByDefault = enabledByDefault;
return this;
}
+
+ /**
+ * Returns the class of the detector to use to find this issue
+ *
+ * @return the class of the detector to use to find this issue
+ */
+ public Class<? extends Detector> getDetectorClass() {
+ return mClass;
+ }
+
+ @Override
+ public String toString() {
+ return mId;
+ }
}
diff --git a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/LayoutDetector.java b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/LayoutDetector.java
index b2eaf10..c18c762 100644
--- a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/LayoutDetector.java
+++ b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/LayoutDetector.java
@@ -16,6 +16,17 @@
package com.android.tools.lint.detector.api;
+import static com.android.tools.lint.detector.api.LintConstants.ANDROID_URI;
+import static com.android.tools.lint.detector.api.LintConstants.ATTR_LAYOUT_HEIGHT;
+import static com.android.tools.lint.detector.api.LintConstants.ATTR_LAYOUT_WIDTH;
+import static com.android.tools.lint.detector.api.LintConstants.ATTR_PADDING;
+import static com.android.tools.lint.detector.api.LintConstants.ATTR_PADDING_BOTTOM;
+import static com.android.tools.lint.detector.api.LintConstants.ATTR_PADDING_LEFT;
+import static com.android.tools.lint.detector.api.LintConstants.ATTR_PADDING_RIGHT;
+import static com.android.tools.lint.detector.api.LintConstants.ATTR_PADDING_TOP;
+import static com.android.tools.lint.detector.api.LintConstants.VALUE_FILL_PARENT;
+import static com.android.tools.lint.detector.api.LintConstants.VALUE_MATCH_PARENT;
+
import com.android.resources.ResourceFolderType;
import org.w3c.dom.Element;
@@ -28,57 +39,6 @@ import org.w3c.dom.Element;
* to adjust your code for the next tools release.</b>
*/
public abstract class LayoutDetector extends ResourceXmlDetector {
- // Layouts
- protected static final String FRAME_LAYOUT = "FrameLayout"; //$NON-NLS-1$
- protected static final String LINEAR_LAYOUT = "LinearLayout"; //$NON-NLS-1$
- protected static final String SCROLL_VIEW = "ScrollView"; //$NON-NLS-1$
- protected static final String GALLERY = "Gallery"; //$NON-NLS-1$
- protected static final String GRID_VIEW = "GridView"; //$NON-NLS-1$
- protected static final String EDIT_TEXT = "EditText"; //$NON-NLS-1$
- protected static final String LIST_VIEW = "ListView"; //$NON-NLS-1$
- protected static final String TEXT_VIEW = "TextView"; //$NON-NLS-1$
- protected static final String IMAGE_VIEW = "ImageView"; //$NON-NLS-1$
- protected static final String IMAGE_BUTTON = "ImageButton"; //$NON-NLS-1$
- protected static final String INCLUDE = "include"; //$NON-NLS-1$
- protected static final String MERGE = "merge"; //$NON-NLS-1$
- protected static final String HORIZONTAL_SCROLL_VIEW = "HorizontalScrollView"; //$NON-NLS-1$
-
- // Attributes
- protected static final String ATTR_ID = "id"; //$NON-NLS-1$
- protected static final String ATTR_TEXT = "text"; //$NON-NLS-1$
- protected static final String ATTR_LABEL = "label"; //$NON-NLS-1$
- protected static final String ATTR_HINT = "hint"; //$NON-NLS-1$
- protected static final String ATTR_PROMPT = "prompt"; //$NON-NLS-1$
- protected static final String ATTR_INPUT_TYPE = "inputType"; //$NON-NLS-1$
- protected static final String ATTR_INPUT_METHOD = "inputMethod"; //$NON-NLS-1$
- protected static final String ATTR_LAYOUT_GRAVITY = "layout_gravity"; //$NON-NLS-1$
- protected static final String ATTR_LAYOUT_WIDTH = "layout_width"; //$NON-NLS-1$
- protected static final String ATTR_LAYOUT_HEIGHT = "layout_height"; //$NON-NLS-1$
- protected static final String ATTR_LAYOUT_WEIGHT = "layout_weight"; //$NON-NLS-1$
- protected static final String ATTR_PADDING = "padding"; //$NON-NLS-1$
- protected static final String ATTR_PADDING_BOTTOM = "paddingBottom"; //$NON-NLS-1$
- protected static final String ATTR_PADDING_TOP = "paddingTop"; //$NON-NLS-1$
- protected static final String ATTR_PADDING_RIGHT = "paddingRight"; //$NON-NLS-1$
- protected static final String ATTR_PADDING_LEFT = "paddingLeft"; //$NON-NLS-1$
- protected static final String ATTR_FOREGROUND = "foreground"; //$NON-NLS-1$
- protected static final String ATTR_BACKGROUND = "background"; //$NON-NLS-1$
- protected static final String ATTR_ORIENTATION = "orientation"; //$NON-NLS-1$
- protected static final String ATTR_LAYOUT = "layout"; //$NON-NLS-1$
- protected static final String ATTR_LAYOUT_ROW = "layout_row"; //$NON-NLS-1$
- protected static final String ATTR_LAYOUT_ROW_SPAN = "layout_rowSpan";//$NON-NLS-1$
- protected static final String ATTR_LAYOUT_COLUMN = "layout_column"; //$NON-NLS-1$
- protected static final String ATTR_ROW_COUNT = "rowCount"; //$NON-NLS-1$
- protected static final String ATTR_COLUMN_COUNT = "columnCount"; //$NON-NLS-1$
- protected static final String ATTR_LAYOUT_COLUMN_SPAN = "layout_columnSpan"; //$NON-NLS-1$
- protected static final String ATTR_CONTENT_DESCRIPTION = "contentDescription"; //$NON-NLS-1$
-
-
- // Attribute values
- protected static final String VALUE_FILL_PARENT = "fill_parent"; //$NON-NLS-1$
- protected static final String VALUE_MATCH_PARENT = "match_parent"; //$NON-NLS-1$
- protected static final String VALUE_VERTICAL = "vertical"; //$NON-NLS-1$
- protected static final String VALUE_LAYOUT_PREFIX = "@layout/"; //$NON-NLS-1$
-
@Override
public boolean appliesTo(ResourceFolderType folderType) {
return folderType == ResourceFolderType.LAYOUT;
diff --git a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/LintConstants.java b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/LintConstants.java
new file mode 100644
index 0000000..b0ff394
--- /dev/null
+++ b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/LintConstants.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.lint.detector.api;
+
+/**
+ * Constants used by the various detectors, defined in one place
+ */
+@SuppressWarnings("javadoc") // Not documenting each and every obvious constant
+public class LintConstants {
+ /** Namespace used in XML files for Android attributes */
+ public static final String ANDROID_URI =
+ "http://schemas.android.com/apk/res/android"; //$NON-NLS-1$
+
+ // Tags: Manifest
+ public static final String TAG_SERVICE = "service"; //$NON-NLS-1$
+ public static final String TAG_USES_PERMISSION = "uses-permission";//$NON-NLS-1$
+ public static final String TAG_APPLICATION = "application"; //$NON-NLS-1$
+ public static final String TAG_INTENT_FILTER = " intent-filter"; //$NON-NLS-1$
+ public static final String TAG_USES_SDK = "uses-sdk"; //$NON-NLS-1$
+
+ // Tags: Layouts
+ public static final String FRAME_LAYOUT = "FrameLayout"; //$NON-NLS-1$
+ public static final String LINEAR_LAYOUT = "LinearLayout"; //$NON-NLS-1$
+ public static final String SCROLL_VIEW = "ScrollView"; //$NON-NLS-1$
+ public static final String GALLERY = "Gallery"; //$NON-NLS-1$
+ public static final String GRID_VIEW = "GridView"; //$NON-NLS-1$
+ public static final String EDIT_TEXT = "EditText"; //$NON-NLS-1$
+ public static final String LIST_VIEW = "ListView"; //$NON-NLS-1$
+ public static final String TEXT_VIEW = "TextView"; //$NON-NLS-1$
+ public static final String IMAGE_VIEW = "ImageView"; //$NON-NLS-1$
+ public static final String IMAGE_BUTTON = "ImageButton"; //$NON-NLS-1$
+ public static final String INCLUDE = "include"; //$NON-NLS-1$
+ public static final String MERGE = "merge"; //$NON-NLS-1$
+ public static final String HORIZONTAL_SCROLL_VIEW = "HorizontalScrollView"; //$NON-NLS-1$
+
+ // Attributes: Manifest
+ public static final String ATTR_EXPORTED = "exported"; //$NON-NLS-1$
+ public static final String ATTR_PERMISSION = "permission"; //$NON-NLS-1$
+ public static final String ATTR_MIN_SDK_VERSION = "minSdkVersion"; //$NON-NLS-1$
+ public static final String ATTR_TARGET_SDK_VERSION = "targetSdkVersion"; //$NON-NLS-1$
+ public static final String ATTR_ICON = "icon"; //$NON-NLS-1$
+
+ // Attributes: Layout
+ public static final String ATTR_ID = "id"; //$NON-NLS-1$
+ public static final String ATTR_TEXT = "text"; //$NON-NLS-1$
+ public static final String ATTR_LABEL = "label"; //$NON-NLS-1$
+ public static final String ATTR_HINT = "hint"; //$NON-NLS-1$
+ public static final String ATTR_PROMPT = "prompt"; //$NON-NLS-1$
+ public static final String ATTR_INPUT_TYPE = "inputType"; //$NON-NLS-1$
+ public static final String ATTR_INPUT_METHOD = "inputMethod"; //$NON-NLS-1$
+ public static final String ATTR_LAYOUT_GRAVITY = "layout_gravity"; //$NON-NLS-1$
+ public static final String ATTR_LAYOUT_WIDTH = "layout_width"; //$NON-NLS-1$
+ public static final String ATTR_LAYOUT_HEIGHT = "layout_height"; //$NON-NLS-1$
+ public static final String ATTR_LAYOUT_WEIGHT = "layout_weight"; //$NON-NLS-1$
+ public static final String ATTR_PADDING = "padding"; //$NON-NLS-1$
+ public static final String ATTR_PADDING_BOTTOM = "paddingBottom"; //$NON-NLS-1$
+ public static final String ATTR_PADDING_TOP = "paddingTop"; //$NON-NLS-1$
+ public static final String ATTR_PADDING_RIGHT = "paddingRight"; //$NON-NLS-1$
+ public static final String ATTR_PADDING_LEFT = "paddingLeft"; //$NON-NLS-1$
+ public static final String ATTR_FOREGROUND = "foreground"; //$NON-NLS-1$
+ public static final String ATTR_BACKGROUND = "background"; //$NON-NLS-1$
+ public static final String ATTR_ORIENTATION = "orientation"; //$NON-NLS-1$
+ public static final String ATTR_LAYOUT = "layout"; //$NON-NLS-1$
+ public static final String ATTR_LAYOUT_ROW = "layout_row"; //$NON-NLS-1$
+ public static final String ATTR_LAYOUT_ROW_SPAN = "layout_rowSpan";//$NON-NLS-1$
+ public static final String ATTR_LAYOUT_COLUMN = "layout_column"; //$NON-NLS-1$
+ public static final String ATTR_ROW_COUNT = "rowCount"; //$NON-NLS-1$
+ public static final String ATTR_COLUMN_COUNT = "columnCount"; //$NON-NLS-1$
+ public static final String ATTR_LAYOUT_COLUMN_SPAN = "layout_columnSpan"; //$NON-NLS-1$
+ public static final String ATTR_CONTENT_DESCRIPTION = "contentDescription"; //$NON-NLS-1$
+
+ // Attribute values
+ public static final String VALUE_FILL_PARENT = "fill_parent"; //$NON-NLS-1$
+ public static final String VALUE_MATCH_PARENT = "match_parent"; //$NON-NLS-1$
+ public static final String VALUE_VERTICAL = "vertical"; //$NON-NLS-1$
+
+ // Filenames and folder names
+ public static final String ANDROID_MANIFEST_XML = "AndroidManifest.xml"; //$NON-NLS-1$
+ public static final String RES_FOLDER = "res"; //$NON-NLS-1$
+ public static final String DOT_XML = ".xml"; //$NON-NLS-1$
+ public static final String DOT_GIF = ".gif"; //$NON-NLS-1$
+ public static final String DOT_JPG = ".jpg"; //$NON-NLS-1$
+ public static final String DOT_PNG = ".png"; //$NON-NLS-1$
+ public static final String DOT_9PNG = ".9.png"; //$NON-NLS-1$
+ public static final String DRAWABLE_FOLDER = "drawable"; //$NON-NLS-1$
+ public static final String DRAWABLE_XHDPI = "drawable-xhdpi"; //$NON-NLS-1$
+ public static final String DRAWABLE_HDPI = "drawable-hdpi"; //$NON-NLS-1$
+ public static final String DRAWABLE_MDPI = "drawable-mdpi"; //$NON-NLS-1$
+ public static final String DRAWABLE_LDPI = "drawable-ldpi"; //$NON-NLS-1$
+
+ // Resources
+ public static final String DRAWABLE_RESOURCE_PREFIX = "@drawable/";//$NON-NLS-1$
+ public static final String VALUE_LAYOUT_PREFIX = "@layout/"; //$NON-NLS-1$
+}
diff --git a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/LintUtils.java b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/LintUtils.java
new file mode 100644
index 0000000..4bc8503
--- /dev/null
+++ b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/LintUtils.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.lint.detector.api;
+
+import static com.android.tools.lint.detector.api.LintConstants.DOT_XML;
+
+import com.android.resources.FolderTypeRelationship;
+import com.android.resources.ResourceFolderType;
+import com.android.resources.ResourceType;
+
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+
+/**
+ * Useful utility methods related to lint.
+ */
+public class LintUtils {
+ /**
+ * Format a list of strings, and cut of the list at {@code maxItems} if the
+ * number of items are greater.
+ *
+ * @param strings the list of strings to print out as a comma separated list
+ * @param maxItems the maximum number of items to print
+ * @return a comma separated list
+ */
+ public static String formatList(List<String> strings, int maxItems) {
+ StringBuilder sb = new StringBuilder(20 * strings.size());
+
+ for (int i = 0, n = strings.size(); i < n; i++) {
+ if (sb.length() > 0) {
+ sb.append(", "); //$NON-NLS-1$
+ }
+ sb.append(strings.get(i));
+
+ if (i == maxItems - 1 && n > maxItems) {
+ sb.append(String.format("... (%1$d more)", n - i - 1));
+ break;
+ }
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * Computes the set difference {@code a - b}
+ *
+ * @param a the set to subtract from
+ * @param b the set to subtract
+ * @return the elements that are in {@code a} but not in {@code b}
+ */
+ public static Set<String> difference(Set<String> a, Set<String> b) {
+ HashSet<String> copy = new HashSet<String>(a);
+ copy.removeAll(b);
+ return copy;
+ }
+
+ /**
+ * Determine if the given type corresponds to a resource that has a unique
+ * file
+ *
+ * @param type the resource type to check
+ * @return true if the given type corresponds to a file-type resource
+ */
+ public static boolean isFileBasedResourceType(ResourceType type) {
+ List<ResourceFolderType> folderTypes = FolderTypeRelationship.getRelatedFolders(type);
+ for (ResourceFolderType folderType : folderTypes) {
+ if (folderType != ResourceFolderType.VALUES) {
+ if (type == ResourceType.ID) {
+ return false;
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns true if the given file represents an XML file
+ *
+ * @param file the file to be checked
+ * @return true if the given file is an xml file
+ */
+ public static boolean isXmlFile(File file) {
+ String string = file.getName();
+ return string.regionMatches(true, string.length() - DOT_XML.length(),
+ DOT_XML, 0, DOT_XML.length());
+ }
+
+ /**
+ * Returns the children elements of the given node
+ *
+ * @param node the parent node
+ * @return a list of element children, never null
+ */
+ public static List<Element> getChildren(Node node) {
+ NodeList childNodes = node.getChildNodes();
+ List<Element> children = new ArrayList<Element>(childNodes.getLength());
+ for (int i = 0, n = childNodes.getLength(); i < n; i++) {
+ Node child = childNodes.item(i);
+ if (child.getNodeType() == Node.ELEMENT_NODE) {
+ children.add((Element) child);
+ }
+ }
+
+ return children;
+ }
+
+ /**
+ * Returns the <b>number</b> of children of the given node
+ *
+ * @param node the parent node
+ * @return the count of element children
+ */
+ public static int getChildCount(Node node) {
+ NodeList childNodes = node.getChildNodes();
+ int childCount = 0;
+ for (int i = 0, n = childNodes.getLength(); i < n; i++) {
+ Node child = childNodes.item(i);
+ if (child.getNodeType() == Node.ELEMENT_NODE) {
+ childCount++;
+ }
+ }
+
+ return childCount;
+ }
+}
diff --git a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Project.java b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Project.java
index f09d4a9..2011e86 100644
--- a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Project.java
+++ b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Project.java
@@ -16,7 +16,8 @@
package com.android.tools.lint.detector.api;
-import com.android.tools.lint.api.ToolContext;
+import com.android.tools.lint.client.api.Configuration;
+import com.android.tools.lint.client.api.LintClient;
import java.io.File;
import java.util.ArrayList;
@@ -31,9 +32,11 @@ import java.util.List;
*/
public class Project {
/** The associated tool */
- private final ToolContext mTool;
- private final File dir;
- private final File referenceDir;
+ private final LintClient mTool;
+ private final File mDir;
+ private final File mReferenceDir;
+ private Configuration mConfiguration;
+
/**
* If non null, specifies a non-empty list of specific files under this
* project which should be checked.
@@ -49,22 +52,22 @@ public class Project {
* @param dir the root directory of the project
* @param referenceDir See {@link #getReferenceDir()}.
*/
- public Project(ToolContext tool, File dir, File referenceDir) {
- this.mTool = tool;
- this.dir = dir;
- this.referenceDir = referenceDir;
+ public Project(LintClient tool, File dir, File referenceDir) {
+ mTool = tool;
+ mDir = dir;
+ mReferenceDir = referenceDir;
}
@Override
public String toString() {
- return "Project [dir=" + dir + "]";
+ return "Project [dir=" + mDir + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
- result = prime * result + ((dir == null) ? 0 : dir.hashCode());
+ result = prime * result + ((mDir == null) ? 0 : mDir.hashCode());
return result;
}
@@ -77,10 +80,10 @@ public class Project {
if (getClass() != obj.getClass())
return false;
Project other = (Project) obj;
- if (dir == null) {
- if (other.dir != null)
+ if (mDir == null) {
+ if (other.mDir != null)
return false;
- } else if (!dir.equals(other.dir))
+ } else if (!mDir.equals(other.mDir))
return false;
return true;
}
@@ -138,11 +141,32 @@ public class Project {
* directory when a directory tree is being scanned
*
* @param file the file under this project to check
- * @return the relative path
+ * @return the path relative to the reference directory (often the project directory)
+ */
+ public String getDisplayPath(File file) {
+ String path = file.getPath();
+ String referencePath = mReferenceDir.getPath();
+ if (path.startsWith(referencePath)) {
+ int length = referencePath.length();
+ if (path.length() > length && path.charAt(length) == File.separatorChar) {
+ length++;
+ }
+
+ return path.substring(length);
+ }
+
+ return path;
+ }
+
+ /**
+ * Returns the relative path of a given file within the current project.
+ *
+ * @param file the file under this project to check
+ * @return the path relative to the project
*/
public String getRelativePath(File file) {
String path = file.getPath();
- String referencePath = referenceDir.getPath();
+ String referencePath = mDir.getPath();
if (path.startsWith(referencePath)) {
int length = referencePath.length();
if (path.length() > length && path.charAt(length) == File.separatorChar) {
@@ -161,7 +185,7 @@ public class Project {
* @return the dir
*/
public File getDir() {
- return dir;
+ return mDir;
}
/**
@@ -174,6 +198,24 @@ public class Project {
* @return the reference directory, never null
*/
public File getReferenceDir() {
- return referenceDir;
+ return mReferenceDir;
+ }
+
+ /**
+ * Gets the configuration associated with this project
+ *
+ * @return the configuration associated with this project
+ */
+ public Configuration getConfiguration() {
+ return mConfiguration;
+ }
+
+ /**
+ * Sets the configuration associated with this project
+ *
+ * @param configuration sets the configuration associated with this project
+ */
+ public void setConfiguration(Configuration configuration) {
+ mConfiguration = configuration;
}
}
diff --git a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/ResourceXmlDetector.java b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/ResourceXmlDetector.java
index 34ceaf2..828f946 100644
--- a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/ResourceXmlDetector.java
+++ b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/ResourceXmlDetector.java
@@ -31,7 +31,7 @@ import java.io.File;
public abstract class ResourceXmlDetector extends Detector.XmlDetectorAdapter {
@Override
public boolean appliesTo(Context context, File file) {
- return isXmlFile(file);
+ return LintUtils.isXmlFile(file);
}
/**
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/AccessibilityDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/AccessibilityDetector.java
index 8ee4cd9..055d8bb 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/AccessibilityDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/AccessibilityDetector.java
@@ -16,6 +16,12 @@
package com.android.tools.lint.checks;
+import static com.android.tools.lint.detector.api.LintConstants.ANDROID_URI;
+import static com.android.tools.lint.detector.api.LintConstants.ATTR_CONTENT_DESCRIPTION;
+import static com.android.tools.lint.detector.api.LintConstants.IMAGE_BUTTON;
+import static com.android.tools.lint.detector.api.LintConstants.IMAGE_VIEW;
+
+import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.LayoutDetector;
@@ -44,18 +50,17 @@ public class AccessibilityDetector extends LayoutDetector {
"contentDescription attribute to specify a textual description of " +
"the widget such that screen readers and other accessibility tools " +
"can adequately describe the user interface.",
- CATEGORY_A11Y, 5, Severity.WARNING, Scope.RESOURCE_FILE_SCOPE);
+ Category.A11Y,
+ 3,
+ Severity.WARNING,
+ AccessibilityDetector.class,
+ Scope.RESOURCE_FILE_SCOPE);
/** Constructs a new accessibility check */
public AccessibilityDetector() {
}
@Override
- public Issue[] getIssues() {
- return new Issue[] { ISSUE };
- }
-
- @Override
public Speed getSpeed() {
return Speed.FAST;
}
@@ -71,12 +76,12 @@ public class AccessibilityDetector extends LayoutDetector {
@Override
public void visitElement(Context context, Element element) {
if (!element.hasAttributeNS(ANDROID_URI, ATTR_CONTENT_DESCRIPTION)) {
- context.toolContext.report(context, ISSUE, context.getLocation(context),
+ context.client.report(context, ISSUE, context.getLocation(context),
"[Accessibility] Missing contentDescription attribute on image", null);
} else {
String attribute = element.getAttributeNS(ANDROID_URI, ATTR_CONTENT_DESCRIPTION);
if (attribute.length() == 0 || attribute.equals("TODO")) { //$NON-NLS-1$
- context.toolContext.report(context, ISSUE, context.getLocation(context),
+ context.client.report(context, ISSUE, context.getLocation(context),
"[Accessibility] Empty contentDescription attribute on image", null);
}
}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/ArraySizeDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/ArraySizeDetector.java
index 875e63b..1ecc159 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/ArraySizeDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/ArraySizeDetector.java
@@ -22,8 +22,10 @@ import static com.android.tools.lint.checks.TranslationDetector.TAG_INTEGER_ARRA
import static com.android.tools.lint.checks.TranslationDetector.TAG_STRING_ARRAY;
import com.android.resources.ResourceFolderType;
+import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Issue;
+import com.android.tools.lint.detector.api.LintUtils;
import com.android.tools.lint.detector.api.Location;
import com.android.tools.lint.detector.api.ResourceXmlDetector;
import com.android.tools.lint.detector.api.Scope;
@@ -66,7 +68,11 @@ public class ArraySizeDetector extends ResourceXmlDetector {
"decide if this is really an error.\n" +
"\n" +
"You can suppress this error type if it finds false errors in your project.",
- CATEGORY_CORRECTNESS, 7, Severity.WARNING, Scope.ALL_RESOURCES_SCOPE);
+ Category.CORRECTNESS,
+ 7,
+ Severity.WARNING,
+ ArraySizeDetector.class,
+ Scope.ALL_RESOURCES_SCOPE);
private Map<File, Pair<String, Integer>> mFileToArrayCount;
@@ -80,11 +86,6 @@ public class ArraySizeDetector extends ResourceXmlDetector {
}
@Override
- public Issue[] getIssues() {
- return new Issue[] { INCONSISTENT };
- }
-
- @Override
public Speed getSpeed() {
return Speed.NORMAL;
}
@@ -134,7 +135,7 @@ public class ArraySizeDetector extends ResourceXmlDetector {
File otherFile = fileMap.get(name);
String otherName = otherFile.getParentFile().getName() + File.separator
+ otherFile.getName();
- context.toolContext.report(context, INCONSISTENT, location,
+ context.client.report(context, INCONSISTENT, location,
String.format(
"Array %1$s has an inconsistent number of items (%2$d in %3$s, %4$d in %5$s)",
name, count, thisName, current, otherName), null);
@@ -149,12 +150,12 @@ public class ArraySizeDetector extends ResourceXmlDetector {
public void visitElement(Context context, Element element) {
Attr attribute = element.getAttributeNode(ATTR_NAME);
if (attribute == null || attribute.getValue().length() == 0) {
- context.toolContext.report(context, INCONSISTENT, context.getLocation(element),
+ context.client.report(context, INCONSISTENT, context.getLocation(element),
String.format("Missing name attribute in %1$s declaration", element.getTagName()),
null);
} else {
String name = attribute.getValue();
- int childCount = getChildCount(element);
+ int childCount = LintUtils.getChildCount(element);
mFileToArrayCount.put(context.file, Pair.of(name, childCount));
}
}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/BuiltinDetectorRegistry.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/BuiltinDetectorRegistry.java
deleted file mode 100644
index f807319..0000000
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/BuiltinDetectorRegistry.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.tools.lint.checks;
-
-import com.android.tools.lint.detector.api.Detector;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/** Registry which provides a list of checks to be performed on an Android project */
-public class BuiltinDetectorRegistry extends com.android.tools.lint.api.DetectorRegistry {
- private static final List<Detector> sDetectors;
- static {
- // TODO: Maybe just store class names here instead such that
- // each invocation can have its own context with fields without
- // worrying about having to clear state in beforeCheckProject or beforeCheckFile?
- List<Detector> detectors = new ArrayList<Detector>();
- detectors.add(new AccessibilityDetector());
- detectors.add(new DuplicateIdDetector());
- detectors.add(new StateListDetector());
- detectors.add(new InefficientWeightDetector());
- detectors.add(new ScrollViewChildDetector());
- detectors.add(new MergeRootFrameLayoutDetector());
- detectors.add(new NestedScrollingWidgetDetector());
- detectors.add(new ChildCountDetector());
- detectors.add(new UseCompoundDrawableDetector());
- detectors.add(new UselessViewDetector());
- detectors.add(new TooManyViewsDetector());
- detectors.add(new GridLayoutDetector());
- detectors.add(new TranslationDetector());
- detectors.add(new HardcodedValuesDetector());
- detectors.add(new ProguardDetector());
- detectors.add(new PxUsageDetector());
- detectors.add(new TextFieldDetector());
- detectors.add(new UnusedResourceDetector());
- detectors.add(new ArraySizeDetector());
- detectors.add(new ManifestOrderDetector());
- detectors.add(new ExportedServiceDetector());
-
- // TODO: Populate dynamically somehow?
-
- sDetectors = Collections.unmodifiableList(detectors);
- }
-
- /**
- * Constructs a new {@link BuiltinDetectorRegistry}
- */
- public BuiltinDetectorRegistry() {
- }
-
- @Override
- public List<? extends Detector> getDetectors() {
- return sDetectors;
- }
-}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/BuiltinIssueRegistry.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/BuiltinIssueRegistry.java
new file mode 100644
index 0000000..5e63caf
--- /dev/null
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/BuiltinIssueRegistry.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.lint.checks;
+
+import com.android.tools.lint.client.api.IssueRegistry;
+import com.android.tools.lint.detector.api.Issue;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/** Registry which provides a list of checks to be performed on an Android project */
+public class BuiltinIssueRegistry extends IssueRegistry {
+ private static final List<Issue> sIssues;
+
+ static {
+ List<Issue> issues = new ArrayList<Issue>();
+
+ issues.add(AccessibilityDetector.ISSUE);
+ issues.add(DuplicateIdDetector.CROSS_LAYOUT);
+ issues.add(DuplicateIdDetector.WITHIN_LAYOUT);
+ issues.add(StateListDetector.ISSUE);
+ issues.add(InefficientWeightDetector.ISSUE);
+ issues.add(ScrollViewChildDetector.ISSUE);
+ issues.add(MergeRootFrameLayoutDetector.ISSUE);
+ issues.add(NestedScrollingWidgetDetector.ISSUE);
+ issues.add(ChildCountDetector.SCROLLVIEW_ISSUE);
+ issues.add(ChildCountDetector.ADAPTERVIEW_ISSUE);
+ issues.add(UseCompoundDrawableDetector.ISSUE);
+ issues.add(UselessViewDetector.USELESS_PARENT);
+ issues.add(UselessViewDetector.USELESS_LEAF);
+ issues.add(TooManyViewsDetector.TOO_MANY);
+ issues.add(TooManyViewsDetector.TOO_DEEP);
+ issues.add(GridLayoutDetector.ISSUE);
+ issues.add(TranslationDetector.EXTRA);
+ issues.add(TranslationDetector.MISSING);
+ issues.add(HardcodedValuesDetector.ISSUE);
+ issues.add(ProguardDetector.ISSUE);
+ issues.add(PxUsageDetector.ISSUE);
+ issues.add(TextFieldDetector.ISSUE);
+ issues.add(UnusedResourceDetector.ISSUE);
+ issues.add(UnusedResourceDetector.ISSUE_IDS);
+ issues.add(ArraySizeDetector.INCONSISTENT);
+ issues.add(ManifestOrderDetector.ISSUE);
+ issues.add(ExportedServiceDetector.ISSUE);
+
+ // TODO: Populate dynamically somehow?
+
+ sIssues = Collections.unmodifiableList(issues);
+
+ // Check that ids are unique
+ boolean assertionsEnabled = false;
+ assert assertionsEnabled = true; // Intentional side-effect
+ if (assertionsEnabled) {
+ Set<String> ids = new HashSet<String>();
+ for (Issue issue : sIssues) {
+ String id = issue.getId();
+ assert !ids.contains(id) : "Duplicate id " + id; //$NON-NLS-1$
+ ids.add(id);
+ }
+ }
+ }
+
+ /**
+ * Constructs a new {@link BuiltinIssueRegistry}
+ */
+ public BuiltinIssueRegistry() {
+ }
+
+ @Override
+ public List<Issue> getIssues() {
+ return sIssues;
+ }
+}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/ChildCountDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/ChildCountDetector.java
index 23e00a6..4b2cea5 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/ChildCountDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/ChildCountDetector.java
@@ -16,9 +16,16 @@
package com.android.tools.lint.checks;
+import static com.android.tools.lint.detector.api.LintConstants.GRID_VIEW;
+import static com.android.tools.lint.detector.api.LintConstants.HORIZONTAL_SCROLL_VIEW;
+import static com.android.tools.lint.detector.api.LintConstants.LIST_VIEW;
+import static com.android.tools.lint.detector.api.LintConstants.SCROLL_VIEW;
+
+import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.LayoutDetector;
+import com.android.tools.lint.detector.api.LintUtils;
import com.android.tools.lint.detector.api.Scope;
import com.android.tools.lint.detector.api.Severity;
import com.android.tools.lint.detector.api.Speed;
@@ -40,7 +47,11 @@ public class ChildCountDetector extends LayoutDetector {
"Checks that ScrollViews have exactly one child widget",
"ScrollViews can only have one child widget. If you want more children, wrap them " +
"in a container layout.",
- CATEGORY_CORRECTNESS, 8, Severity.WARNING, Scope.RESOURCE_FILE_SCOPE);
+ Category.CORRECTNESS,
+ 8,
+ Severity.WARNING,
+ ChildCountDetector.class,
+ Scope.RESOURCE_FILE_SCOPE);
/** The main issue discovered by this detector */
public static final Issue ADAPTERVIEW_ISSUE = Issue.create(
@@ -48,7 +59,11 @@ public class ChildCountDetector extends LayoutDetector {
"Checks that AdapterViews do not define their children in XML",
"AdapterViews such as ListViews must be configured with data from Java code, " +
"such as a ListAdapter.",
- CATEGORY_CORRECTNESS, 10, Severity.WARNING, Scope.RESOURCE_FILE_SCOPE).setMoreInfo(
+ Category.CORRECTNESS,
+ 10,
+ Severity.WARNING,
+ ChildCountDetector.class,
+ Scope.RESOURCE_FILE_SCOPE).setMoreInfo(
"http://developer.android.com/reference/android/widget/AdapterView.html"); //$NON-NLS-1$
/** Constructs a new {@link ChildCountDetector} */
@@ -56,11 +71,6 @@ public class ChildCountDetector extends LayoutDetector {
}
@Override
- public Issue[] getIssues() {
- return new Issue[] { SCROLLVIEW_ISSUE, ADAPTERVIEW_ISSUE };
- }
-
- @Override
public Speed getSpeed() {
return Speed.FAST;
}
@@ -78,18 +88,18 @@ public class ChildCountDetector extends LayoutDetector {
@Override
public void visitElement(Context context, Element element) {
- int childCount = getChildCount(element);
+ int childCount = LintUtils.getChildCount(element);
String tagName = element.getTagName();
if (tagName.equals(SCROLL_VIEW) || tagName.equals(HORIZONTAL_SCROLL_VIEW)) {
if (childCount > 1) {
- context.toolContext.report(context, SCROLLVIEW_ISSUE,
+ context.client.report(context, SCROLLVIEW_ISSUE,
context.getLocation(element), "A scroll view can have only one child",
null);
}
} else {
// Adapter view
if (childCount > 0) {
- context.toolContext.report(context, ADAPTERVIEW_ISSUE,
+ context.client.report(context, ADAPTERVIEW_ISSUE,
context.getLocation(element),
"A list/grid should have no children declared in XML", null);
}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/DuplicateIdDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/DuplicateIdDetector.java
index f31840e..2e7aadd 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/DuplicateIdDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/DuplicateIdDetector.java
@@ -16,7 +16,13 @@
package com.android.tools.lint.checks;
+import static com.android.tools.lint.detector.api.LintConstants.ATTR_ID;
+import static com.android.tools.lint.detector.api.LintConstants.ATTR_LAYOUT;
+import static com.android.tools.lint.detector.api.LintConstants.INCLUDE;
+import static com.android.tools.lint.detector.api.LintConstants.VALUE_LAYOUT_PREFIX;
+
import com.android.resources.ResourceFolderType;
+import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.LayoutDetector;
@@ -53,7 +59,11 @@ public class DuplicateIdDetector extends LayoutDetector {
"Checks for duplicate ids within a single layout",
"Within a layout, id's should be unique since otherwise findViewById() can " +
"return an unexpected view.",
- CATEGORY_CORRECTNESS, 7, Severity.WARNING, Scope.RESOURCE_FILE_SCOPE);
+ Category.CORRECTNESS,
+ 7,
+ Severity.WARNING,
+ DuplicateIdDetector.class,
+ Scope.RESOURCE_FILE_SCOPE);
/** The main issue discovered by this detector */
public static final Issue CROSS_LAYOUT = Issue.create(
@@ -63,7 +73,11 @@ public class DuplicateIdDetector extends LayoutDetector {
"layouts are combined with include tags, then the id's need to be unique " +
"within any chain of included layouts, or Activity#findViewById() can " +
"return an unexpected view.",
- CATEGORY_CORRECTNESS, 6, Severity.WARNING, Scope.ALL_RESOURCES_SCOPE);
+ Category.CORRECTNESS,
+ 6,
+ Severity.WARNING,
+ DuplicateIdDetector.class,
+ Scope.ALL_RESOURCES_SCOPE);
/** Constructs a duplicate id check */
public DuplicateIdDetector() {
@@ -71,11 +85,6 @@ public class DuplicateIdDetector extends LayoutDetector {
@Override
- public Issue[] getIssues() {
- return new Issue[] { WITHIN_LAYOUT, CROSS_LAYOUT };
- }
-
- @Override
public boolean appliesTo(ResourceFolderType folderType) {
return folderType == ResourceFolderType.LAYOUT || folderType == ResourceFolderType.MENU;
}
@@ -147,7 +156,7 @@ public class DuplicateIdDetector extends LayoutDetector {
}
private void checkForIncludeDuplicates(Context context) {
- if (!context.toolContext.isEnabled(CROSS_LAYOUT) ||
+ if (!context.configuration.isEnabled(CROSS_LAYOUT) ||
!context.scope.contains(Scope.ALL_RESOURCE_FILES)) {
return;
}
@@ -342,7 +351,7 @@ public class DuplicateIdDetector extends LayoutDetector {
// Also record the secondary location
location.setSecondary(new Location(second, null, null));
}
- context.toolContext.report(context, CROSS_LAYOUT, location, msg, null);
+ context.client.report(context, CROSS_LAYOUT, location, msg, null);
}
/**
@@ -390,7 +399,7 @@ public class DuplicateIdDetector extends LayoutDetector {
assert attribute.getLocalName().equals(ATTR_ID);
String id = attribute.getValue();
if (mIds.contains(id)) {
- context.toolContext.report(context, WITHIN_LAYOUT, context.getLocation(attribute),
+ context.client.report(context, WITHIN_LAYOUT, context.getLocation(attribute),
String.format("Duplicate id %1$s, already defined earlier in this layout",
id), null);
} else if (id.startsWith("@+id/")) { //$NON-NLS-1$
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/ExportedServiceDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/ExportedServiceDetector.java
index e85be6e..437256c 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/ExportedServiceDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/ExportedServiceDetector.java
@@ -16,16 +16,19 @@
package com.android.tools.lint.checks;
-import static com.android.tools.lint.checks.LintConstants.ANDROID_MANIFEST_XML;
-import static com.android.tools.lint.checks.LintConstants.ATTR_EXPORTED;
-import static com.android.tools.lint.checks.LintConstants.ATTR_PERMISSION;
-import static com.android.tools.lint.checks.LintConstants.TAG_APPLICATION;
-import static com.android.tools.lint.checks.LintConstants.TAG_INTENT_FILTER;
-import static com.android.tools.lint.checks.LintConstants.TAG_SERVICE;
+import static com.android.tools.lint.detector.api.LintConstants.ANDROID_MANIFEST_XML;
+import static com.android.tools.lint.detector.api.LintConstants.ANDROID_URI;
+import static com.android.tools.lint.detector.api.LintConstants.ATTR_EXPORTED;
+import static com.android.tools.lint.detector.api.LintConstants.ATTR_PERMISSION;
+import static com.android.tools.lint.detector.api.LintConstants.TAG_APPLICATION;
+import static com.android.tools.lint.detector.api.LintConstants.TAG_INTENT_FILTER;
+import static com.android.tools.lint.detector.api.LintConstants.TAG_SERVICE;
+import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Detector;
import com.android.tools.lint.detector.api.Issue;
+import com.android.tools.lint.detector.api.LintUtils;
import com.android.tools.lint.detector.api.Scope;
import com.android.tools.lint.detector.api.Severity;
import com.android.tools.lint.detector.api.Speed;
@@ -51,18 +54,17 @@ public class ExportedServiceDetector extends Detector.XmlDetectorAdapter {
"an intent-filter and do not specify exported=false) should define a " +
"permission that an entity must have in order to launch the service " +
"or bind to it. Without this, any application can use this service.",
- CATEGORY_SECURITY, 5, Severity.WARNING, EnumSet.of(Scope.MANIFEST));
+ Category.SECURITY,
+ 5,
+ Severity.WARNING,
+ ExportedServiceDetector.class,
+ EnumSet.of(Scope.MANIFEST));
/** Constructs a new accessibility check */
public ExportedServiceDetector() {
}
@Override
- public Issue[] getIssues() {
- return new Issue[] { ISSUE };
- }
-
- @Override
public Speed getSpeed() {
return Speed.FAST;
}
@@ -90,7 +92,7 @@ public class ExportedServiceDetector extends Detector.XmlDetectorAdapter {
exported = Boolean.valueOf(exportValue);
} else {
boolean haveIntentFilters = false;
- for (Element child : getChildren(element)) {
+ for (Element child : LintUtils.getChildren(element)) {
if (child.getTagName().equals(TAG_INTENT_FILTER)) {
haveIntentFilters = true;
break;
@@ -110,9 +112,9 @@ public class ExportedServiceDetector extends Detector.XmlDetectorAdapter {
permission = application.getAttributeNS(ANDROID_URI, ATTR_PERMISSION);
if (permission == null || permission.length() == 0) {
// No declared permission for this exported service: complain
- context.toolContext.report(context, ISSUE,
+ context.client.report(context, ISSUE,
context.getLocation(element),
- "Export service does not require permission", null);
+ "Exported service does not require permission", null);
}
}
}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/GridLayoutDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/GridLayoutDetector.java
index 78d8083..f8cc3c9 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/GridLayoutDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/GridLayoutDetector.java
@@ -16,9 +16,17 @@
package com.android.tools.lint.checks;
+import static com.android.tools.lint.detector.api.LintConstants.ANDROID_URI;
+import static com.android.tools.lint.detector.api.LintConstants.ATTR_COLUMN_COUNT;
+import static com.android.tools.lint.detector.api.LintConstants.ATTR_LAYOUT_COLUMN;
+import static com.android.tools.lint.detector.api.LintConstants.ATTR_LAYOUT_ROW;
+import static com.android.tools.lint.detector.api.LintConstants.ATTR_ROW_COUNT;
+
+import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.LayoutDetector;
+import com.android.tools.lint.detector.api.LintUtils;
import com.android.tools.lint.detector.api.Scope;
import com.android.tools.lint.detector.api.Severity;
import com.android.tools.lint.detector.api.Speed;
@@ -41,18 +49,17 @@ public class GridLayoutDetector extends LayoutDetector {
"the declared grid dimensions",
"Declaring a layout_row or layout_column that falls outside the declared size " +
"of a GridLayout's rowCount or columnCount is usually an unintentional error.",
- CATEGORY_CORRECTNESS, 4, Severity.ERROR, Scope.RESOURCE_FILE_SCOPE);
+ Category.CORRECTNESS,
+ 4,
+ Severity.ERROR,
+ GridLayoutDetector.class,
+ Scope.RESOURCE_FILE_SCOPE);
/** Constructs a new accessibility check */
public GridLayoutDetector() {
}
@Override
- public Issue[] getIssues() {
- return new Issue[] { ISSUE };
- }
-
- @Override
public Speed getSpeed() {
return Speed.FAST;
}
@@ -83,12 +90,12 @@ public class GridLayoutDetector extends LayoutDetector {
int declaredColumnCount = getInt(element, ATTR_COLUMN_COUNT, -1);
if (declaredColumnCount != -1 || declaredRowCount != -1) {
- for (Element child : getChildren(element)) {
+ for (Element child : LintUtils.getChildren(element)) {
if (declaredColumnCount != -1) {
int column = getInt(child, ATTR_LAYOUT_COLUMN, -1);
if (column >= declaredColumnCount) {
Attr node = child.getAttributeNodeNS(ANDROID_URI, ATTR_LAYOUT_COLUMN);
- context.toolContext.report(context, ISSUE, context.getLocation(node),
+ context.client.report(context, ISSUE, context.getLocation(node),
String.format("Column attribute (%1$d) exceeds declared grid column count (%2$d)",
column, declaredColumnCount), null);
}
@@ -97,7 +104,7 @@ public class GridLayoutDetector extends LayoutDetector {
int row = getInt(child, ATTR_LAYOUT_ROW, -1);
if (row > declaredRowCount) {
Attr node = child.getAttributeNodeNS(ANDROID_URI, ATTR_LAYOUT_ROW);
- context.toolContext.report(context, ISSUE, context.getLocation(node),
+ context.client.report(context, ISSUE, context.getLocation(node),
String.format("Row attribute (%1$d) exceeds declared grid row count (%2$d)",
row, declaredRowCount), null);
}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/HardcodedValuesDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/HardcodedValuesDetector.java
index 35922f9..fd43ecd 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/HardcodedValuesDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/HardcodedValuesDetector.java
@@ -16,6 +16,14 @@
package com.android.tools.lint.checks;
+import static com.android.tools.lint.detector.api.LintConstants.ANDROID_URI;
+import static com.android.tools.lint.detector.api.LintConstants.ATTR_CONTENT_DESCRIPTION;
+import static com.android.tools.lint.detector.api.LintConstants.ATTR_HINT;
+import static com.android.tools.lint.detector.api.LintConstants.ATTR_LABEL;
+import static com.android.tools.lint.detector.api.LintConstants.ATTR_PROMPT;
+import static com.android.tools.lint.detector.api.LintConstants.ATTR_TEXT;
+
+import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.LayoutDetector;
@@ -45,7 +53,11 @@ public class HardcodedValuesDetector extends LayoutDetector {
"* The application cannot be translated to other languages by just adding new " +
"translations for existing string resources.",
- CATEGORY_I18N, 5, Severity.WARNING, Scope.RESOURCE_FILE_SCOPE);
+ Category.I18N,
+ 5,
+ Severity.WARNING,
+ HardcodedValuesDetector.class,
+ Scope.RESOURCE_FILE_SCOPE);
// TODO: Add additional issues here, such as hardcoded colors, hardcoded sizes, etc
@@ -54,11 +66,6 @@ public class HardcodedValuesDetector extends LayoutDetector {
}
@Override
- public Issue[] getIssues() {
- return new Issue[] { ISSUE };
- }
-
- @Override
public Speed getSpeed() {
return Speed.FAST;
}
@@ -83,7 +90,7 @@ public class HardcodedValuesDetector extends LayoutDetector {
return;
}
- context.toolContext.report(context, ISSUE, context.getLocation(attribute),
+ context.client.report(context, ISSUE, context.getLocation(attribute),
String.format("[I18N] Hardcoded string \"%1$s\", should use @string resource",
value), null);
}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/InefficientWeightDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/InefficientWeightDetector.java
index 3ca204f..0f92f44 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/InefficientWeightDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/InefficientWeightDetector.java
@@ -16,9 +16,19 @@
package com.android.tools.lint.checks;
+import static com.android.tools.lint.detector.api.LintConstants.ANDROID_URI;
+import static com.android.tools.lint.detector.api.LintConstants.ATTR_LAYOUT_HEIGHT;
+import static com.android.tools.lint.detector.api.LintConstants.ATTR_LAYOUT_WEIGHT;
+import static com.android.tools.lint.detector.api.LintConstants.ATTR_LAYOUT_WIDTH;
+import static com.android.tools.lint.detector.api.LintConstants.ATTR_ORIENTATION;
+import static com.android.tools.lint.detector.api.LintConstants.LINEAR_LAYOUT;
+import static com.android.tools.lint.detector.api.LintConstants.VALUE_VERTICAL;
+
+import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.LayoutDetector;
+import com.android.tools.lint.detector.api.LintUtils;
import com.android.tools.lint.detector.api.Scope;
import com.android.tools.lint.detector.api.Severity;
import com.android.tools.lint.detector.api.Speed;
@@ -43,18 +53,17 @@ public class InefficientWeightDetector extends LayoutDetector {
"efficient to assign a width/height of 0dp to it since it will absorb all " +
"the remaining space anyway. With a declared width/height of 0dp it " +
"does not have to measure its own size first.",
- CATEGORY_PERFORMANCE, 3, Severity.WARNING, Scope.RESOURCE_FILE_SCOPE);
+ Category.PERFORMANCE,
+ 3,
+ Severity.WARNING,
+ InefficientWeightDetector.class,
+ Scope.RESOURCE_FILE_SCOPE);
/** Constructs a new {@link InefficientWeightDetector} */
public InefficientWeightDetector() {
}
@Override
- public Issue[] getIssues() {
- return new Issue[] { ISSUE };
- }
-
- @Override
public Speed getSpeed() {
return Speed.FAST;
}
@@ -66,7 +75,7 @@ public class InefficientWeightDetector extends LayoutDetector {
@Override
public void visitElement(Context context, Element element) {
- List<Element> children = getChildren(element);
+ List<Element> children = LintUtils.getChildren(element);
// See if there is exactly one child with a weight
Element weightChild = null;
for (Element child : children) {
@@ -93,7 +102,7 @@ public class InefficientWeightDetector extends LayoutDetector {
String msg = String.format(
"Use a %1$s of 0dip instead of %2$s for better performance",
dimension, size);
- context.toolContext.report(context, ISSUE,
+ context.client.report(context, ISSUE,
context.getLocation(sizeNode != null ? sizeNode : weightChild), msg, null);
}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/LintConstants.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/LintConstants.java
deleted file mode 100644
index 79feac2..0000000
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/LintConstants.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.tools.lint.checks;
-
-/**
- * Constants used by the various detectors, defined in one place
- */
-public class LintConstants {
- // Tags
- public static final String TAG_SERVICE = "service"; //$NON-NLS-1$
- public static final String TAG_USES_PERMISSION = "uses-permission";//$NON-NLS-1$
- public static final String TAG_APPLICATION = "application"; //$NON-NLS-1$
- public static final String TAG_INTENT_FILTER = " intent-filter"; //$NON-NLS-1$
-
- // Attributes
- public static final String ATTR_EXPORTED = "exported"; //$NON-NLS-1$
- public static final String ATTR_PERMISSION = "permission"; //$NON-NLS-1$
-
- // Filenames
- public static final String ANDROID_MANIFEST_XML = "AndroidManifest.xml"; //$NON-NLS-1$
-
-}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/ManifestOrderDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/ManifestOrderDetector.java
index 1126080..bb82541 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/ManifestOrderDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/ManifestOrderDetector.java
@@ -16,10 +16,12 @@
package com.android.tools.lint.checks;
-import static com.android.tools.lint.checks.LintConstants.ANDROID_MANIFEST_XML;
-import static com.android.tools.lint.checks.LintConstants.TAG_APPLICATION;
-import static com.android.tools.lint.checks.LintConstants.TAG_USES_PERMISSION;
+import static com.android.tools.lint.detector.api.LintConstants.ANDROID_MANIFEST_XML;
+import static com.android.tools.lint.detector.api.LintConstants.TAG_APPLICATION;
+import static com.android.tools.lint.detector.api.LintConstants.TAG_USES_PERMISSION;
+import static com.android.tools.lint.detector.api.LintConstants.TAG_USES_SDK;
+import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Detector;
import com.android.tools.lint.detector.api.Issue;
@@ -50,7 +52,11 @@ public class ManifestOrderDetector extends Detector.XmlDetectorAdapter {
"themes not getting applied correctly) when the <application> tag appears " +
"before some of these other elements, so it's best to order your" +
"manifest in the logical dependency order.",
- CATEGORY_CORRECTNESS, 5, Severity.WARNING, EnumSet.of(Scope.MANIFEST));
+ Category.CORRECTNESS,
+ 5,
+ Severity.WARNING,
+ ManifestOrderDetector.class,
+ EnumSet.of(Scope.MANIFEST));
/** Constructs a new accessibility check */
public ManifestOrderDetector() {
@@ -59,14 +65,10 @@ public class ManifestOrderDetector extends Detector.XmlDetectorAdapter {
private boolean mSeenApplication;
@Override
- public Issue[] getIssues() {
- return new Issue[] { ISSUE };
- }
-
- @Override
public Speed getSpeed() {
return Speed.FAST;
}
+
@Override
public boolean appliesTo(Context context, File file) {
return file.getName().equals(ANDROID_MANIFEST_XML);
@@ -87,7 +89,7 @@ public class ManifestOrderDetector extends Detector.XmlDetectorAdapter {
"permission", //$NON-NLS-1$
"permission-tree", //$NON-NLS-1$
"permission-group", //$NON-NLS-1$
- "uses-sdk", //$NON-NLS-1$
+ TAG_USES_SDK,
"uses-configuration", //$NON-NLS-1$
"uses-feature", //$NON-NLS-1$
"supports-screens", //$NON-NLS-1$
@@ -102,7 +104,7 @@ public class ManifestOrderDetector extends Detector.XmlDetectorAdapter {
if (tag.equals(TAG_APPLICATION)) {
mSeenApplication = true;
} else if (mSeenApplication) {
- context.toolContext.report(context, ISSUE, context.getLocation(element),
+ context.client.report(context, ISSUE, context.getLocation(element),
String.format("<%1$s> tag appears after <application> tag", tag), null);
// Don't complain for *every* element following the <application> tag
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/MergeRootFrameLayoutDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/MergeRootFrameLayoutDetector.java
index 92f7058..d5fb886 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/MergeRootFrameLayoutDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/MergeRootFrameLayoutDetector.java
@@ -16,6 +16,13 @@
package com.android.tools.lint.checks;
+import static com.android.tools.lint.detector.api.LintConstants.ANDROID_URI;
+import static com.android.tools.lint.detector.api.LintConstants.ATTR_BACKGROUND;
+import static com.android.tools.lint.detector.api.LintConstants.ATTR_FOREGROUND;
+import static com.android.tools.lint.detector.api.LintConstants.ATTR_LAYOUT_GRAVITY;
+import static com.android.tools.lint.detector.api.LintConstants.FRAME_LAYOUT;
+
+import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.LayoutDetector;
@@ -38,18 +45,17 @@ public class MergeRootFrameLayoutDetector extends LayoutDetector {
"If a <FrameLayout> is the root of a layout and does not provide background " +
"or padding etc, it can be replaced with a <merge> tag which is slightly " +
"more efficient.",
- CATEGORY_PERFORMANCE, 4, Severity.WARNING, Scope.RESOURCE_FILE_SCOPE);
+ Category.PERFORMANCE,
+ 4,
+ Severity.WARNING,
+ MergeRootFrameLayoutDetector.class,
+ Scope.RESOURCE_FILE_SCOPE);
/** Constructs a new {@link MergeRootFrameLayoutDetector} */
public MergeRootFrameLayoutDetector() {
}
@Override
- public Issue[] getIssues() {
- return new Issue[] { ISSUE };
- }
-
- @Override
public Speed getSpeed() {
return Speed.FAST;
}
@@ -63,7 +69,7 @@ public class MergeRootFrameLayoutDetector extends LayoutDetector {
&& !root.hasAttributeNS(ANDROID_URI, ATTR_BACKGROUND)
&& !root.hasAttributeNS(ANDROID_URI, ATTR_FOREGROUND)
&& !hasPadding(root)) {
- context.toolContext.report(context, ISSUE, context.getLocation(root),
+ context.client.report(context, ISSUE, context.getLocation(root),
"This <FrameLayout> can be replaced with a <merge> tag", null);
}
}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/NestedScrollingWidgetDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/NestedScrollingWidgetDetector.java
index c264f15..8528e90 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/NestedScrollingWidgetDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/NestedScrollingWidgetDetector.java
@@ -16,6 +16,13 @@
package com.android.tools.lint.checks;
+import static com.android.tools.lint.detector.api.LintConstants.GALLERY;
+import static com.android.tools.lint.detector.api.LintConstants.GRID_VIEW;
+import static com.android.tools.lint.detector.api.LintConstants.HORIZONTAL_SCROLL_VIEW;
+import static com.android.tools.lint.detector.api.LintConstants.LIST_VIEW;
+import static com.android.tools.lint.detector.api.LintConstants.SCROLL_VIEW;
+
+import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.LayoutDetector;
@@ -43,18 +50,17 @@ public class NestedScrollingWidgetDetector extends LayoutDetector {
// TODO: Better description!
"A scrolling widget such as a ScrollView should not contain any nested " +
"scrolling widgets since this has various usability issues",
- CATEGORY_CORRECTNESS, 7, Severity.WARNING, Scope.RESOURCE_FILE_SCOPE);
+ Category.CORRECTNESS,
+ 7,
+ Severity.WARNING,
+ NestedScrollingWidgetDetector.class,
+ Scope.RESOURCE_FILE_SCOPE);
/** Constructs a new {@link NestedScrollingWidgetDetector} */
public NestedScrollingWidgetDetector() {
}
@Override
- public Issue[] getIssues() {
- return new Issue[] { ISSUE };
- }
-
- @Override
public void beforeCheckFile(Context context) {
mVisitingHorizontalScroll = 0;
mVisitingVerticalScroll = 0;
@@ -116,7 +122,7 @@ public class NestedScrollingWidgetDetector extends LayoutDetector {
"horizontally scrolling widget (%2$s)";
}
String msg = String.format(format, parent.getTagName(), element.getTagName());
- context.toolContext.report(context, ISSUE, context.getLocation(element), msg, null);
+ context.client.report(context, ISSUE, context.getLocation(element), msg, null);
}
}
}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/ProguardDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/ProguardDetector.java
index 500d14a..841e7d6 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/ProguardDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/ProguardDetector.java
@@ -16,6 +16,7 @@
package com.android.tools.lint.checks;
+import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Detector;
import com.android.tools.lint.detector.api.Issue;
@@ -42,15 +43,14 @@ public class ProguardDetector extends Detector {
"-keepclasseswithmembers, since the old flags also implies " +
"\"allow shrinking\" which means symbols only referred to from XML and " +
"not Java (such as possibly CustomViews) can get deleted.",
- CATEGORY_CORRECTNESS, 8, Severity.ERROR, EnumSet.of(Scope.PROGUARD)).setMoreInfo(
+ Category.CORRECTNESS,
+ 8,
+ Severity.ERROR,
+ ProguardDetector.class,
+ EnumSet.of(Scope.PROGUARD)).setMoreInfo(
"http://http://code.google.com/p/android/issues/detail?id=16384"); //$NON-NLS-1$
@Override
- public Issue[] getIssues() {
- return new Issue[] { ISSUE };
- }
-
- @Override
public void run(Context context) {
String contents = context.getContents();
if (contents != null) {
@@ -59,7 +59,7 @@ public class ProguardDetector extends Detector {
"-keepclasseswithmembernames class * {\n" + //$NON-NLS-1$
" public <init>(android."); //$NON-NLS-1$
if (index != -1) {
- context.toolContext.report(context, ISSUE, context.getLocation(context),
+ context.client.report(context, ISSUE, context.getLocation(context),
"Obsolete proguard file; use -keepclasseswithmembers instead of -keepclasseswithmembernames", null);
}
}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/PxUsageDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/PxUsageDetector.java
index e2bee0e..2e8d41e 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/PxUsageDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/PxUsageDetector.java
@@ -16,6 +16,7 @@
package com.android.tools.lint.checks;
+import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.LayoutDetector;
@@ -46,7 +47,11 @@ public class PxUsageDetector extends LayoutDetector {
"in your application code to work with bitmaps that are not pre-scaled for the " +
"current screen density, you might need to scale the pixel values that you use in " +
"your code to match the un-scaled bitmap source.",
- CATEGORY_CORRECTNESS, 2, Severity.WARNING, Scope.RESOURCE_FILE_SCOPE).setMoreInfo(
+ Category.CORRECTNESS,
+ 2,
+ Severity.WARNING,
+ PxUsageDetector.class,
+ Scope.RESOURCE_FILE_SCOPE).setMoreInfo(
"http://developer.android.com/guide/practices/screens_support.html#screen-independence"); //$NON-NLS-1$
/** Constructs a new {@link PxUsageDetector} */
@@ -54,11 +59,6 @@ public class PxUsageDetector extends LayoutDetector {
}
@Override
- public Issue[] getIssues() {
- return new Issue[] { ISSUE };
- }
-
- @Override
public Speed getSpeed() {
return Speed.FAST;
}
@@ -76,7 +76,7 @@ public class PxUsageDetector extends LayoutDetector {
// 0px is fine. 0px is 0dp regardless of density...
return;
}
- context.toolContext.report(context, ISSUE, context.getLocation(attribute),
+ context.client.report(context, ISSUE, context.getLocation(attribute),
"Avoid using \"px\" as units; use \"dp\" instead", null);
}
}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/ScrollViewChildDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/ScrollViewChildDetector.java
index 4387315..62db541 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/ScrollViewChildDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/ScrollViewChildDetector.java
@@ -16,9 +16,19 @@
package com.android.tools.lint.checks;
+import static com.android.tools.lint.detector.api.LintConstants.ANDROID_URI;
+import static com.android.tools.lint.detector.api.LintConstants.ATTR_LAYOUT_HEIGHT;
+import static com.android.tools.lint.detector.api.LintConstants.ATTR_LAYOUT_WIDTH;
+import static com.android.tools.lint.detector.api.LintConstants.HORIZONTAL_SCROLL_VIEW;
+import static com.android.tools.lint.detector.api.LintConstants.SCROLL_VIEW;
+import static com.android.tools.lint.detector.api.LintConstants.VALUE_FILL_PARENT;
+import static com.android.tools.lint.detector.api.LintConstants.VALUE_MATCH_PARENT;
+
+import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.LayoutDetector;
+import com.android.tools.lint.detector.api.LintUtils;
import com.android.tools.lint.detector.api.Scope;
import com.android.tools.lint.detector.api.Severity;
import com.android.tools.lint.detector.api.Speed;
@@ -43,18 +53,17 @@ public class ScrollViewChildDetector extends LayoutDetector {
"ScrollView children must set their layout_width or layout_height attributes " +
"to wrap_content rather than fill_parent or match_parent in the scrolling " +
"dimension",
- CATEGORY_CORRECTNESS, 7, Severity.WARNING, Scope.RESOURCE_FILE_SCOPE);
+ Category.CORRECTNESS,
+ 7,
+ Severity.WARNING,
+ ScrollViewChildDetector.class,
+ Scope.RESOURCE_FILE_SCOPE);
/** Constructs a new {@link ScrollViewChildDetector} */
public ScrollViewChildDetector() {
}
@Override
- public Issue[] getIssues() {
- return new Issue[] { ISSUE };
- }
-
- @Override
public Speed getSpeed() {
return Speed.FAST;
}
@@ -69,7 +78,7 @@ public class ScrollViewChildDetector extends LayoutDetector {
@Override
public void visitElement(Context context, Element element) {
- List<Element> children = getChildren(element);
+ List<Element> children = LintUtils.getChildren(element);
boolean isHorizontal = HORIZONTAL_SCROLL_VIEW.equals(element.getTagName());
String attributeName = isHorizontal ? ATTR_LAYOUT_WIDTH : ATTR_LAYOUT_HEIGHT;
for (Element child : children) {
@@ -78,7 +87,7 @@ public class ScrollViewChildDetector extends LayoutDetector {
if (VALUE_FILL_PARENT.equals(value) || VALUE_MATCH_PARENT.equals(value)) {
String msg = String.format("This %1$s should use android:%2$s=\"wrap_content\"",
child.getTagName(), attributeName);
- context.toolContext.report(context, ISSUE, context.getLocation(sizeNode), msg,
+ context.client.report(context, ISSUE, context.getLocation(sizeNode), msg,
null);
}
}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/StateListDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/StateListDetector.java
index 3b412e5..7fa2ae4 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/StateListDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/StateListDetector.java
@@ -17,8 +17,10 @@
package com.android.tools.lint.checks;
import com.android.resources.ResourceFolderType;
+import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Issue;
+import com.android.tools.lint.detector.api.LintUtils;
import com.android.tools.lint.detector.api.ResourceXmlDetector;
import com.android.tools.lint.detector.api.Scope;
import com.android.tools.lint.detector.api.Severity;
@@ -42,18 +44,17 @@ public class StateListDetector extends ResourceXmlDetector {
"In a selector, only the last child in the state list should omit a " +
"state qualifier. If not, all subsequent items in the list will be ignored " +
"since the given item will match all.",
- CATEGORY_CORRECTNESS, 5, Severity.WARNING, Scope.RESOURCE_FILE_SCOPE);
+ Category.CORRECTNESS,
+ 5,
+ Severity.WARNING,
+ StateListDetector.class,
+ Scope.RESOURCE_FILE_SCOPE);
/** Constructs a new {@link StateListDetector} */
public StateListDetector() {
};
@Override
- public Issue[] getIssues() {
- return new Issue[] { ISSUE };
- }
-
- @Override
public boolean appliesTo(ResourceFolderType folderType) {
return folderType == ResourceFolderType.DRAWABLE;
}
@@ -72,7 +73,7 @@ public class StateListDetector extends ResourceXmlDetector {
Element root = document.getDocumentElement();
if (root != null && root.getTagName().equals("selector")) { //$NON-NLS-1$
- List<Element> children = getChildren(root);
+ List<Element> children = LintUtils.getChildren(root);
for (int i = 0; i < children.size() - 1; i++) {
Element child = children.get(i);
boolean hasState = false;
@@ -85,7 +86,7 @@ public class StateListDetector extends ResourceXmlDetector {
}
}
if (!hasState) {
- context.toolContext.report(context, ISSUE, context.getLocation(child),
+ context.client.report(context, ISSUE, context.getLocation(child),
String.format("No android:state_ attribute found on <item> %1$d, later states not reachable",
i), null);
}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/TextFieldDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/TextFieldDetector.java
index cc17fbd..f06c5fb 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/TextFieldDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/TextFieldDetector.java
@@ -16,6 +16,13 @@
package com.android.tools.lint.checks;
+import static com.android.tools.lint.detector.api.LintConstants.ANDROID_URI;
+import static com.android.tools.lint.detector.api.LintConstants.ATTR_HINT;
+import static com.android.tools.lint.detector.api.LintConstants.ATTR_INPUT_METHOD;
+import static com.android.tools.lint.detector.api.LintConstants.ATTR_INPUT_TYPE;
+import static com.android.tools.lint.detector.api.LintConstants.EDIT_TEXT;
+
+import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.LayoutDetector;
@@ -45,18 +52,17 @@ public class TextFieldDetector extends LayoutDetector {
"If you really want to keep the text field generic, you can suppress this warning " +
"by setting inputType=\"text\".",
- CATEGORY_USABILITY, 5, Severity.WARNING, Scope.RESOURCE_FILE_SCOPE);
+ Category.USABILITY,
+ 5,
+ Severity.WARNING,
+ TextFieldDetector.class,
+ Scope.RESOURCE_FILE_SCOPE);
/** Constructs a new {@link TextFieldDetector} */
public TextFieldDetector() {
}
@Override
- public Issue[] getIssues() {
- return new Issue[] { ISSUE };
- }
-
- @Override
public Speed getSpeed() {
return Speed.FAST;
}
@@ -76,7 +82,7 @@ public class TextFieldDetector extends LayoutDetector {
return;
}
- context.toolContext.report(context, ISSUE, context.getLocation(element),
+ context.client.report(context, ISSUE, context.getLocation(element),
"This text field does not specify an inputType or a hint", null);
}
}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/TooManyViewsDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/TooManyViewsDetector.java
index 70bcf45..116cdfe 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/TooManyViewsDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/TooManyViewsDetector.java
@@ -16,6 +16,7 @@
package com.android.tools.lint.checks;
+import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.LayoutDetector;
@@ -40,7 +41,11 @@ public class TooManyViewsDetector extends LayoutDetector {
"reducing the number of views in this layout.\n\n" +
"The maximum view count defaults to 80 but can be configured with the " +
"environment variable ANDROID_LINT_MAX_VIEW_COUNT.",
- CATEGORY_PERFORMANCE, 1, Severity.WARNING, Scope.RESOURCE_FILE_SCOPE);
+ Category.PERFORMANCE,
+ 1,
+ Severity.WARNING,
+ TooManyViewsDetector.class,
+ Scope.RESOURCE_FILE_SCOPE);
/** Issue of having too deep hierarchies in layouts */
public static final Issue TOO_DEEP = Issue.create(
@@ -50,7 +55,11 @@ public class TooManyViewsDetector extends LayoutDetector {
"Consider using a flatter layout (such as RelativeLayout or GridLayout)." +
"The default maximum depth is 10 but can be configured with the environment " +
"variable ANDROID_LINT_MAX_DEPTH.",
- CATEGORY_PERFORMANCE, 1, Severity.WARNING, Scope.RESOURCE_FILE_SCOPE);
+ Category.PERFORMANCE,
+ 1,
+ Severity.WARNING,
+ TooManyViewsDetector.class,
+ Scope.RESOURCE_FILE_SCOPE);
private static final int MAX_VIEW_COUNT;
private static final int MAX_DEPTH;
@@ -92,11 +101,6 @@ public class TooManyViewsDetector extends LayoutDetector {
}
@Override
- public Issue[] getIssues() {
- return new Issue[] { TOO_DEEP, TOO_MANY };
- }
-
- @Override
public Speed getSpeed() {
return Speed.FAST;
}
@@ -125,12 +129,12 @@ public class TooManyViewsDetector extends LayoutDetector {
mWarnedAboutDepth = true;
String msg = String.format("%1$s has more than %2$d levels, bad for performance",
context.file.getName(), MAX_DEPTH);
- context.toolContext.report(context, TOO_DEEP, context.getLocation(element), msg, null);
+ context.client.report(context, TOO_DEEP, context.getLocation(element), msg, null);
}
if (mViewCount == MAX_VIEW_COUNT) {
String msg = String.format("%1$s has more than %2$d views, bad for performance",
context.file.getName(), MAX_VIEW_COUNT);
- context.toolContext.report(context, TOO_MANY, context.getLocation(element), msg, null);
+ context.client.report(context, TOO_MANY, context.getLocation(element), msg, null);
}
}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/TranslationDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/TranslationDetector.java
index d105980..80371d8 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/TranslationDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/TranslationDetector.java
@@ -18,8 +18,10 @@ package com.android.tools.lint.checks;
import com.android.annotations.VisibleForTesting;
import com.android.resources.ResourceFolderType;
+import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Issue;
+import com.android.tools.lint.detector.api.LintUtils;
import com.android.tools.lint.detector.api.Location;
import com.android.tools.lint.detector.api.ResourceXmlDetector;
import com.android.tools.lint.detector.api.Scope;
@@ -72,7 +74,11 @@ public class TranslationDetector extends ResourceXmlDetector {
"subset of the strings and fall back to the standard language strings. " +
"You can require all regions to provide a full translation by setting the " +
"environment variable ANDROID_LINT_COMPLETE_REGIONS.",
- CATEGORY_CORRECTNESS, 8, Severity.ERROR, Scope.ALL_RESOURCES_SCOPE);
+ Category.CORRECTNESS,
+ 8,
+ Severity.ERROR,
+ TranslationDetector.class,
+ Scope.ALL_RESOURCES_SCOPE);
/** Are there extra translations that are "unused" (appear only in specific languages) ? */
public static final Issue EXTRA = Issue.create(
@@ -82,7 +88,11 @@ public class TranslationDetector extends ResourceXmlDetector {
"no corresponding string in the default locale, then this string is probably " +
"unused. (It's technically possible that your application is only intended to " +
"run in a specific locale, but it's still a good idea to provide a fallback.)",
- CATEGORY_CORRECTNESS, 6, Severity.WARNING, Scope.ALL_RESOURCES_SCOPE);
+ Category.CORRECTNESS,
+ 6,
+ Severity.WARNING,
+ TranslationDetector.class,
+ Scope.ALL_RESOURCES_SCOPE);
private Set<String> mNames;
private boolean mIgnoreFile;
@@ -98,11 +108,6 @@ public class TranslationDetector extends ResourceXmlDetector {
}
@Override
- public Issue[] getIssues() {
- return new Issue[] { MISSING, EXTRA };
- }
-
- @Override
public Speed getSpeed() {
return Speed.NORMAL;
}
@@ -163,8 +168,8 @@ public class TranslationDetector extends ResourceXmlDetector {
return;
}
- boolean reportMissing = context.toolContext.isEnabled(MISSING);
- boolean reportExtra = context.toolContext.isEnabled(EXTRA);
+ boolean reportMissing = context.configuration.isEnabled(MISSING);
+ boolean reportExtra = context.configuration.isEnabled(EXTRA);
// res/strings.xml etc
String defaultLanguage = "Default";
@@ -299,26 +304,26 @@ public class TranslationDetector extends ResourceXmlDetector {
// defined in other languages, so there's no problem.
if (stringCount != strings.size()) {
if (reportMissing) {
- Set<String> difference = difference(defaultStrings, strings);
+ Set<String> difference = LintUtils.difference(defaultStrings, strings);
if (difference.size() > 0) {
List<String> sorted = new ArrayList<String>(difference);
Collections.sort(sorted);
Location location = getLocation(language, parentFolderToLanguage);
- context.toolContext.report(context, MISSING, location,
+ context.client.report(context, MISSING, location,
String.format("Locale %1$s is missing translations for: %2$s",
- language, formatList(sorted, 4)), null);
+ language, LintUtils.formatList(sorted, 4)), null);
}
}
if (reportExtra) {
- Set<String> difference = difference(strings, defaultStrings);
+ Set<String> difference = LintUtils.difference(strings, defaultStrings);
if (difference.size() > 0) {
List<String> sorted = new ArrayList<String>(difference);
Collections.sort(sorted);
Location location = getLocation(language, parentFolderToLanguage);
- context.toolContext.report(context, EXTRA, location, String.format(
+ context.client.report(context, EXTRA, location, String.format(
"Locale %1$s is translating names not found in default locale: %2$s",
- language, formatList(sorted, 4)), null);
+ language, LintUtils.formatList(sorted, 4)), null);
}
}
}
@@ -337,30 +342,6 @@ public class TranslationDetector extends ResourceXmlDetector {
return location;
}
- private static Set<String> difference(Set<String> a, Set<String> b) {
- HashSet<String> copy = new HashSet<String>(a);
- copy.removeAll(b);
- return copy;
- }
-
- static String formatList(List<String> strings, int maxItems) {
- StringBuilder sb = new StringBuilder(20 * strings.size());
-
- for (int i = 0, n = strings.size(); i < n; i++) {
- if (sb.length() > 0) {
- sb.append(", "); //$NON-NLS-1$
- }
- sb.append(strings.get(i));
-
- if (i == maxItems - 1 && n > maxItems) {
- sb.append(String.format("... (%1$d more)", n - i - 1));
- break;
- }
- }
-
- return sb.toString();
- }
-
@Override
public void visitElement(Context context, Element element) {
if (mIgnoreFile) {
@@ -369,7 +350,7 @@ public class TranslationDetector extends ResourceXmlDetector {
Attr attribute = element.getAttributeNode(ATTR_NAME);
if (attribute == null || attribute.getValue().length() == 0) {
- context.toolContext.report(context, MISSING, context.getLocation(element),
+ context.client.report(context, MISSING, context.getLocation(element),
"Missing name attribute in <string> declaration", null);
} else {
String name = attribute.getValue();
@@ -379,12 +360,10 @@ public class TranslationDetector extends ResourceXmlDetector {
return;
}
- // TODO: Consider string-arrays as well?
-
// Check for duplicate name definitions? No, because there can be
// additional customizations like product=
//if (mNames.contains(name)) {
- // context.toolContext.report(ISSUE, context.getLocation(attribute),
+ // context.mClient.report(ISSUE, context.getLocation(attribute),
// String.format("Duplicate name %1$s, already defined earlier in this file",
// name));
//}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/UnusedResourceDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/UnusedResourceDetector.java
index 4af024f..4ec1551 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/UnusedResourceDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/UnusedResourceDetector.java
@@ -16,12 +16,12 @@
package com.android.tools.lint.checks;
-import com.android.resources.FolderTypeRelationship;
-import com.android.resources.ResourceFolderType;
import com.android.resources.ResourceType;
+import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Detector;
import com.android.tools.lint.detector.api.Issue;
+import com.android.tools.lint.detector.api.LintUtils;
import com.android.tools.lint.detector.api.Location;
import com.android.tools.lint.detector.api.Position;
import com.android.tools.lint.detector.api.ResourceXmlDetector;
@@ -57,7 +57,10 @@ public class UnusedResourceDetector extends ResourceXmlDetector implements Detec
public static final Issue ISSUE = Issue.create("UnusedResources", //$NON-NLS-1$
"Looks for unused resources",
"Unused resources make applications larger and slow down builds.",
- CATEGORY_PERFORMANCE, 3, Severity.WARNING,
+ Category.PERFORMANCE,
+ 3,
+ Severity.WARNING,
+ UnusedResourceDetector.class,
EnumSet.of(Scope.MANIFEST, Scope.ALL_RESOURCE_FILES, Scope.ALL_JAVA_FILES));
/** Unused id's */
public static final Issue ISSUE_IDS = Issue.create("UnusedIds", //$NON-NLS-1$
@@ -66,7 +69,10 @@ public class UnusedResourceDetector extends ResourceXmlDetector implements Detec
"from anywhere. Having id definitions, even if unused, is not necessarily a bad " +
"idea since they make working on layouts and menus easier, so there is not a " +
"strong reason to delete these.",
- CATEGORY_PERFORMANCE, 1, Severity.WARNING,
+ Category.PERFORMANCE,
+ 1,
+ Severity.WARNING,
+ UnusedResourceDetector.class,
EnumSet.of(Scope.MANIFEST, Scope.ALL_RESOURCE_FILES, Scope.ALL_JAVA_FILES))
.setEnabledByDefault(false);
@@ -83,11 +89,6 @@ public class UnusedResourceDetector extends ResourceXmlDetector implements Detec
}
@Override
- public Issue[] getIssues() {
- return new Issue[] { ISSUE };
- }
-
- @Override
public void run(Context context) {
assert false;
}
@@ -109,14 +110,16 @@ public class UnusedResourceDetector extends ResourceXmlDetector implements Detec
// TODO: Look up from project metadata
File src = new File(context.project.getDir(), "src"); //$NON-NLS-1$
if (!src.exists()) {
- context.toolContext.log(null, "Did not find source folder in project");
+ context.client.log(null, "Did not find source folder in project %1$s",
+ context.project.getDir());
} else {
scanJavaFile(context, src);
}
File gen = new File(context.project.getDir(), "gen"); //$NON-NLS-1$
if (!gen.exists()) {
- context.toolContext.log(null, "Did not find gen folder in project");
+ context.client.log(null, "Did not find gen folder in project %1$s",
+ context.project.getDir());
} else {
scanJavaFile(context, gen);
}
@@ -146,7 +149,7 @@ public class UnusedResourceDetector extends ResourceXmlDetector implements Detec
private void addJavaDeclarations(Context context, File file) {
// mDeclarations
- String s = context.toolContext.readFile(file);
+ String s = context.client.readFile(file);
String[] lines = s.split("\n"); //$NON-NLS-1$
String currentType = null;
for (int i = 0; i < lines.length; i++) {
@@ -183,7 +186,7 @@ public class UnusedResourceDetector extends ResourceXmlDetector implements Detec
/** Adds the resource identifiers found in the given file into the given set */
private void addJavaReferences(Context context, File file) {
- String s = context.toolContext.readFile(file);
+ String s = context.client.readFile(file);
if (s.length() <= 2) {
return;
}
@@ -284,7 +287,7 @@ public class UnusedResourceDetector extends ResourceXmlDetector implements Detec
mDeclarations.removeAll(mReferences);
Set<String> unused = mDeclarations;
- if (unused.size() > 0 && !context.toolContext.isEnabled(ISSUE_IDS)) {
+ if (unused.size() > 0 && !context.configuration.isEnabled(ISSUE_IDS)) {
// Remove all R.id references
List<String> ids = new ArrayList<String>();
for (String resource : unused) {
@@ -309,11 +312,11 @@ public class UnusedResourceDetector extends ResourceXmlDetector implements Detec
location = mAttrToLocation.get(attr);
if (location == null) {
File f = mAttrToFile.get(attr);
- Position start = context.toolContext.getParser().getStartPosition(context,
+ Position start = context.client.getParser().getStartPosition(context,
attr);
Position end = null;
if (start != null) {
- end = context.toolContext.getParser().getEndPosition(context, attr);
+ end = context.client.getParser().getEndPosition(context, attr);
}
location = new Location(f, start, end);
}
@@ -325,7 +328,7 @@ public class UnusedResourceDetector extends ResourceXmlDetector implements Detec
int secondDot = resource.indexOf('.', 2);
String typeName = resource.substring(2, secondDot); // 2: Skip R.
ResourceType type = ResourceType.getEnum(typeName);
- if (type != null && isFileBasedResourceType(type)) {
+ if (type != null && LintUtils.isFileBasedResourceType(type)) {
String name = resource.substring(secondDot + 1);
File file = new File(context.project.getDir(),
"res" + File.separator + typeName + File.separator + //$NON-NLS-1$
@@ -335,7 +338,7 @@ public class UnusedResourceDetector extends ResourceXmlDetector implements Detec
}
}
}
- context.toolContext.report(context, ISSUE, location, message, resource);
+ context.client.report(context, ISSUE, location, message, resource);
}
mReferences = null;
@@ -345,21 +348,6 @@ public class UnusedResourceDetector extends ResourceXmlDetector implements Detec
mDeclarations = null;
}
- /** Determine if the given type corresponds to a resource that has a unique file */
- private static boolean isFileBasedResourceType(ResourceType type) {
- List<ResourceFolderType> folderTypes = FolderTypeRelationship.getRelatedFolders(type);
- for (ResourceFolderType folderType : folderTypes) {
- if (folderType != ResourceFolderType.VALUES) {
- if (type == ResourceType.ID) {
- return false;
- }
- return true;
- }
- }
- return false;
- }
-
-
@Override
public Collection<String> getApplicableAttributes() {
return ALL;
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/UseCompoundDrawableDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/UseCompoundDrawableDetector.java
index bdcf299..2a634e6 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/UseCompoundDrawableDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/UseCompoundDrawableDetector.java
@@ -16,9 +16,17 @@
package com.android.tools.lint.checks;
+import static com.android.tools.lint.detector.api.LintConstants.ANDROID_URI;
+import static com.android.tools.lint.detector.api.LintConstants.ATTR_LAYOUT_WEIGHT;
+import static com.android.tools.lint.detector.api.LintConstants.IMAGE_VIEW;
+import static com.android.tools.lint.detector.api.LintConstants.LINEAR_LAYOUT;
+import static com.android.tools.lint.detector.api.LintConstants.TEXT_VIEW;
+
+import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.LayoutDetector;
+import com.android.tools.lint.detector.api.LintUtils;
import com.android.tools.lint.detector.api.Scope;
import com.android.tools.lint.detector.api.Severity;
import com.android.tools.lint.detector.api.Speed;
@@ -41,18 +49,17 @@ public class UseCompoundDrawableDetector extends LayoutDetector {
// TODO: OFFER MORE HELP!
"A LinearLayout which contains an ImageView and a TextView can be more efficiently " +
"handled as a compound drawable",
- CATEGORY_PERFORMANCE, 6, Severity.WARNING, Scope.RESOURCE_FILE_SCOPE);
+ Category.PERFORMANCE,
+ 6,
+ Severity.WARNING,
+ UseCompoundDrawableDetector.class,
+ Scope.RESOURCE_FILE_SCOPE);
/** Constructs a new {@link UseCompoundDrawableDetector} */
public UseCompoundDrawableDetector() {
}
@Override
- public Issue[] getIssues() {
- return new Issue[] { ISSUE };
- }
-
- @Override
public Speed getSpeed() {
return Speed.FAST;
}
@@ -66,9 +73,9 @@ public class UseCompoundDrawableDetector extends LayoutDetector {
@Override
public void visitElement(Context context, Element element) {
- int childCount = getChildCount(element);
+ int childCount = LintUtils.getChildCount(element);
if (childCount == 2) {
- List<Element> children = getChildren(element);
+ List<Element> children = LintUtils.getChildren(element);
Element first = children.get(0);
Element second = children.get(1);
if ((first.getTagName().equals(IMAGE_VIEW) &&
@@ -77,7 +84,7 @@ public class UseCompoundDrawableDetector extends LayoutDetector {
((second.getTagName().equals(IMAGE_VIEW) &&
first.getTagName().equals(TEXT_VIEW) &&
!second.hasAttributeNS(ANDROID_URI, ATTR_LAYOUT_WEIGHT)))) {
- context.toolContext.report(context, ISSUE, context.getLocation(element),
+ context.client.report(context, ISSUE, context.getLocation(element),
"This tag and its children can be replaced by one <TextView/> and " +
"a compound drawable", null);
}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/UselessViewDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/UselessViewDetector.java
index becd401..6a5e3ab 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/UselessViewDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/UselessViewDetector.java
@@ -16,9 +16,21 @@
package com.android.tools.lint.checks;
+import static com.android.tools.lint.detector.api.LintConstants.ANDROID_URI;
+import static com.android.tools.lint.detector.api.LintConstants.ATTR_BACKGROUND;
+import static com.android.tools.lint.detector.api.LintConstants.ATTR_ID;
+import static com.android.tools.lint.detector.api.LintConstants.FRAME_LAYOUT;
+import static com.android.tools.lint.detector.api.LintConstants.GRID_VIEW;
+import static com.android.tools.lint.detector.api.LintConstants.HORIZONTAL_SCROLL_VIEW;
+import static com.android.tools.lint.detector.api.LintConstants.LINEAR_LAYOUT;
+import static com.android.tools.lint.detector.api.LintConstants.MERGE;
+import static com.android.tools.lint.detector.api.LintConstants.SCROLL_VIEW;
+
+import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.LayoutDetector;
+import com.android.tools.lint.detector.api.LintUtils;
import com.android.tools.lint.detector.api.Location;
import com.android.tools.lint.detector.api.Scope;
import com.android.tools.lint.detector.api.Severity;
@@ -43,7 +55,11 @@ public class UselessViewDetector extends LayoutDetector {
"a root layout, and does not have a background, can be removed and have " +
"its children moved directly into the parent for a flatter and more " +
"efficient layout hierarchy.",
- CATEGORY_PERFORMANCE, 2, Severity.WARNING, Scope.RESOURCE_FILE_SCOPE);
+ Category.PERFORMANCE,
+ 2,
+ Severity.WARNING,
+ UselessViewDetector.class,
+ Scope.RESOURCE_FILE_SCOPE);
/** Issue of including a leaf that isn't shown */
public static final Issue USELESS_LEAF = Issue.create(
@@ -51,18 +67,17 @@ public class UselessViewDetector extends LayoutDetector {
"Checks whether a leaf layout can be removed.",
"A layout that has no children or no background can often be removed (since it " +
"is invisible) for a flatter and more efficient layout hierarchy.",
- CATEGORY_PERFORMANCE, 2, Severity.WARNING, Scope.RESOURCE_FILE_SCOPE);
+ Category.PERFORMANCE,
+ 2,
+ Severity.WARNING,
+ UselessViewDetector.class,
+ Scope.RESOURCE_FILE_SCOPE);
/** Constructs a new {@link UselessViewDetector} */
public UselessViewDetector() {
}
@Override
- public Issue[] getIssues() {
- return new Issue[] { USELESS_PARENT, USELESS_LEAF };
- }
-
- @Override
public Speed getSpeed() {
return Speed.FAST;
}
@@ -106,7 +121,7 @@ public class UselessViewDetector extends LayoutDetector {
@Override
public void visitElement(Context context, Element element) {
- int childCount = getChildCount(element);
+ int childCount = LintUtils.getChildCount(element);
if (childCount == 0) {
// Check to see if this is a leaf layout that can be removed
checkUselessLeaf(context, element);
@@ -141,9 +156,9 @@ public class UselessViewDetector extends LayoutDetector {
}
// This method is only called when we've already ensured that it has children
- assert getChildCount(element) > 0;
+ assert LintUtils.getChildCount(element) > 0;
- int parentChildCount = getChildCount(parent);
+ int parentChildCount = LintUtils.getChildCount(parent);
if (parentChildCount != 1) {
// Don't remove if the node has siblings
return;
@@ -176,12 +191,12 @@ public class UselessViewDetector extends LayoutDetector {
format += "; transfer the background attribute to the other view";
}
String message = String.format(format, tag, parentTag);
- context.toolContext.report(context, USELESS_PARENT, location, message, null);
+ context.client.report(context, USELESS_PARENT, location, message, null);
}
// This is the old UselessView check from layoutopt
private void checkUselessLeaf(Context context, Element element) {
- assert getChildCount(element) == 0;
+ assert LintUtils.getChildCount(element) == 0;
// Conditions:
// - The node is a container view (LinearLayout, etc.)
@@ -201,6 +216,6 @@ public class UselessViewDetector extends LayoutDetector {
String tag = element.getTagName();
String message = String.format(
"This %1$s view is useless (no children, no background, no id)", tag);
- context.toolContext.report(context, USELESS_LEAF, location, message, null);
+ context.client.report(context, USELESS_LEAF, location, message, null);
}
}
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/MainTest.java b/lint/libs/lint_checks/tests/src/com/android/tools/lint/MainTest.java
index c26b84a..d4fd069 100644
--- a/lint/libs/lint_checks/tests/src/com/android/tools/lint/MainTest.java
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/MainTest.java
@@ -29,7 +29,7 @@ public class MainTest extends TestCase {
"\n" +
"* The application cannot be translated to other languages by just adding new " +
"translations for existing string resources.";
- String wrapped = Main.wrap(s, 70);
+ String wrapped = Main.wrap(s, 70, "");
assertEquals(
"Hardcoding text attributes directly in layout files is bad for several\n" +
"reasons:\n" +
@@ -42,4 +42,27 @@ public class MainTest extends TestCase {
"adding new translations for existing string resources.\n",
wrapped);
}
+
+ public void testWrapPrefix() {
+ String s =
+ "Hardcoding text attributes directly in layout files is bad for several reasons:\n" +
+ "\n" +
+ "* When creating configuration variations (for example for landscape or portrait)" +
+ "you have to repeat the actual text (and keep it up to date when making changes)\n" +
+ "\n" +
+ "* The application cannot be translated to other languages by just adding new " +
+ "translations for existing string resources.";
+ String wrapped = Main.wrap(s, 70, " ");
+ assertEquals(
+ "Hardcoding text attributes directly in layout files is bad for several\n" +
+ " reasons:\n" +
+ " \n" +
+ " * When creating configuration variations (for example for\n" +
+ " landscape or portrait)you have to repeat the actual text (and keep\n" +
+ " it up to date when making changes)\n" +
+ " \n" +
+ " * The application cannot be translated to other languages by just\n" +
+ " adding new translations for existing string resources.\n",
+ wrapped);
+ }
}
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/PositionXmlParserTest.java b/lint/libs/lint_checks/tests/src/com/android/tools/lint/PositionXmlParserTest.java
index 038576c..af37f06 100644
--- a/lint/libs/lint_checks/tests/src/com/android/tools/lint/PositionXmlParserTest.java
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/PositionXmlParserTest.java
@@ -106,6 +106,8 @@ public class PositionXmlParserTest extends TestCase {
assertEquals(xml.indexOf("/>", start.getOffset()) + 2, end.getOffset());
assertEquals(16, end.getLine());
+ parser.dispose(context);
+
file.delete();
}
}
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/AbstractCheckTest.java b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/AbstractCheckTest.java
index 543bee8..3227f0d 100644
--- a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/AbstractCheckTest.java
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/AbstractCheckTest.java
@@ -17,15 +17,17 @@
package com.android.tools.lint.checks;
import com.android.tools.lint.PositionXmlParser;
-import com.android.tools.lint.api.DetectorRegistry;
-import com.android.tools.lint.api.IDomParser;
-import com.android.tools.lint.api.Lint;
-import com.android.tools.lint.api.ToolContext;
+import com.android.tools.lint.client.api.Configuration;
+import com.android.tools.lint.client.api.IDomParser;
+import com.android.tools.lint.client.api.IssueRegistry;
+import com.android.tools.lint.client.api.Lint;
+import com.android.tools.lint.client.api.LintClient;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Detector;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.Location;
import com.android.tools.lint.detector.api.Position;
+import com.android.tools.lint.detector.api.Project;
import com.android.tools.lint.detector.api.Severity;
import java.io.BufferedReader;
@@ -49,12 +51,25 @@ import junit.framework.TestCase;
abstract class AbstractCheckTest extends TestCase {
protected abstract Detector getDetector();
- private class CustomDetectorRegistry extends DetectorRegistry {
+ protected List<Issue> getIssues() {
+ List<Issue> issues = new ArrayList<Issue>();
+ Class<? extends Detector> detectorClass = getDetector().getClass();
+ // Get the list of issues from the registry and filter out others, to make sure
+ // issues are properly registered
+ List<Issue> candidates = new BuiltinIssueRegistry().getIssues();
+ for (Issue issue : candidates) {
+ if (issue.getDetectorClass() == detectorClass) {
+ issues.add(issue);
+ }
+ }
+
+ return issues;
+ }
+
+ private class CustomIssueRegistry extends IssueRegistry {
@Override
- public List<? extends Detector> getDetectors() {
- List<Detector> detectors = new ArrayList<Detector>(1);
- detectors.add(AbstractCheckTest.this.getDetector());
- return detectors;
+ public List<Issue> getIssues() {
+ return AbstractCheckTest.this.getIssues();
}
}
@@ -73,12 +88,11 @@ abstract class AbstractCheckTest extends TestCase {
protected String checkLint(List<File> files) throws Exception {
mOutput = new StringBuilder();
- TestToolContext toolContext = new TestToolContext();
- Lint analyzer = new Lint(new CustomDetectorRegistry(), toolContext,
- null);
- analyzer.analyze(files);
+ TestLintClient lintClient = new TestLintClient();
+ Lint analyzer = new Lint(new CustomIssueRegistry(), lintClient);
+ analyzer.analyze(files, null /* scope */);
- List<String> errors = toolContext.getErrors();
+ List<String> errors = lintClient.getErrors();
Collections.sort(errors);
for (String error : errors) {
if (mOutput.length() > 0) {
@@ -140,9 +154,9 @@ abstract class AbstractCheckTest extends TestCase {
base = new File("/tmp");
}
Calendar c = Calendar.getInstance();
- String name = String.format("lintTests_%1$tF_%1$tT", c).replace(':', '-'); //$NON-NLS-1$
+ String name = String.format("lintTests/%1$tF_%1$tT", c).replace(':', '-'); //$NON-NLS-1$
File tmpDir = new File(base, name);
- if (!tmpDir.exists() && tmpDir.mkdir()) {
+ if (!tmpDir.exists() && tmpDir.mkdirs()) {
sTempDir = tmpDir;
} else {
sTempDir = base;
@@ -231,10 +245,15 @@ abstract class AbstractCheckTest extends TestCase {
}
protected boolean isEnabled(Issue issue) {
+ Class<? extends Detector> detectorClass = getDetector().getClass();
+ if (issue.getDetectorClass() == detectorClass) {
+ return true;
+ }
+
return false;
}
- private class TestToolContext extends ToolContext {
+ private class TestLintClient extends LintClient {
private List<String> mErrors = new ArrayList<String>();
public List<String> getErrors() {
@@ -263,7 +282,7 @@ abstract class AbstractCheckTest extends TestCase {
sb.append(' ');
}
- Severity severity = getSeverity(issue);
+ Severity severity = context.configuration.getSeverity(issue);
sb.append(severity.getDescription());
sb.append(": ");
@@ -292,35 +311,36 @@ abstract class AbstractCheckTest extends TestCase {
}
@Override
- public boolean isEnabled(Issue issue) {
- for (Issue detectorIssue : getDetector().getIssues()) {
- if (issue == detectorIssue) {
- return true;
- }
+ public String readFile(File file) {
+ try {
+ return AbstractCheckTest.readFile(new FileReader(file));
+ } catch (Throwable e) {
+ fail(e.toString());
}
+ return null;
+ }
- return AbstractCheckTest.this.isEnabled(issue);
+ @Override
+ public Configuration getConfiguration(Project project) {
+ return new TestConfiguration();
}
+ }
+ public class TestConfiguration extends Configuration {
@Override
- public boolean isSuppressed(Context context, Issue issue, Location range, String message,
- Severity severity, Object data) {
- return false;
+ public boolean isEnabled(Issue issue) {
+ return AbstractCheckTest.this.isEnabled(issue);
}
@Override
- public Severity getSeverity(Issue issue) {
- return issue.getDefaultSeverity();
+ public void ignore(Context context, Issue issue, Location location, String message,
+ Object data) {
+ fail("Not supported in tests.");
}
@Override
- public String readFile(File file) {
- try {
- return AbstractCheckTest.readFile(new FileReader(file));
- } catch (Throwable e) {
- fail(e.toString());
- }
- return null;
+ public void setSeverity(Issue issue, Severity severity) {
+ fail("Not supported in tests.");
}
}
}
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/ExportedServiceDetectorTest.java b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/ExportedServiceDetectorTest.java
index 874901a..839c199 100644
--- a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/ExportedServiceDetectorTest.java
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/ExportedServiceDetectorTest.java
@@ -27,7 +27,7 @@ public class ExportedServiceDetectorTest extends AbstractCheckTest {
public void testBroken() throws Exception {
assertEquals(
- "AndroidManifest.xml:12: Warning: Export service does not require permission",
+ "AndroidManifest.xml:12: Warning: Exported service does not require permission",
lintProject(
"exportservice1.xml=>AndroidManifest.xml",
"res/values/strings.xml"));
@@ -35,7 +35,7 @@ public class ExportedServiceDetectorTest extends AbstractCheckTest {
public void testBroken2() throws Exception {
assertEquals(
- "AndroidManifest.xml:12: Warning: Export service does not require permission",
+ "AndroidManifest.xml:12: Warning: Exported service does not require permission",
lintProject(
"exportservice1.xml=>AndroidManifest.xml",
"res/values/strings.xml"));
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/TranslationDetectorTest.java b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/TranslationDetectorTest.java
index f6a9727..2d4e807 100644
--- a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/TranslationDetectorTest.java
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/TranslationDetectorTest.java
@@ -17,6 +17,7 @@
package com.android.tools.lint.checks;
import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.LintUtils;
import java.util.Arrays;
@@ -69,18 +70,4 @@ public class TranslationDetectorTest extends AbstractCheckTest {
"res/values-land/strings.xml",
"res/values-nl-rNL/strings.xml"));
}
-
- public void testPrintList() throws Exception {
- assertEquals("foo, bar, baz",
- TranslationDetector.formatList(Arrays.asList("foo", "bar", "baz"), 3));
- assertEquals("foo, bar, baz",
- TranslationDetector.formatList(Arrays.asList("foo", "bar", "baz"), 5));
-
- assertEquals("foo, bar, baz... (3 more)",
- TranslationDetector.formatList(
- Arrays.asList("foo", "bar", "baz", "4", "5", "6"), 3));
- assertEquals("foo... (5 more)",
- TranslationDetector.formatList(
- Arrays.asList("foo", "bar", "baz", "4", "5", "6"), 1));
- }
}
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/UnusedResourceDetectorTest.java b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/UnusedResourceDetectorTest.java
index 498bfab..3dfb4f6 100644
--- a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/UnusedResourceDetectorTest.java
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/UnusedResourceDetectorTest.java
@@ -32,9 +32,9 @@ public class UnusedResourceDetectorTest extends AbstractCheckTest {
protected boolean isEnabled(Issue issue) {
if (issue == UnusedResourceDetector.ISSUE_IDS) {
return mEnableIds;
+ } else {
+ return true;
}
-
- return super.isEnabled(issue);
}
public void testUnused() throws Exception {
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/detector/api/LintUtilsTest.java b/lint/libs/lint_checks/tests/src/com/android/tools/lint/detector/api/LintUtilsTest.java
new file mode 100644
index 0000000..e4c4372
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/detector/api/LintUtilsTest.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.tools.lint.detector.api;
+
+import java.io.File;
+import java.util.Arrays;
+
+import junit.framework.TestCase;
+
+@SuppressWarnings("javadoc")
+public class LintUtilsTest extends TestCase {
+ public void testPrintList() throws Exception {
+ assertEquals("foo, bar, baz",
+ LintUtils.formatList(Arrays.asList("foo", "bar", "baz"), 3));
+ assertEquals("foo, bar, baz",
+ LintUtils.formatList(Arrays.asList("foo", "bar", "baz"), 5));
+
+ assertEquals("foo, bar, baz... (3 more)",
+ LintUtils.formatList(
+ Arrays.asList("foo", "bar", "baz", "4", "5", "6"), 3));
+ assertEquals("foo... (5 more)",
+ LintUtils.formatList(
+ Arrays.asList("foo", "bar", "baz", "4", "5", "6"), 1));
+ }
+
+ public void testIsXmlFile() throws Exception {
+ assertTrue(LintUtils.isXmlFile(new File("foo.xml")));
+ assertTrue(LintUtils.isXmlFile(new File("foo.Xml")));
+ assertTrue(LintUtils.isXmlFile(new File("foo.XML")));
+
+ assertFalse(LintUtils.isXmlFile(new File("foo.png")));
+ assertFalse(LintUtils.isXmlFile(new File("xml")));
+ assertFalse(LintUtils.isXmlFile(new File("xml.png")));
+ }
+} \ No newline at end of file