Home | History | Annotate | Download | only in checks
      1 /*
      2  * Copyright (C) 2011 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.tools.lint.checks;
     18 
     19 import static com.android.tools.lint.detector.api.LintUtils.assertionsEnabled;
     20 import static com.android.tools.lint.detector.api.LintUtils.endsWith;
     21 
     22 import com.android.prefs.AndroidLocation;
     23 import com.android.prefs.AndroidLocation.AndroidLocationException;
     24 import com.android.tools.lint.client.api.IssueRegistry;
     25 import com.android.tools.lint.detector.api.Issue;
     26 import com.google.common.annotations.Beta;
     27 
     28 import java.io.File;
     29 import java.io.IOException;
     30 import java.net.MalformedURLException;
     31 import java.net.URL;
     32 import java.net.URLClassLoader;
     33 import java.util.ArrayList;
     34 import java.util.Collections;
     35 import java.util.HashSet;
     36 import java.util.List;
     37 import java.util.Set;
     38 import java.util.jar.Attributes;
     39 import java.util.jar.JarFile;
     40 import java.util.jar.Manifest;
     41 
     42 /** Registry which provides a list of checks to be performed on an Android project */
     43 public class BuiltinIssueRegistry extends IssueRegistry {
     44     /** Folder name in the .android dir where additional detector jars are found */
     45     private static final String LINT_FOLDER = "lint"; //$NON-NLS-1$
     46 
     47     /**
     48      * Manifest constant for declaring an issue provider. Example:
     49      * Lint-Issues: foo.bar.CustomIssueRegistry
     50      */
     51     private static final String MF_LINT_REGISTRY = "Lint-Registry"; //$NON-NLS-1$
     52 
     53     private static final List<Issue> sIssues;
     54 
     55     static {
     56         final int initialCapacity = 88;
     57         List<Issue> issues = new ArrayList<Issue>(initialCapacity);
     58 
     59         issues.add(AccessibilityDetector.ISSUE);
     60         issues.add(MathDetector.ISSUE);
     61         issues.add(FieldGetterDetector.ISSUE);
     62         issues.add(SdCardDetector.ISSUE);
     63         issues.add(ApiDetector.UNSUPPORTED);
     64         issues.add(DuplicateIdDetector.CROSS_LAYOUT);
     65         issues.add(DuplicateIdDetector.WITHIN_LAYOUT);
     66         issues.add(WrongIdDetector.UNKNOWN_ID);
     67         issues.add(WrongIdDetector.UNKNOWN_ID_LAYOUT);
     68         issues.add(StateListDetector.ISSUE);
     69         issues.add(StyleCycleDetector.ISSUE);
     70         issues.add(InefficientWeightDetector.INEFFICIENT_WEIGHT);
     71         issues.add(InefficientWeightDetector.NESTED_WEIGHTS);
     72         issues.add(InefficientWeightDetector.BASELINE_WEIGHTS);
     73         issues.add(ScrollViewChildDetector.ISSUE);
     74         issues.add(DeprecationDetector.ISSUE);
     75         issues.add(ObsoleteLayoutParamsDetector.ISSUE);
     76         issues.add(MergeRootFrameLayoutDetector.ISSUE);
     77         issues.add(NestedScrollingWidgetDetector.ISSUE);
     78         issues.add(ChildCountDetector.SCROLLVIEW_ISSUE);
     79         issues.add(ChildCountDetector.ADAPTERVIEW_ISSUE);
     80         issues.add(UseCompoundDrawableDetector.ISSUE);
     81         issues.add(UselessViewDetector.USELESS_PARENT);
     82         issues.add(UselessViewDetector.USELESS_LEAF);
     83         issues.add(TooManyViewsDetector.TOO_MANY);
     84         issues.add(TooManyViewsDetector.TOO_DEEP);
     85         issues.add(GridLayoutDetector.ISSUE);
     86         issues.add(OnClickDetector.ISSUE);
     87         issues.add(RegistrationDetector.ISSUE);
     88         issues.add(TranslationDetector.EXTRA);
     89         issues.add(TranslationDetector.MISSING);
     90         issues.add(HardcodedValuesDetector.ISSUE);
     91         issues.add(Utf8Detector.ISSUE);
     92         issues.add(ProguardDetector.WRONGKEEP);
     93         issues.add(ProguardDetector.SPLITCONFIG);
     94         issues.add(PxUsageDetector.ISSUE);
     95         issues.add(TextFieldDetector.ISSUE);
     96         issues.add(TextViewDetector.ISSUE);
     97         issues.add(UnusedResourceDetector.ISSUE);
     98         issues.add(UnusedResourceDetector.ISSUE_IDS);
     99         issues.add(ExtraTextDetector.ISSUE);
    100         issues.add(PrivateResourceDetector.ISSUE);
    101         issues.add(ArraySizeDetector.INCONSISTENT);
    102         issues.add(HardcodedDebugModeDetector.ISSUE);
    103         issues.add(ManifestOrderDetector.ORDER);
    104         issues.add(ManifestOrderDetector.USES_SDK);
    105         issues.add(ManifestOrderDetector.MULTIPLE_USES_SDK);
    106         issues.add(ManifestOrderDetector.WRONG_PARENT);
    107         issues.add(SecurityDetector.EXPORTED_PROVIDER);
    108         issues.add(SecurityDetector.EXPORTED_SERVICE);
    109         issues.add(SecurityDetector.OPEN_PROVIDER);
    110         issues.add(SecurityDetector.WORLD_READABLE);
    111         issues.add(SecurityDetector.WORLD_WRITEABLE);
    112         issues.add(IconDetector.GIF_USAGE);
    113         issues.add(IconDetector.ICON_DENSITIES);
    114         issues.add(IconDetector.ICON_MISSING_FOLDER);
    115         issues.add(IconDetector.ICON_DIP_SIZE);
    116         issues.add(IconDetector.ICON_EXPECTED_SIZE);
    117         issues.add(IconDetector.ICON_LOCATION);
    118         issues.add(IconDetector.DUPLICATES_NAMES);
    119         issues.add(IconDetector.DUPLICATES_CONFIGURATIONS);
    120         issues.add(IconDetector.ICON_NODPI);
    121         issues.add(TypographyDetector.DASHES);
    122         issues.add(TypographyDetector.QUOTES);
    123         issues.add(TypographyDetector.FRACTIONS);
    124         issues.add(TypographyDetector.ELLIPSIS);
    125         issues.add(TypographyDetector.OTHER);
    126         issues.add(ButtonDetector.ORDER);
    127         issues.add(ButtonDetector.CASE);
    128         issues.add(ButtonDetector.BACKBUTTON);
    129         issues.add(DetectMissingPrefix.MISSING_NAMESPACE);
    130         issues.add(OverdrawDetector.ISSUE);
    131         issues.add(StringFormatDetector.INVALID);
    132         issues.add(StringFormatDetector.ARG_COUNT);
    133         issues.add(StringFormatDetector.ARG_TYPES);
    134         issues.add(ViewTypeDetector.ISSUE);
    135         issues.add(WrongImportDetector.ISSUE);
    136         issues.add(ViewConstructorDetector.ISSUE);
    137         issues.add(NamespaceDetector.CUSTOMVIEW);
    138         issues.add(NamespaceDetector.UNUSED);
    139         issues.add(NamespaceDetector.TYPO);
    140         issues.add(AlwaysShowActionDetector.ISSUE);
    141         issues.add(ColorUsageDetector.ISSUE);
    142         issues.add(JavaPerformanceDetector.PAINT_ALLOC);
    143         issues.add(JavaPerformanceDetector.USE_VALUEOF);
    144         issues.add(JavaPerformanceDetector.USE_SPARSEARRAY);
    145         issues.add(SetJavaScriptEnabledDetector.ISSUE);
    146         issues.add(ToastDetector.ISSUE);
    147 
    148         assert initialCapacity >= issues.size() : issues.size();
    149 
    150         addCustomIssues(issues);
    151 
    152         sIssues = Collections.unmodifiableList(issues);
    153 
    154         // Check that ids are unique
    155         if (assertionsEnabled()) {
    156             Set<String> ids = new HashSet<String>();
    157             for (Issue issue : sIssues) {
    158                 String id = issue.getId();
    159                 assert !ids.contains(id) : "Duplicate id " + id; //$NON-NLS-1$
    160                 ids.add(id);
    161             }
    162         }
    163     }
    164 
    165     /**
    166      * Constructs a new {@link BuiltinIssueRegistry}
    167      */
    168     public BuiltinIssueRegistry() {
    169     }
    170 
    171     @Override
    172     public List<Issue> getIssues() {
    173         return sIssues;
    174     }
    175 
    176     /**
    177      * Add in custom issues registered by the user - via an environment variable
    178      * or in the .android/lint directory.
    179      */
    180     private static void addCustomIssues(List<Issue> issues) {
    181         // Look for additional detectors registered by the user, via
    182         // (1) an environment variable (useful for build servers etc), and
    183         // (2) via jar files in the .android/lint directory
    184         Set<File> files = null;
    185         try {
    186             File lint = new File(AndroidLocation.getFolder() + File.separator + LINT_FOLDER);
    187             if (lint.exists()) {
    188                 File[] list = lint.listFiles();
    189                 if (list != null) {
    190                     for (File jarFile : list) {
    191                         if (endsWith(jarFile.getName(), ".jar")) { //$NON-NLS-1$
    192                             if (files == null) {
    193                                 files = new HashSet<File>();
    194                             }
    195                             files.add(jarFile);
    196                             addIssuesFromJar(jarFile, issues);
    197                         }
    198                     }
    199                 }
    200             }
    201         } catch (AndroidLocationException e) {
    202             // Ignore -- no android dir, so no rules to load.
    203         }
    204 
    205         String lintClassPath = System.getenv("ANDROID_LINT_JARS"); //$NON-NLS-1$
    206         if (lintClassPath != null && lintClassPath.length() > 0) {
    207             String[] paths = lintClassPath.split(File.pathSeparator);
    208             for (String path : paths) {
    209                 File jarFile = new File(path);
    210                 if (jarFile.exists() && (files == null || !files.contains(jarFile))) {
    211                     addIssuesFromJar(jarFile, issues);
    212                 }
    213             }
    214         }
    215 
    216     }
    217 
    218     /** Add the issues found in the given jar file into the given list of issues */
    219     private static void addIssuesFromJar(File jarFile, List<Issue> issues) {
    220         try {
    221             JarFile jarfile = new JarFile(jarFile);
    222             Manifest manifest = jarfile.getManifest();
    223             Attributes attrs = manifest.getMainAttributes();
    224             Object object = attrs.get(new Attributes.Name(MF_LINT_REGISTRY));
    225             if (object instanceof String) {
    226                 String className = (String) object;
    227 
    228                 // Make a class loader for this jar
    229                 try {
    230                     URL url = jarFile.toURI().toURL();
    231                     URLClassLoader loader = new URLClassLoader(new URL[] { url },
    232                             BuiltinIssueRegistry.class.getClassLoader());
    233                     try {
    234                         Class<?> registryClass = Class.forName(className, true, loader);
    235                         IssueRegistry registry = (IssueRegistry) registryClass.newInstance();
    236                         for (Issue issue : registry.getIssues()) {
    237                             issues.add(issue);
    238                         }
    239                     } catch (Throwable e) {
    240                         log(e);
    241                     }
    242                 } catch (MalformedURLException e) {
    243                     log(e);
    244                 }
    245             }
    246         } catch (IOException e) {
    247             log(e);
    248         }
    249     }
    250 
    251     private static void log(Throwable e) {
    252         // TODO: Where do we log this? There's no embedding tool context here. For now,
    253         // just dump to the console so detector developers get some feedback on what went
    254         // wrong.
    255         e.printStackTrace();
    256     }
    257 
    258     private static Set<Issue> sAdtFixes;
    259 
    260     /**
    261      * Returns true if the given issue has an automatic IDE fix.
    262      *
    263      * @param tool the name of the tool to be checked
    264      * @param issue the issue to be checked
    265      * @return true if the given tool is known to have an automatic fix for the
    266      *         given issue
    267      */
    268     @Beta
    269     public boolean hasAutoFix(String tool, Issue issue) {
    270         assert tool.equals("adt"); // This is not yet a generic facility;
    271         // the primary purpose right now is to allow for example the HTML report
    272         // to give a hint to the user that some fixes don't require manual work
    273 
    274         if (sAdtFixes == null) {
    275             sAdtFixes = new HashSet<Issue>(20);
    276             sAdtFixes.add(InefficientWeightDetector.INEFFICIENT_WEIGHT);
    277             sAdtFixes.add(AccessibilityDetector.ISSUE);
    278             sAdtFixes.add(InefficientWeightDetector.BASELINE_WEIGHTS);
    279             sAdtFixes.add(HardcodedValuesDetector.ISSUE);
    280             sAdtFixes.add(UselessViewDetector.USELESS_LEAF);
    281             sAdtFixes.add(UselessViewDetector.USELESS_PARENT);
    282             sAdtFixes.add(PxUsageDetector.ISSUE);
    283             sAdtFixes.add(TextFieldDetector.ISSUE);
    284             sAdtFixes.add(SecurityDetector.EXPORTED_SERVICE);
    285             sAdtFixes.add(DetectMissingPrefix.MISSING_NAMESPACE);
    286             sAdtFixes.add(ScrollViewChildDetector.ISSUE);
    287             sAdtFixes.add(ObsoleteLayoutParamsDetector.ISSUE);
    288             sAdtFixes.add(TypographyDetector.DASHES);
    289             sAdtFixes.add(TypographyDetector.ELLIPSIS);
    290             sAdtFixes.add(TypographyDetector.FRACTIONS);
    291             sAdtFixes.add(TypographyDetector.OTHER);
    292             sAdtFixes.add(TypographyDetector.QUOTES);
    293             sAdtFixes.add(UseCompoundDrawableDetector.ISSUE);
    294             sAdtFixes.add(ApiDetector.UNSUPPORTED);
    295         }
    296 
    297         return sAdtFixes.contains(issue);
    298     }
    299 }
    300