| /* |
| * 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); |
| } |
| } |