blob: ac09a1b41ad4d5681b0d28dae1f14950f683bbbe [file] [log] [blame]
/*
* 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 static com.android.tools.lint.detector.api.LintUtils.assertionsEnabled;
import static com.android.tools.lint.detector.api.LintUtils.endsWith;
import com.android.annotations.NonNull;
import com.android.prefs.AndroidLocation;
import com.android.prefs.AndroidLocation.AndroidLocationException;
import com.android.tools.lint.client.api.IssueRegistry;
import com.android.tools.lint.detector.api.Issue;
import com.google.common.annotations.Beta;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
/** Registry which provides a list of checks to be performed on an Android project */
public class BuiltinIssueRegistry extends IssueRegistry {
/** Folder name in the .android dir where additional detector jars are found */
private static final String LINT_FOLDER = "lint"; //$NON-NLS-1$
/**
* Manifest constant for declaring an issue provider. Example:
* Lint-Registry: foo.bar.CustomIssueRegistry
*/
private static final String MF_LINT_REGISTRY = "Lint-Registry"; //$NON-NLS-1$
private static final List<Issue> sIssues;
static {
final int initialCapacity = 106;
List<Issue> issues = new ArrayList<Issue>(initialCapacity);
issues.add(AccessibilityDetector.ISSUE);
issues.add(MathDetector.ISSUE);
issues.add(FieldGetterDetector.ISSUE);
issues.add(SdCardDetector.ISSUE);
issues.add(ApiDetector.UNSUPPORTED);
issues.add(DuplicateIdDetector.CROSS_LAYOUT);
issues.add(DuplicateIdDetector.WITHIN_LAYOUT);
issues.add(WrongIdDetector.UNKNOWN_ID);
issues.add(WrongIdDetector.UNKNOWN_ID_LAYOUT);
issues.add(StateListDetector.ISSUE);
issues.add(StyleCycleDetector.ISSUE);
issues.add(InefficientWeightDetector.INEFFICIENT_WEIGHT);
issues.add(InefficientWeightDetector.NESTED_WEIGHTS);
issues.add(InefficientWeightDetector.BASELINE_WEIGHTS);
issues.add(ScrollViewChildDetector.ISSUE);
issues.add(DeprecationDetector.ISSUE);
issues.add(ObsoleteLayoutParamsDetector.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(OnClickDetector.ISSUE);
issues.add(ViewTagDetector.ISSUE);
issues.add(LocaleDetector.STRING_LOCALE);
issues.add(LocaleDetector.DATE_FORMAT);
issues.add(RegistrationDetector.ISSUE);
issues.add(HandlerDetector.ISSUE);
issues.add(FragmentDetector.ISSUE);
issues.add(TranslationDetector.EXTRA);
issues.add(TranslationDetector.MISSING);
issues.add(HardcodedValuesDetector.ISSUE);
issues.add(Utf8Detector.ISSUE);
issues.add(ProguardDetector.WRONGKEEP);
issues.add(ProguardDetector.SPLITCONFIG);
issues.add(PxUsageDetector.PX_ISSUE);
issues.add(PxUsageDetector.DP_ISSUE);
issues.add(TextFieldDetector.ISSUE);
issues.add(TextViewDetector.ISSUE);
issues.add(UnusedResourceDetector.ISSUE);
issues.add(UnusedResourceDetector.ISSUE_IDS);
issues.add(ExtraTextDetector.ISSUE);
issues.add(PrivateResourceDetector.ISSUE);
issues.add(ArraySizeDetector.INCONSISTENT);
issues.add(HardcodedDebugModeDetector.ISSUE);
issues.add(ManifestOrderDetector.ORDER);
issues.add(ManifestOrderDetector.USES_SDK);
issues.add(ManifestOrderDetector.MULTIPLE_USES_SDK);
issues.add(ManifestOrderDetector.WRONG_PARENT);
issues.add(ManifestOrderDetector.DUPLICATE_ACTIVITY);
issues.add(ManifestOrderDetector.TARGET_NEWER);
issues.add(SecurityDetector.EXPORTED_PROVIDER);
issues.add(SecurityDetector.EXPORTED_SERVICE);
issues.add(SecurityDetector.EXPORTED_ACTIVITY);
issues.add(SecurityDetector.EXPORTED_RECEIVER);
issues.add(SecurityDetector.OPEN_PROVIDER);
issues.add(SecurityDetector.WORLD_READABLE);
issues.add(SecurityDetector.WORLD_WRITEABLE);
issues.add(SecureRandomDetector.ISSUE);
issues.add(IconDetector.GIF_USAGE);
issues.add(IconDetector.ICON_DENSITIES);
issues.add(IconDetector.ICON_MISSING_FOLDER);
issues.add(IconDetector.ICON_DIP_SIZE);
issues.add(IconDetector.ICON_EXPECTED_SIZE);
issues.add(IconDetector.ICON_LOCATION);
issues.add(IconDetector.DUPLICATES_NAMES);
issues.add(IconDetector.DUPLICATES_CONFIGURATIONS);
issues.add(IconDetector.ICON_NODPI);
issues.add(TypographyDetector.DASHES);
issues.add(TypographyDetector.QUOTES);
issues.add(TypographyDetector.FRACTIONS);
issues.add(TypographyDetector.ELLIPSIS);
issues.add(TypographyDetector.OTHER);
issues.add(ButtonDetector.ORDER);
issues.add(ButtonDetector.CASE);
issues.add(ButtonDetector.BACKBUTTON);
issues.add(DetectMissingPrefix.MISSING_NAMESPACE);
issues.add(OverdrawDetector.ISSUE);
issues.add(StringFormatDetector.INVALID);
issues.add(StringFormatDetector.ARG_COUNT);
issues.add(StringFormatDetector.ARG_TYPES);
issues.add(TypoDetector.ISSUE);
issues.add(ViewTypeDetector.ISSUE);
issues.add(WrongImportDetector.ISSUE);
issues.add(WrongLocationDetector.ISSUE);
issues.add(ViewConstructorDetector.ISSUE);
issues.add(NamespaceDetector.CUSTOMVIEW);
issues.add(NamespaceDetector.UNUSED);
issues.add(NamespaceDetector.TYPO);
issues.add(AlwaysShowActionDetector.ISSUE);
issues.add(ColorUsageDetector.ISSUE);
issues.add(JavaPerformanceDetector.PAINT_ALLOC);
issues.add(JavaPerformanceDetector.USE_VALUEOF);
issues.add(JavaPerformanceDetector.USE_SPARSEARRAY);
issues.add(WakelockDetector.ISSUE);
issues.add(SetJavaScriptEnabledDetector.ISSUE);
issues.add(ToastDetector.ISSUE);
issues.add(SharedPrefsDetector.ISSUE);
issues.add(NonInternationalizedSmsDetector.ISSUE);
issues.add(PrivateKeyDetector.ISSUE);
issues.add(AnnotationDetector.ISSUE);
assert initialCapacity >= issues.size() : issues.size();
addCustomIssues(issues);
sIssues = Collections.unmodifiableList(issues);
// Check that ids are unique
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 @NonNull List<Issue> getIssues() {
return sIssues;
}
/**
* Add in custom issues registered by the user - via an environment variable
* or in the .android/lint directory.
*/
private static void addCustomIssues(List<Issue> issues) {
// Look for additional detectors registered by the user, via
// (1) an environment variable (useful for build servers etc), and
// (2) via jar files in the .android/lint directory
Set<File> files = null;
try {
File lint = new File(AndroidLocation.getFolder() + File.separator + LINT_FOLDER);
if (lint.exists()) {
File[] list = lint.listFiles();
if (list != null) {
for (File jarFile : list) {
if (endsWith(jarFile.getName(), ".jar")) { //$NON-NLS-1$
if (files == null) {
files = new HashSet<File>();
}
files.add(jarFile);
addIssuesFromJar(jarFile, issues);
}
}
}
}
} catch (AndroidLocationException e) {
// Ignore -- no android dir, so no rules to load.
}
String lintClassPath = System.getenv("ANDROID_LINT_JARS"); //$NON-NLS-1$
if (lintClassPath != null && lintClassPath.length() > 0) {
String[] paths = lintClassPath.split(File.pathSeparator);
for (String path : paths) {
File jarFile = new File(path);
if (jarFile.exists() && (files == null || !files.contains(jarFile))) {
addIssuesFromJar(jarFile, issues);
}
}
}
}
/** Add the issues found in the given jar file into the given list of issues */
private static void addIssuesFromJar(File jarFile, List<Issue> issues) {
try {
JarFile jarfile = new JarFile(jarFile);
Manifest manifest = jarfile.getManifest();
Attributes attrs = manifest.getMainAttributes();
Object object = attrs.get(new Attributes.Name(MF_LINT_REGISTRY));
if (object instanceof String) {
String className = (String) object;
// Make a class loader for this jar
try {
URL url = jarFile.toURI().toURL();
URLClassLoader loader = new URLClassLoader(new URL[] { url },
BuiltinIssueRegistry.class.getClassLoader());
try {
Class<?> registryClass = Class.forName(className, true, loader);
IssueRegistry registry = (IssueRegistry) registryClass.newInstance();
for (Issue issue : registry.getIssues()) {
issues.add(issue);
}
} catch (Throwable e) {
log(e);
}
} catch (MalformedURLException e) {
log(e);
}
}
} catch (IOException e) {
log(e);
}
}
private static void log(Throwable e) {
// TODO: Where do we log this? There's no embedding tool context here. For now,
// just dump to the console so detector developers get some feedback on what went
// wrong.
e.printStackTrace();
}
private static Set<Issue> sAdtFixes;
/**
* Returns true if the given issue has an automatic IDE fix.
*
* @param tool the name of the tool to be checked
* @param issue the issue to be checked
* @return true if the given tool is known to have an automatic fix for the
* given issue
*/
@Beta
public boolean hasAutoFix(String tool, Issue issue) {
assert tool.equals("adt"); // This is not yet a generic facility;
// the primary purpose right now is to allow for example the HTML report
// to give a hint to the user that some fixes don't require manual work
if (sAdtFixes == null) {
sAdtFixes = new HashSet<Issue>(20);
sAdtFixes.add(InefficientWeightDetector.INEFFICIENT_WEIGHT);
sAdtFixes.add(AccessibilityDetector.ISSUE);
sAdtFixes.add(InefficientWeightDetector.BASELINE_WEIGHTS);
sAdtFixes.add(HardcodedValuesDetector.ISSUE);
sAdtFixes.add(UselessViewDetector.USELESS_LEAF);
sAdtFixes.add(UselessViewDetector.USELESS_PARENT);
sAdtFixes.add(PxUsageDetector.PX_ISSUE);
sAdtFixes.add(TextFieldDetector.ISSUE);
sAdtFixes.add(SecurityDetector.EXPORTED_SERVICE);
sAdtFixes.add(DetectMissingPrefix.MISSING_NAMESPACE);
sAdtFixes.add(ScrollViewChildDetector.ISSUE);
sAdtFixes.add(ObsoleteLayoutParamsDetector.ISSUE);
sAdtFixes.add(TypographyDetector.DASHES);
sAdtFixes.add(TypographyDetector.ELLIPSIS);
sAdtFixes.add(TypographyDetector.FRACTIONS);
sAdtFixes.add(TypographyDetector.OTHER);
sAdtFixes.add(TypographyDetector.QUOTES);
sAdtFixes.add(UseCompoundDrawableDetector.ISSUE);
sAdtFixes.add(ApiDetector.UNSUPPORTED);
sAdtFixes.add(TypoDetector.ISSUE);
}
return sAdtFixes.contains(issue);
}
}