aboutsummaryrefslogtreecommitdiffstats
path: root/lint
diff options
context:
space:
mode:
Diffstat (limited to 'lint')
-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
54 files changed, 2430 insertions, 1025 deletions
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