Package com.android.tools.lint.client.api

Source Code of com.android.tools.lint.client.api.IssueRegistry

/*
* 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.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.annotations.VisibleForTesting;
import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Detector;
import com.android.tools.lint.detector.api.Implementation;
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 com.google.common.annotations.Beta;
import com.google.common.collect.Maps;

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
* <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>
*/
@Beta
public abstract class IssueRegistry {
    private static List<Category> sCategories;
    private static Map<String, Issue> sIdToIssue;
    private static Map<EnumSet<Scope>, List<Issue>> sScopeIssues = Maps.newHashMap();

    /**
     * Creates a new {@linkplain IssueRegistry}
     */
    protected IssueRegistry() {
    }

    private static final Implementation DUMMY_IMPLEMENTATION = new Implementation(Detector.class,
            EnumSet.noneOf(Scope.class));
    /**
     * Issue reported by lint (not a specific detector) when it cannot even
     * parse an XML file prior to analysis
     */
    @NonNull
    public static final Issue PARSER_ERROR = Issue.create(
            "ParserError", //$NON-NLS-1$
            "Parser Errors",
            "Finds files that contain fatal parser errors",
            "Lint will ignore any files that contain fatal parsing errors. These may contain " +
            "other errors, or contain code which affects issues in other files.",
            Category.CORRECTNESS,
            10,
            Severity.ERROR,
            DUMMY_IMPLEMENTATION);

    /**
     * Issue reported by lint for various other issues which prevents lint from
     * running normally when it's not necessarily an error in the user's code base.
     */
    @NonNull
    public static final Issue LINT_ERROR = Issue.create(
            "LintError", //$NON-NLS-1$
            "Lint Failure",
            "Issues related to running lint itself, such as failure to read files, etc",
            "This issue type represents a problem running lint itself. Examples include " +
            "failure to find bytecode for source files (which means certain detectors " +
            "could not be run), parsing errors in lint configuration files, etc." +
            "\n" +
            "These errors are not errors in your own code, but they are shown to make " +
            "it clear that some checks were not completed.",

            Category.LINT,
            10,
            Severity.ERROR,
            DUMMY_IMPLEMENTATION);

    /**
     * Issue reported when lint is canceled
     */
    @NonNull
    public static final Issue CANCELLED = Issue.create(
            "LintCanceled", //$NON-NLS-1$
            "Lint Canceled",
            "Lint canceled by user",
            "Lint canceled by user; the issue report may not be complete.",

            Category.LINT,
            0,
            Severity.INFORMATIONAL,
            DUMMY_IMPLEMENTATION);

    /**
     * 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!)
     */
    @NonNull
    public abstract List<Issue> getIssues();

    /**
     * Returns all available issues of a given scope (regardless of whether
     * they are actually enabled for a given configuration etc)
     *
     * @param scope the applicable scope set
     * @return a list of issues
     */
    @NonNull
    private List<Issue> getIssuesForScope(@NonNull EnumSet<Scope> scope) {
        List<Issue> list = sScopeIssues.get(scope);
        if (list == null) {
            List<Issue> issues = getIssues();
            if (scope.equals(Scope.ALL)) {
                list = issues;
            } else {
                int initialSize = 12;
                if (scope.contains(Scope.RESOURCE_FILE)) {
                    initialSize += 50;
                }
                if (scope.contains(Scope.JAVA_FILE)) {
                    initialSize += 12;
                }
                if (scope.contains(Scope.CLASS_FILE)) {
                    initialSize += 12;
                }
                list = new ArrayList<Issue>(initialSize);
                for (Issue issue : issues) {
                    // Determine if the scope matches
                    if (issue.getImplementation().isAdequate(scope)) {
                        list.add(issue);
                    }
                }
            }
            sScopeIssues.put(scope, list);
        }

        return list;
    }

    /**
     * Creates a list of detectors applicable to the given scope, 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
     */
    @NonNull
    final List<? extends Detector> createDetectors(
            @NonNull LintClient client,
            @NonNull Configuration configuration,
            @NonNull EnumSet<Scope> scope,
            @Nullable Map<Scope, List<Detector>> scopeToDetectors) {

        List<Issue> issues = getIssuesForScope(scope);
        if (issues.isEmpty()) {
            return Collections.emptyList();
        }

        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) {
            Implementation implementation = issue.getImplementation();
            Class<? extends Detector> detectorClass = implementation.getDetectorClass();
            EnumSet<Scope> issueScope = implementation.getScope();
            if (!detectorClasses.contains(detectorClass)) {
                // Determine if the issue is enabled
                if (!configuration.isEnabled(issue)) {
                    continue;
                }

                assert implementation.isAdequate(scope); // Ensured by getIssuesForScope above

                detectorClass = client.replaceDetector(detectorClass);

                assert detectorClass != null : issue.getId();
                detectorClasses.add(detectorClass);
            }

            if (scopeToDetectors != null) {
                EnumSet<Scope> s = detectorToScope.get(detectorClass);
                if (s == null) {
                    detectorToScope.put(detectorClass, issueScope);
                } else if (!s.containsAll(issueScope)) {
                    EnumSet<Scope> union = EnumSet.copyOf(s);
                    union.addAll(issueScope);
                    detectorToScope.put(detectorClass, union);
                }
            }
        }

        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(@NonNull 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(@NonNull String name) {
        for (Category category : getCategories()) {
            if (category.getName().equals(name) || category.getFullName().equals(name)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Returns the available categories
     *
     * @return an iterator for all the categories, never null
     */
    @NonNull
    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
     */
    @Nullable
    public final Issue getIssue(@NonNull 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);
            }

            sIdToIssue.put(PARSER_ERROR.getId(), PARSER_ERROR);
            sIdToIssue.put(LINT_ERROR.getId(), LINT_ERROR);
        }
        return sIdToIssue.get(id);
    }

    /**
     * Reset the registry such that it recomputes its available issues.
     * <p>
     * NOTE: This is only intended for testing purposes.
     */
    @VisibleForTesting
    protected static void reset() {
        sIdToIssue = null;
        sCategories = null;
        sScopeIssues = Maps.newHashMap();
    }
}
TOP

Related Classes of com.android.tools.lint.client.api.IssueRegistry

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.