com.android.ide.eclipse.adt.internal.lint.EclipseLintRunner.java Source code

Java tutorial

Introduction

Here is the source code for com.android.ide.eclipse.adt.internal.lint.EclipseLintRunner.java

Source

/*
 * 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.ide.eclipse.adt.internal.lint;

import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs;
import com.android.ide.eclipse.adt.internal.sdk.ProjectState;
import com.android.ide.eclipse.adt.internal.sdk.Sdk;
import com.android.tools.lint.client.api.IssueRegistry;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.text.IDocument;
import org.eclipse.swt.widgets.Shell;

import java.util.ArrayList;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;

/**
 * Eclipse implementation for running lint on workspace files and projects.
 */
public class EclipseLintRunner {
    static final String MARKER_CHECKID_PROPERTY = "checkid"; //$NON-NLS-1$

    /**
     * Runs lint and updates the markers, and waits for the result. Returns
     * true if fatal errors were found.
     *
     * @param resources the resources (project, folder or file) to be analyzed
     * @param source if checking a single source file, the source file
     * @param doc the associated document, if known, or null
     * @param fatalOnly if true, only report fatal issues (severity=error)
     * @return true if any fatal errors were encountered.
     */
    private static boolean runLint(@NonNull List<? extends IResource> resources, @Nullable IResource source,
            @Nullable IDocument doc, boolean fatalOnly) {
        resources = addLibraries(resources);
        LintJob job = (LintJob) startLint(resources, source, doc, fatalOnly, false /*show*/);
        try {
            job.join();
            boolean fatal = job.isFatal();

            if (fatal) {
                LintViewPart.show(resources);
            }

            return fatal;
        } catch (InterruptedException e) {
            AdtPlugin.log(e, null);
        }

        return false;
    }

    /**
     * Runs lint and updates the markers. Does not wait for the job to finish -
     * just returns immediately.
     *
     * @param resources the resources (project, folder or file) to be analyzed
     * @param source if checking a single source file, the source file. When
     *            single checking an XML file, this is typically the same as the
     *            file passed in the list in the first parameter, but when
     *            checking the .class files of a Java file for example, the
     *            .class file and all the inner classes of the Java file are
     *            passed in the first parameter, and the corresponding .java
     *            source file is passed here.
     * @param doc the associated document, if known, or null
     * @param fatalOnly if true, only report fatal issues (severity=error)
     * @param show if true, show the results in a {@link LintViewPart}
     * @return the job running lint in the background.
     */
    public static Job startLint(@NonNull List<? extends IResource> resources, @Nullable IResource source,
            @Nullable IDocument doc, boolean fatalOnly, boolean show) {
        IssueRegistry registry = EclipseLintClient.getRegistry();
        EclipseLintClient client = new EclipseLintClient(registry, resources, doc, fatalOnly);
        return startLint(client, resources, source, show);
    }

    /**
     * Runs lint and updates the markers. Does not wait for the job to finish -
     * just returns immediately.
     *
     * @param client the lint client receiving issue reports etc
     * @param resources the resources (project, folder or file) to be analyzed
     * @param source if checking a single source file, the source file. When
     *            single checking an XML file, this is typically the same as the
     *            file passed in the list in the first parameter, but when
     *            checking the .class files of a Java file for example, the
     *            .class file and all the inner classes of the Java file are
     *            passed in the first parameter, and the corresponding .java
     *            source file is passed here.
     * @param show if true, show the results in a {@link LintViewPart}
     * @return the job running lint in the background.
     */
    public static Job startLint(@NonNull EclipseLintClient client, @NonNull List<? extends IResource> resources,
            @Nullable IResource source, boolean show) {
        if (resources != null && !resources.isEmpty()) {
            if (!AdtPrefs.getPrefs().getSkipLibrariesFromLint()) {
                resources = addLibraries(resources);
            }

            cancelCurrentJobs(false);

            LintJob job = new LintJob(client, resources, source);
            job.schedule();

            if (show) {
                // Show lint view where the results are listed
                LintViewPart.show(resources);
            }
            return job;
        }

        return null;
    }

    /**
     * Run Lint for an Export APK action. If it succeeds (no fatal errors)
     * returns true, and if it fails it will display an error message and return
     * false.
     *
     * @param shell the parent shell to show error messages in
     * @param project the project to run lint on
     * @return true if the lint run succeeded with no fatal errors
     */
    public static boolean runLintOnExport(Shell shell, IProject project) {
        if (AdtPrefs.getPrefs().isLintOnExport()) {
            boolean fatal = EclipseLintRunner.runLint(Collections.singletonList(project), null, null,
                    true /*fatalOnly*/);
            if (fatal) {
                MessageDialog.openWarning(shell, "Export Aborted",
                        "Export aborted because fatal lint errors were found. These "
                                + "are listed in the Lint View. Either fix these before "
                                + "running Export again, or turn off \"Run full error check "
                                + "when exporting app\" in the Android > Lint Error Checking "
                                + "preference page.");
                return false;
            }
        }

        return true;
    }

    /** Cancels the current lint jobs, if any, and optionally waits for them to finish */
    static void cancelCurrentJobs(boolean wait) {
        // Cancel any current running jobs first
        Job[] currentJobs = LintJob.getCurrentJobs();
        for (Job job : currentJobs) {
            job.cancel();
        }

        if (wait) {
            for (Job job : currentJobs) {
                try {
                    job.join();
                } catch (InterruptedException e) {
                    AdtPlugin.log(e, null);
                }
            }
        }
    }

    /** If the resource list contains projects, add in any library projects as well */
    private static List<? extends IResource> addLibraries(List<? extends IResource> resources) {
        if (resources != null && !resources.isEmpty()) {
            boolean haveProjects = false;
            for (IResource resource : resources) {
                if (resource instanceof IProject) {
                    haveProjects = true;
                    break;
                }
            }

            if (haveProjects) {
                List<IResource> result = new ArrayList<IResource>();
                Map<IProject, IProject> allProjects = new IdentityHashMap<IProject, IProject>();
                List<IProject> projects = new ArrayList<IProject>();
                for (IResource resource : resources) {
                    if (resource instanceof IProject) {
                        IProject project = (IProject) resource;
                        allProjects.put(project, project);
                        projects.add(project);
                    } else {
                        result.add(resource);
                    }
                }
                for (IProject project : projects) {
                    ProjectState state = Sdk.getProjectState(project);
                    if (state != null) {
                        for (IProject library : state.getFullLibraryProjects()) {
                            allProjects.put(library, library);
                        }
                    }
                }
                for (IProject project : allProjects.keySet()) {
                    result.add(project);
                }

                return result;
            }
        }

        return resources;
    }
}