[android-platform-tools-base] 01/06: Drop disable-lint.patch

Markus Koschany apo-guest at moszumanska.debian.org
Sat Mar 12 14:46:33 UTC 2016


This is an automated email from the git hooks/post-receive script.

apo-guest pushed a commit to branch master
in repository android-platform-tools-base.

commit e6d5eb9ea8322d74c1da737a85921235bd755095
Author: Markus Koschany <apo at debian.org>
Date:   Mon Mar 7 18:35:43 2016 +0100

    Drop disable-lint.patch
---
 debian/patches/disable-lint.patch | 9620 -------------------------------------
 debian/patches/series             |    1 -
 2 files changed, 9621 deletions(-)

diff --git a/debian/patches/disable-lint.patch b/debian/patches/disable-lint.patch
deleted file mode 100644
index c913129..0000000
--- a/debian/patches/disable-lint.patch
+++ /dev/null
@@ -1,9620 +0,0 @@
-From: Markus Koschany <apo at debian.org>
-Date: Wed, 17 Feb 2016 18:55:28 +0100
-Subject: disable lint
-
-Temporarily necessary until lombok-ast is available in Debian and Lint can be
-built from source.
-
-Forwarded: not-needed
----
- .../com/android/builder/model/AndroidProject.java  |    6 -
- base/build-system/gradle-core/build.gradle         |    2 +-
- .../com/android/build/gradle/AndroidConfig.java    |    4 -
- .../gradle/internal/ApplicationTaskManager.java    |    1 -
- .../build/gradle/internal/LibraryTaskManager.java  |   73 +-
- .../build/gradle/internal/LintGradleClient.java    |  198 --
- .../build/gradle/internal/LintGradleProject.java   |  658 -----
- .../build/gradle/internal/LintGradleRequest.java   |   69 -
- .../android/build/gradle/internal/TaskManager.java |  108 +-
- .../build/gradle/internal/dsl/LintOptions.java     |  798 ------
- .../internal/model/DefaultAndroidProject.java      |   11 -
- .../build/gradle/internal/model/ModelBuilder.java  |    5 -
- .../internal/variant/LibraryVariantData.java       |   15 +-
- .../build/gradle/tasks/ExtractAnnotations.groovy   |  265 --
- .../build/gradle/tasks/GroovyGradleDetector.java   |  242 --
- .../com/android/build/gradle/tasks/Lint.groovy     |  295 ---
- .../build/gradle/tasks/PackageApplication.java     |   46 -
- .../build/gradle/tasks/ResourceUsageAnalyzer.java  | 2568 --------------------
- .../build/gradle/tasks/ShrinkResources.groovy      |  220 --
- .../gradle/tasks/annotations/ApiDatabase.java      |  356 ---
- .../annotations/ExtractAnnotationsDriver.java      |  400 ---
- .../build/gradle/tasks/annotations/Extractor.java  | 2484 -------------------
- .../gradle/tasks/annotations/TypedefCollector.java |  154 --
- .../gradle/tasks/annotations/TypedefRemover.java   |  164 --
- .../com/android/build/gradle/BaseExtension.java    |   19 -
- 25 files changed, 4 insertions(+), 9157 deletions(-)
- delete mode 100644 base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/LintGradleClient.java
- delete mode 100644 base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/LintGradleProject.java
- delete mode 100644 base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/LintGradleRequest.java
- delete mode 100644 base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/LintOptions.java
- delete mode 100644 base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ExtractAnnotations.groovy
- delete mode 100644 base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/GroovyGradleDetector.java
- delete mode 100644 base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/Lint.groovy
- delete mode 100644 base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ResourceUsageAnalyzer.java
- delete mode 100644 base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ShrinkResources.groovy
- delete mode 100644 base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/ApiDatabase.java
- delete mode 100644 base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/ExtractAnnotationsDriver.java
- delete mode 100644 base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/Extractor.java
- delete mode 100644 base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/TypedefCollector.java
- delete mode 100644 base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/TypedefRemover.java
-
-diff --git a/base/build-system/builder-model/src/main/java/com/android/builder/model/AndroidProject.java b/base/build-system/builder-model/src/main/java/com/android/builder/model/AndroidProject.java
-index 068f365..2dfa09c 100644
---- a/base/build-system/builder-model/src/main/java/com/android/builder/model/AndroidProject.java
-+++ b/base/build-system/builder-model/src/main/java/com/android/builder/model/AndroidProject.java
-@@ -185,12 +185,6 @@ public interface AndroidProject {
-     AaptOptions getAaptOptions();
- 
-     /**
--     * Returns the lint options.
--     */
--    @NonNull
--    LintOptions getLintOptions();
--
--    /**
-      * Returns the dependencies that were not successfully resolved. The returned list gets
-      * populated only if the system property {@link #PROPERTY_BUILD_MODEL_ONLY} has been
-      * set to {@code true}.
-diff --git a/base/build-system/gradle-core/build.gradle b/base/build-system/gradle-core/build.gradle
-index e36483b..f8ed473 100644
---- a/base/build-system/gradle-core/build.gradle
-+++ b/base/build-system/gradle-core/build.gradle
-@@ -16,7 +16,7 @@ ext.proguardVersion = "5.2.1"
- 
- dependencies {
-     compile project(':base:builder')
--    compile project(':base:lint')
-+    //compile project(':base:lint')
-     compile "net.sf.proguard:proguard-gradle:${project.ext.proguardVersion}"
- 
-     // Add gradleApi to classpath for compilation, but use provided configuration so that groovy is
-diff --git a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/AndroidConfig.java b/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/AndroidConfig.java
-index ec7373f..46eca5e 100644
---- a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/AndroidConfig.java
-+++ b/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/AndroidConfig.java
-@@ -26,7 +26,6 @@ import com.android.build.gradle.internal.dsl.AdbOptions;
- import com.android.build.gradle.internal.dsl.CoreBuildType;
- import com.android.build.gradle.internal.dsl.CoreProductFlavor;
- import com.android.build.gradle.internal.dsl.DexOptions;
--import com.android.build.gradle.internal.dsl.LintOptions;
- import com.android.build.gradle.internal.dsl.PackagingOptions;
- import com.android.build.gradle.internal.dsl.PreprocessingOptions;
- import com.android.build.gradle.internal.dsl.Splits;
-@@ -99,9 +98,6 @@ public interface AndroidConfig {
-     /** JaCoCo options. */
-     JacocoExtension getJacoco();
- 
--    /** Lint options. */
--    LintOptions getLintOptions();
--
-     /** Packaging options. */
-     PackagingOptions getPackagingOptions();
- 
-diff --git a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/ApplicationTaskManager.java b/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/ApplicationTaskManager.java
-index 649c43e..3badeb9 100644
---- a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/ApplicationTaskManager.java
-+++ b/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/ApplicationTaskManager.java
-@@ -216,7 +216,6 @@ public class ApplicationTaskManager extends TaskManager {
-                 new Recorder.Block<Void>() {
-                     @Override
-                     public Void call() {
--                        createLintTasks(tasks, variantScope);
-                         return null;
-                     }
-                 });
-diff --git a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/LibraryTaskManager.java b/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/LibraryTaskManager.java
-index 2f2c7a3..f2af17d 100644
---- a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/LibraryTaskManager.java
-+++ b/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/LibraryTaskManager.java
-@@ -36,7 +36,7 @@ import com.android.build.gradle.internal.variant.BaseVariantOutputData;
- import com.android.build.gradle.internal.variant.LibVariantOutputData;
- import com.android.build.gradle.internal.variant.LibraryVariantData;
- import com.android.build.gradle.internal.variant.VariantHelper;
--import com.android.build.gradle.tasks.ExtractAnnotations;
-+//import com.android.build.gradle.tasks.ExtractAnnotations;
- import com.android.build.gradle.tasks.MergeResources;
- import com.android.builder.core.AndroidBuilder;
- import com.android.builder.core.BuilderConstants;
-@@ -306,26 +306,9 @@ public class LibraryTaskManager extends TaskManager {
- 
-                 });
- 
--        // copy lint.jar into the bundle folder
--        Copy lintCopy = project.getTasks().create(
--                variantScope.getTaskName("copy", "Lint"), Copy.class);
--        lintCopy.dependsOn(LINT_COMPILE);
--        lintCopy.from(new File(
--                variantScope.getGlobalScope().getIntermediatesDir(),
--                "lint/lint.jar"));
--        lintCopy.into(new File(
--                variantScope.getGlobalScope().getIntermediatesDir(),
--                DIR_BUNDLES + "/" + dirName));
- 
-         final Zip bundle = project.getTasks().create(variantScope.getTaskName("bundle"), Zip.class);
- 
--        if (variantData.getVariantDependency().isAnnotationsPresent()) {
--            libVariantData.generateAnnotationsTask =
--                    createExtractAnnotations(project, variantData);
--        }
--        if (libVariantData.generateAnnotationsTask != null) {
--            bundle.dependsOn(libVariantData.generateAnnotationsTask);
--        }
- 
-         final boolean instrumented = variantConfig.getBuildType().isTestCoverageEnabled();
- 
-@@ -438,18 +421,11 @@ public class LibraryTaskManager extends TaskManager {
-                                 jar.exclude(packageName + "/Manifest$*.class");
-                                 jar.exclude(packageName + "/BuildConfig.class");
-                             }
--
--                            if (libVariantData.generateAnnotationsTask != null) {
--                                // In case extract annotations strips out private typedef annotation classes
--                                jar.dependsOn(libVariantData.generateAnnotationsTask);
--                            }
-                             return null;
-                         }
-                     });
-         }
- 
--        bundle.dependsOn(packageRes.getName(), packageRenderscript, lintCopy, packageJniLibs,
--                mergeProGuardFileTask);
-         TaskManager.optionalDependsOn(bundle, pcData.getClassGeneratingTasks());
-         TaskManager.optionalDependsOn(bundle, pcData.getLibraryGeneratingTasks());
- 
-@@ -551,58 +527,11 @@ public class LibraryTaskManager extends TaskManager {
-                 new Recorder.Block<Void>() {
-                     @Override
-                     public Void call() throws Exception {
--                        createLintTasks(tasks, variantScope);
-                         return null;
-                     }
-                 });
-     }
- 
--    public ExtractAnnotations createExtractAnnotations(
--            final Project project,
--            final BaseVariantData variantData) {
--        final GradleVariantConfiguration config = variantData.getVariantConfiguration();
--
--        final ExtractAnnotations task = project.getTasks().create(
--                variantData.getScope().getTaskName("extract", "Annotations"),
--                ExtractAnnotations.class);
--        task.setDescription(
--                "Extracts Android annotations for the " + variantData.getVariantConfiguration()
--                        .getFullName()
--                        + " variant into the archive file");
--        task.setGroup(BasePlugin.BUILD_GROUP);
--        task.variant = variantData;
--        task.setDestinationDir(new File(
--                variantData.getScope().getGlobalScope().getIntermediatesDir(),
--                ANNOTATIONS + "/" + config.getDirName()));
--        task.output = new File(task.getDestinationDir(), FN_ANNOTATIONS_ZIP);
--        task.classDir = new File(variantData.getScope().getGlobalScope().getIntermediatesDir(),
--                "classes/" + variantData.getVariantConfiguration().getDirName());
--        task.setSource(variantData.getJavaSources());
--        task.encoding = getExtension().getCompileOptions().getEncoding();
--        task.setSourceCompatibility(
--                getExtension().getCompileOptions().getSourceCompatibility().toString());
--        ConventionMappingHelper.map(task, "classpath", new Callable<ConfigurableFileCollection>() {
--            @Override
--            public ConfigurableFileCollection call() throws Exception {
--                return project.files(androidBuilder.getCompileClasspath(config));
--            }
--        });
--        task.dependsOn(variantData.getScope().getJavacTask().getName());
--
--        // Setup the boot classpath just before the task actually runs since this will
--        // force the sdk to be parsed. (Same as in compileTask)
--        task.doFirst(new Action<Task>() {
--            @Override
--            public void execute(Task task) {
--                if (task instanceof ExtractAnnotations) {
--                    ExtractAnnotations extractAnnotations = (ExtractAnnotations) task;
--                    extractAnnotations.bootClasspath = androidBuilder.getBootClasspathAsStrings();
--                }
--            }
--        });
--
--        return task;
--    }
- 
-     private Task getAssembleDefault() {
-         if (assembleDefault == null) {
-diff --git a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/LintGradleClient.java b/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/LintGradleClient.java
-deleted file mode 100644
-index 34d2240..0000000
---- a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/LintGradleClient.java
-+++ /dev/null
-@@ -1,198 +0,0 @@
--/*
-- * Copyright (C) 2013 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.build.gradle.internal;
--
--import static com.android.builder.model.AndroidProject.FD_INTERMEDIATES;
--import static java.io.File.separator;
--
--import com.android.annotations.NonNull;
--import com.android.annotations.Nullable;
--import com.android.builder.model.AndroidProject;
--import com.android.builder.model.Variant;
--import com.android.tools.lint.LintCliClient;
--import com.android.tools.lint.LintCliFlags;
--import com.android.tools.lint.Warning;
--import com.android.tools.lint.client.api.IssueRegistry;
--import com.android.tools.lint.client.api.LintRequest;
--import com.android.tools.lint.detector.api.Issue;
--import com.android.tools.lint.detector.api.Project;
--import com.google.common.collect.Lists;
--import com.google.common.collect.Maps;
--import com.google.common.collect.Sets;
--
--import java.io.File;
--import java.io.IOException;
--import java.util.Collections;
--import java.util.List;
--import java.util.Map;
--
--public class LintGradleClient extends LintCliClient {
--    private final AndroidProject mModelProject;
--    private final String mVariantName;
--    private final org.gradle.api.Project mGradleProject;
--    private List<File> mCustomRules = Lists.newArrayList();
--    private File mSdkHome;
--
--    public LintGradleClient(
--            @NonNull IssueRegistry registry,
--            @NonNull LintCliFlags flags,
--            @NonNull org.gradle.api.Project gradleProject,
--            @NonNull AndroidProject modelProject,
--            @Nullable File sdkHome,
--            @Nullable String variantName) {
--        super(flags);
--        mGradleProject = gradleProject;
--        mModelProject = modelProject;
--        mVariantName = variantName;
--        mSdkHome = sdkHome;
--        mRegistry = registry;
--    }
--
--    public void setCustomRules(List<File> customRules) {
--        mCustomRules = customRules;
--    }
--
--    @NonNull
--    @Override
--    public List<File> findRuleJars(@NonNull Project project) {
--        return mCustomRules;
--    }
--
--    @NonNull
--    @Override
--    protected Project createProject(@NonNull File dir, @NonNull File referenceDir) {
--        // Should not be called by lint since we supply an explicit set of projects
--        // to the LintRequest
--        throw new IllegalStateException();
--    }
--
--    @Override
--    public File getSdkHome() {
--        if (mSdkHome != null) {
--            return mSdkHome;
--        }
--        return super.getSdkHome();
--    }
--
--    @Override
--    @Nullable
--    public File getCacheDir(boolean create) {
--        File dir = new File(mGradleProject.getRootProject().getBuildDir(),
--                FD_INTERMEDIATES + separator + "lint-cache"); //$NON-NLS-1$
--        if (dir.exists() || create && dir.mkdirs()) {
--            return dir;
--        }
--
--        return super.getCacheDir(create);
--    }
--
--    @Override
--    @NonNull
--    protected LintRequest createLintRequest(@NonNull List<File> files) {
--        return new LintGradleRequest(this, mModelProject, mGradleProject, mVariantName, files);
--    }
--
--    /** Run lint with the given registry and return the resulting warnings */
--    @NonNull
--    public List<Warning> run(@NonNull IssueRegistry registry) throws IOException {
--        run(registry, Collections.<File>emptyList());
--        return mWarnings;
--    }
--
--    /**
--     * Given a list of results from separate variants, merge them into a single
--     * list of warnings, and mark their
--     * @param warningMap a map from variant to corresponding warnings
--     * @param project the project model
--     * @return a merged list of issues
--     */
--    @NonNull
--    public static List<Warning> merge(
--            @NonNull Map<Variant,List<Warning>> warningMap,
--            @NonNull AndroidProject project) {
--        // Easy merge?
--        if (warningMap.size() == 1) {
--            return warningMap.values().iterator().next();
--        }
--        int maxCount = 0;
--        for (List<Warning> warnings : warningMap.values()) {
--            int size = warnings.size();
--            maxCount = Math.max(size, maxCount);
--        }
--        if (maxCount == 0) {
--            return Collections.emptyList();
--        }
--
--        int totalVariantCount = project.getVariants().size();
--
--        List<Warning> merged = Lists.newArrayListWithExpectedSize(2 * maxCount);
--
--        // Map fro issue to message to line number to file name to canonical warning
--        Map<Issue,Map<String, Map<Integer, Map<String, Warning>>>> map =
--                Maps.newHashMapWithExpectedSize(2 * maxCount);
--
--        for (Map.Entry<Variant,List<Warning>> entry : warningMap.entrySet()) {
--            Variant variant = entry.getKey();
--            List<Warning> warnings = entry.getValue();
--            for (Warning warning : warnings) {
--                Map<String,Map<Integer,Map<String,Warning>>> messageMap = map.get(warning.issue);
--                if (messageMap == null) {
--                    messageMap = Maps.newHashMap();
--                    map.put(warning.issue, messageMap);
--                }
--                Map<Integer, Map<String, Warning>> lineMap = messageMap.get(warning.message);
--                if (lineMap == null) {
--                    lineMap = Maps.newHashMap();
--                    messageMap.put(warning.message, lineMap);
--                }
--                Map<String, Warning> fileMap = lineMap.get(warning.line);
--                if (fileMap == null) {
--                    fileMap = Maps.newHashMap();
--                    lineMap.put(warning.line, fileMap);
--                }
--                String fileName = warning.file != null ? warning.file.getName() : "<unknown>";
--                Warning canonical = fileMap.get(fileName);
--                if (canonical == null) {
--                    canonical = warning;
--                    fileMap.put(fileName, canonical);
--                    canonical.variants = Sets.newHashSet();
--                    canonical.gradleProject = project;
--                    merged.add(canonical);
--                }
--                canonical.variants.add(variant);
--            }
--        }
--
--        // Clear out variants on any nodes that define all
--        for (Warning warning : merged) {
--            if (warning.variants != null && warning.variants.size() == totalVariantCount) {
--                // If this error is present in all variants, just clear it out
--                warning.variants = null;
--            }
--
--        }
--
--        Collections.sort(merged);
--        return merged;
--    }
--
--    @Override
--    protected void addProgressPrinter() {
--        // No progress printing from the Gradle lint task; gradle tasks
--        // do not really do that, even for long-running jobs.
--    }
--}
-diff --git a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/LintGradleProject.java b/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/LintGradleProject.java
-deleted file mode 100644
-index 8193a8e..0000000
---- a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/LintGradleProject.java
-+++ /dev/null
-@@ -1,658 +0,0 @@
--package com.android.build.gradle.internal;
--
--import static com.android.SdkConstants.APPCOMPAT_LIB_ARTIFACT;
--import static com.android.SdkConstants.SUPPORT_LIB_ARTIFACT;
--import static java.io.File.separatorChar;
--
--import com.android.annotations.NonNull;
--import com.android.annotations.Nullable;
--import com.android.builder.model.AndroidArtifact;
--import com.android.builder.model.AndroidLibrary;
--import com.android.builder.model.AndroidProject;
--import com.android.builder.model.ApiVersion;
--import com.android.builder.model.BuildTypeContainer;
--import com.android.builder.model.Dependencies;
--import com.android.builder.model.JavaLibrary;
--import com.android.builder.model.ProductFlavor;
--import com.android.builder.model.ProductFlavorContainer;
--import com.android.builder.model.SourceProvider;
--import com.android.builder.model.SourceProviderContainer;
--import com.android.builder.model.Variant;
--import com.android.sdklib.AndroidTargetHash;
--import com.android.sdklib.AndroidVersion;
--import com.android.tools.lint.detector.api.LintUtils;
--import com.android.tools.lint.detector.api.Project;
--import com.android.utils.Pair;
--import com.android.utils.XmlUtils;
--import com.google.common.base.Charsets;
--import com.google.common.collect.Lists;
--import com.google.common.collect.Sets;
--import com.google.common.io.Files;
--
--import org.w3c.dom.Document;
--
--import java.io.File;
--import java.io.IOException;
--import java.util.ArrayList;
--import java.util.Collection;
--import java.util.Collections;
--import java.util.List;
--import java.util.Set;
--
--/**
-- * An implementation of Lint's {@link Project} class wrapping a Gradle model (project or
-- * library)
-- */
--public class LintGradleProject extends Project {
--    protected AndroidVersion mMinSdkVersion;
--    protected AndroidVersion mTargetSdkVersion;
--
--    private LintGradleProject(
--            @NonNull LintGradleClient client,
--            @NonNull File dir,
--            @NonNull File referenceDir,
--            @NonNull File manifest) {
--        super(client, dir, referenceDir);
--        mGradleProject = true;
--        mMergeManifests = true;
--        mDirectLibraries = Lists.newArrayList();
--        readManifest(manifest);
--    }
--
--    /**
--     * Creates a {@link com.android.build.gradle.internal.LintGradleProject} from
--     * the given {@link com.android.builder.model.AndroidProject} definition for
--     * a given {@link com.android.builder.model.Variant}, and returns it along with
--     * a set of lint custom rule jars applicable for the given model project.
--     *
--     * @param client the client
--     * @param project the model project
--     * @param variant the variant
--     * @param gradleProject the gradle project
--     * @return a pair of new project and list of custom rule jars
--     */
--    @NonNull
--    public static Pair<LintGradleProject, List<File>> create(
--            @NonNull LintGradleClient client,
--            @NonNull AndroidProject project,
--            @NonNull Variant variant,
--            @NonNull org.gradle.api.Project gradleProject) {
--        File dir = gradleProject.getProjectDir();
--        AppGradleProject lintProject = new AppGradleProject(client, dir,
--                dir, project, variant);
--
--        List<File> customRules = Lists.newArrayList();
--        File appLintJar = new File(gradleProject.getBuildDir(),
--                "lint" + separatorChar + "lint.jar");
--        if (appLintJar.exists()) {
--            customRules.add(appLintJar);
--        }
--
--        Set<AndroidLibrary> libraries = Sets.newHashSet();
--        Dependencies dependencies = variant.getMainArtifact().getDependencies();
--        for (AndroidLibrary library : dependencies.getLibraries()) {
--            lintProject.addDirectLibrary(createLibrary(client, library, libraries, customRules));
--        }
--
--        return Pair.<LintGradleProject,List<File>>of(lintProject, customRules);
--    }
--
--    @Override
--    protected void initialize() {
--        // Deliberately not calling super; that code is for ADT compatibility
--    }
--
--    protected void readManifest(File manifest) {
--        if (manifest.exists()) {
--            try {
--                String xml = Files.toString(manifest, Charsets.UTF_8);
--                Document document = XmlUtils.parseDocumentSilently(xml, true);
--                if (document != null) {
--                    readManifest(document);
--                }
--            } catch (IOException e) {
--                mClient.log(e, "Could not read manifest %1$s", manifest);
--            }
--        }
--    }
--
--    @Override
--    public boolean isGradleProject() {
--        return true;
--    }
--
--    protected static boolean dependsOn(@NonNull Dependencies dependencies,
--            @NonNull String artifact) {
--        for (AndroidLibrary library : dependencies.getLibraries()) {
--            if (dependsOn(library, artifact)) {
--                return true;
--            }
--        }
--        return false;
--    }
--
--    protected static boolean dependsOn(@NonNull AndroidLibrary library, @NonNull String artifact) {
--        if (SUPPORT_LIB_ARTIFACT.equals(artifact)) {
--            if (library.getJarFile().getName().startsWith("support-v4-")) {
--                return true;
--            }
--
--        } else if (APPCOMPAT_LIB_ARTIFACT.equals(artifact)) {
--            File bundle = library.getBundle();
--            if (bundle.getName().startsWith("appcompat-v7-")) {
--                return true;
--            }
--        }
--
--        for (AndroidLibrary dependency : library.getLibraryDependencies()) {
--            if (dependsOn(dependency, artifact)) {
--                return true;
--            }
--        }
--
--        return false;
--    }
--
--    void addDirectLibrary(@NonNull Project project) {
--        mDirectLibraries.add(project);
--    }
--
--    @NonNull
--    private static LibraryProject createLibrary(@NonNull LintGradleClient client,
--            @NonNull AndroidLibrary library,
--            @NonNull Set<AndroidLibrary> seen, List<File> customRules) {
--        seen.add(library);
--        File dir = library.getFolder();
--        LibraryProject project = new LibraryProject(client, dir, dir, library);
--
--        File ruleJar = library.getLintJar();
--        if (ruleJar.exists()) {
--            customRules.add(ruleJar);
--        }
--
--        for (AndroidLibrary dependent : library.getLibraryDependencies()) {
--            if (!seen.contains(dependent)) {
--                project.addDirectLibrary(createLibrary(client, dependent, seen, customRules));
--            }
--        }
--
--        return project;
--    }
--
--    private static class AppGradleProject extends LintGradleProject {
--        private AndroidProject mProject;
--        private Variant mVariant;
--        private List<SourceProvider> mProviders;
--        private List<SourceProvider> mTestProviders;
--
--        private AppGradleProject(
--                @NonNull LintGradleClient client,
--                @NonNull File dir,
--                @NonNull File referenceDir,
--                @NonNull AndroidProject project,
--                @NonNull Variant variant) {
--            //TODO FIXME: handle multi-apk
--            super(client, dir, referenceDir,
--                    variant.getMainArtifact().getOutputs().iterator().next().getGeneratedManifest());
--
--            mProject = project;
--            mVariant = variant;
--        }
--
--        @Override
--        public boolean isLibrary() {
--            return mProject.isLibrary();
--        }
--
--        @Override
--        public AndroidProject getGradleProjectModel() {
--            return mProject;
--        }
--
--        @Override
--        public Variant getCurrentVariant() {
--            return mVariant;
--        }
--
--        private List<SourceProvider> getSourceProviders() {
--            if (mProviders == null) {
--                List<SourceProvider> providers = Lists.newArrayList();
--                AndroidArtifact mainArtifact = mVariant.getMainArtifact();
--
--                providers.add(mProject.getDefaultConfig().getSourceProvider());
--
--                for (String flavorName : mVariant.getProductFlavors()) {
--                    for (ProductFlavorContainer flavor : mProject.getProductFlavors()) {
--                        if (flavorName.equals(flavor.getProductFlavor().getName())) {
--                            providers.add(flavor.getSourceProvider());
--                            break;
--                        }
--                    }
--                }
--
--                SourceProvider multiProvider = mainArtifact.getMultiFlavorSourceProvider();
--                if (multiProvider != null) {
--                    providers.add(multiProvider);
--                }
--
--                String buildTypeName = mVariant.getBuildType();
--                for (BuildTypeContainer buildType : mProject.getBuildTypes()) {
--                    if (buildTypeName.equals(buildType.getBuildType().getName())) {
--                        providers.add(buildType.getSourceProvider());
--                        break;
--                    }
--                }
--
--                SourceProvider variantProvider =  mainArtifact.getVariantSourceProvider();
--                if (variantProvider != null) {
--                    providers.add(variantProvider);
--                }
--
--                mProviders = providers;
--            }
--
--            return mProviders;
--        }
--
--        private List<SourceProvider> getTestSourceProviders() {
--            if (mTestProviders == null) {
--                List<SourceProvider> providers = Lists.newArrayList();
--
--                ProductFlavorContainer defaultConfig = mProject.getDefaultConfig();
--                for (SourceProviderContainer extra : defaultConfig.getExtraSourceProviders()) {
--                    String artifactName = extra.getArtifactName();
--                    if (AndroidProject.ARTIFACT_ANDROID_TEST.equals(artifactName)) {
--                        providers.add(extra.getSourceProvider());
--                    }
--                }
--
--                for (String flavorName : mVariant.getProductFlavors()) {
--                    for (ProductFlavorContainer flavor : mProject.getProductFlavors()) {
--                        if (flavorName.equals(flavor.getProductFlavor().getName())) {
--                            for (SourceProviderContainer extra : flavor.getExtraSourceProviders()) {
--                                String artifactName = extra.getArtifactName();
--                                if (AndroidProject.ARTIFACT_ANDROID_TEST.equals(artifactName)) {
--                                    providers.add(extra.getSourceProvider());
--                                }
--                            }
--                        }
--                    }
--                }
--
--                String buildTypeName = mVariant.getBuildType();
--                for (BuildTypeContainer buildType : mProject.getBuildTypes()) {
--                    if (buildTypeName.equals(buildType.getBuildType().getName())) {
--                        for (SourceProviderContainer extra : buildType.getExtraSourceProviders()) {
--                            String artifactName = extra.getArtifactName();
--                            if (AndroidProject.ARTIFACT_ANDROID_TEST.equals(artifactName)) {
--                                providers.add(extra.getSourceProvider());
--                            }
--                        }
--                    }
--                }
--
--                mTestProviders = providers;
--            }
--
--            return mTestProviders;
--        }
--
--        @NonNull
--        @Override
--        public List<File> getManifestFiles() {
--            if (mManifestFiles == null) {
--                mManifestFiles = Lists.newArrayList();
--                for (SourceProvider provider : getSourceProviders()) {
--                    File manifestFile = provider.getManifestFile();
--                    if (manifestFile.exists()) { // model returns path whether or not it exists
--                        mManifestFiles.add(manifestFile);
--                    }
--                }
--            }
--
--            return mManifestFiles;
--        }
--
--        @NonNull
--        @Override
--        public List<File> getProguardFiles() {
--            if (mProguardFiles == null) {
--                ProductFlavor flavor = mProject.getDefaultConfig().getProductFlavor();
--                mProguardFiles = Lists.newArrayList();
--                for (File file : flavor.getProguardFiles()) {
--                    if (file.exists()) {
--                        mProguardFiles.add(file);
--                    }
--                }
--                try {
--                    for (File file : flavor.getConsumerProguardFiles()) {
--                        if (file.exists()) {
--                            mProguardFiles.add(file);
--                        }
--                    }
--                } catch (Throwable t) {
--                    // On some models, this threw
--                    //   org.gradle.tooling.model.UnsupportedMethodException:
--                    //    Unsupported method: BaseConfig.getConsumerProguardFiles().
--                    // Playing it safe for a while.
--                }
--            }
--
--            return mProguardFiles;
--        }
--
--        @NonNull
--        @Override
--        public List<File> getResourceFolders() {
--            if (mResourceFolders == null) {
--                mResourceFolders = Lists.newArrayList();
--                for (SourceProvider provider : getSourceProviders()) {
--                    Collection<File> resDirs = provider.getResDirectories();
--                    for (File res : resDirs) {
--                        if (res.exists()) { // model returns path whether or not it exists
--                            mResourceFolders.add(res);
--                        }
--                    }
--                }
--
--                for (File file : mVariant.getMainArtifact().getGeneratedResourceFolders()) {
--                    if (file.exists()) {
--                        mResourceFolders.add(file);
--                    }
--                }
--
--            }
--
--            return mResourceFolders;
--        }
--
--        @NonNull
--        @Override
--        public List<File> getJavaSourceFolders() {
--            if (mJavaSourceFolders == null) {
--                mJavaSourceFolders = Lists.newArrayList();
--                for (SourceProvider provider : getSourceProviders()) {
--                    Collection<File> srcDirs = provider.getJavaDirectories();
--                    for (File srcDir : srcDirs) {
--                        if (srcDir.exists()) { // model returns path whether or not it exists
--                            mJavaSourceFolders.add(srcDir);
--                        }
--                    }
--                }
--
--                for (File file : mVariant.getMainArtifact().getGeneratedSourceFolders()) {
--                    if (file.exists()) {
--                        mJavaSourceFolders.add(file);
--                    }
--                }
--            }
--
--            return mJavaSourceFolders;
--        }
--
--        @NonNull
--        @Override
--        public List<File> getTestSourceFolders() {
--            if (mTestSourceFolders == null) {
--                mTestSourceFolders = Lists.newArrayList();
--                for (SourceProvider provider : getTestSourceProviders()) {
--                    Collection<File> srcDirs = provider.getJavaDirectories();
--                    for (File srcDir : srcDirs) {
--                        if (srcDir.exists()) { // model returns path whether or not it exists
--                            mTestSourceFolders.add(srcDir);
--                        }
--                    }
--                }
--            }
--
--            return mTestSourceFolders;
--        }
--
--        @NonNull
--        @Override
--        public List<File> getJavaClassFolders() {
--            if (mJavaClassFolders == null) {
--                mJavaClassFolders = new ArrayList<File>(1);
--                File outputClassFolder = mVariant.getMainArtifact().getClassesFolder();
--                if (outputClassFolder.exists()) {
--                    mJavaClassFolders.add(outputClassFolder);
--                }
--            }
--
--            return mJavaClassFolders;
--        }
--
--        @NonNull
--        @Override
--        public List<File> getJavaLibraries() {
--            if (mJavaLibraries == null) {
--                Collection<JavaLibrary> libs = mVariant.getMainArtifact().getDependencies().getJavaLibraries();
--                mJavaLibraries = Lists.newArrayListWithExpectedSize(libs.size());
--                for (JavaLibrary lib : libs) {
--                    File jar = lib.getJarFile();
--                    if (jar.exists()) {
--                        mJavaLibraries.add(jar);
--                    }
--                }
--            }
--            return mJavaLibraries;
--        }
--
--        @Nullable
--        @Override
--        public String getPackage() {
--            // For now, lint only needs the manifest package; not the potentially variant specific
--            // package. As part of the Gradle work on the Lint API we should make two separate
--            // package lookup methods -- one for the manifest package, one for the build package
--            if (mPackage == null) { // only used as a fallback in case manifest somehow is null
--                String packageName = mProject.getDefaultConfig().getProductFlavor().getApplicationId();
--                if (packageName != null) {
--                    return packageName;
--                }
--            }
--
--            return mPackage; // from manifest
--        }
--
--        @Override
--        @NonNull
--        public AndroidVersion getMinSdkVersion() {
--            if (mMinSdkVersion == null) {
--                ApiVersion minSdk = mVariant.getMergedFlavor().getMinSdkVersion();
--                if (minSdk == null) {
--                    ProductFlavor flavor = mProject.getDefaultConfig().getProductFlavor();
--                    minSdk = flavor.getMinSdkVersion();
--                }
--                if (minSdk != null) {
--                    mMinSdkVersion = LintUtils.convertVersion(minSdk, mClient.getTargets());
--                } else {
--                    mMinSdkVersion = super.getMinSdkVersion(); // from manifest
--                }
--            }
--
--            return mMinSdkVersion;
--        }
--
--        @Override
--        @NonNull
--        public AndroidVersion getTargetSdkVersion() {
--            if (mTargetSdkVersion == null) {
--                ApiVersion targetSdk = mVariant.getMergedFlavor().getTargetSdkVersion();
--                if (targetSdk == null) {
--                    ProductFlavor flavor = mProject.getDefaultConfig().getProductFlavor();
--                    targetSdk = flavor.getTargetSdkVersion();
--                }
--                if (targetSdk != null) {
--                    mTargetSdkVersion = LintUtils.convertVersion(targetSdk, mClient.getTargets());
--                } else {
--                    mTargetSdkVersion = super.getTargetSdkVersion(); // from manifest
--                }
--            }
--
--            return mTargetSdkVersion;
--        }
--
--        @Override
--        public int getBuildSdk() {
--            String compileTarget = mProject.getCompileTarget();
--            AndroidVersion version = AndroidTargetHash.getPlatformVersion(compileTarget);
--            if (version != null) {
--                return version.getApiLevel();
--            }
--
--            return super.getBuildSdk();
--        }
--
--        @Nullable
--        @Override
--        public Boolean dependsOn(@NonNull String artifact) {
--            if (SUPPORT_LIB_ARTIFACT.equals(artifact)) {
--                if (mSupportLib == null) {
--                    Dependencies dependencies = mVariant.getMainArtifact().getDependencies();
--                    mSupportLib = dependsOn(dependencies, artifact);
--                }
--                return mSupportLib;
--            } else if (APPCOMPAT_LIB_ARTIFACT.equals(artifact)) {
--                if (mAppCompat == null) {
--                    Dependencies dependencies = mVariant.getMainArtifact().getDependencies();
--                    mAppCompat = dependsOn(dependencies, artifact);
--                }
--                return mAppCompat;
--            } else {
--                return super.dependsOn(artifact);
--            }
--        }
--    }
--
--    private static class LibraryProject extends LintGradleProject {
--        private AndroidLibrary mLibrary;
--
--        private LibraryProject(
--                @NonNull LintGradleClient client,
--                @NonNull File dir,
--                @NonNull File referenceDir,
--                @NonNull AndroidLibrary library) {
--            super(client, dir, referenceDir, library.getManifest());
--            mLibrary = library;
--
--            // TODO: Make sure we don't use this project for any source library projects!
--            mReportIssues = false;
--        }
--
--        @Override
--        public boolean isLibrary() {
--            return true;
--        }
--
--        @Override
--        public AndroidLibrary getGradleLibraryModel() {
--            return mLibrary;
--        }
--
--        @Override
--        public Variant getCurrentVariant() {
--            return null;
--        }
--
--        @NonNull
--        @Override
--        public List<File> getManifestFiles() {
--            if (mManifestFiles == null) {
--                File manifest = mLibrary.getManifest();
--                if (manifest.exists()) {
--                    mManifestFiles = Collections.singletonList(manifest);
--                } else {
--                    mManifestFiles = Collections.emptyList();
--                }
--            }
--
--            return mManifestFiles;
--        }
--
--        @NonNull
--        @Override
--        public List<File> getProguardFiles() {
--            if (mProguardFiles == null) {
--                File proguardRules = mLibrary.getProguardRules();
--                if (proguardRules.exists()) {
--                    mProguardFiles = Collections.singletonList(proguardRules);
--                } else {
--                    mProguardFiles = Collections.emptyList();
--                }
--            }
--
--            return mProguardFiles;
--        }
--
--        @NonNull
--        @Override
--        public List<File> getResourceFolders() {
--            if (mResourceFolders == null) {
--                File folder = mLibrary.getResFolder();
--                if (folder.exists()) {
--                    mResourceFolders = Collections.singletonList(folder);
--                } else {
--                    mResourceFolders = Collections.emptyList();
--                }
--            }
--
--            return mResourceFolders;
--        }
--
--        @NonNull
--        @Override
--        public List<File> getJavaSourceFolders() {
--            return Collections.emptyList();
--        }
--
--        @NonNull
--        @Override
--        public List<File> getTestSourceFolders() {
--            return Collections.emptyList();
--        }
--
--        @NonNull
--        @Override
--        public List<File> getJavaClassFolders() {
--            return Collections.emptyList();
--        }
--
--        @NonNull
--        @Override
--        public List<File> getJavaLibraries() {
--            if (mJavaLibraries == null) {
--                mJavaLibraries = Lists.newArrayList();
--                File jarFile = mLibrary.getJarFile();
--                if (jarFile.exists()) {
--                    mJavaLibraries.add(jarFile);
--                }
--
--                for (File local : mLibrary.getLocalJars()) {
--                    if (local.exists()) {
--                        mJavaLibraries.add(local);
--                    }
--                }
--            }
--
--            return mJavaLibraries;
--        }
--
--        @Nullable
--        @Override
--        public Boolean dependsOn(@NonNull String artifact) {
--            if (SUPPORT_LIB_ARTIFACT.equals(artifact)) {
--                if (mSupportLib == null) {
--                    mSupportLib = dependsOn(mLibrary, artifact);
--                }
--                return mSupportLib;
--            } else if (APPCOMPAT_LIB_ARTIFACT.equals(artifact)) {
--                if (mAppCompat == null) {
--                    mAppCompat = dependsOn(mLibrary, artifact);
--                }
--                return mAppCompat;
--            } else {
--                return super.dependsOn(artifact);
--            }
--        }
--    }
--}
-diff --git a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/LintGradleRequest.java b/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/LintGradleRequest.java
-deleted file mode 100644
-index 8a13bcb..0000000
---- a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/LintGradleRequest.java
-+++ /dev/null
-@@ -1,69 +0,0 @@
--package com.android.build.gradle.internal;
--
--import com.android.annotations.NonNull;
--import com.android.annotations.Nullable;
--import com.android.builder.model.AndroidProject;
--import com.android.builder.model.Variant;
--import com.android.tools.lint.client.api.LintRequest;
--import com.android.tools.lint.detector.api.Project;
--import com.android.utils.Pair;
--
--import java.io.File;
--import java.util.Collection;
--import java.util.Collections;
--import java.util.List;
--
--class LintGradleRequest extends LintRequest {
--    @NonNull private final LintGradleClient mLintClient;
--    @NonNull private final org.gradle.api.Project mGradleProject;
--    @Nullable private final String mVariantName;
--    @NonNull private final AndroidProject mModelProject;
--
--    public LintGradleRequest(
--            @NonNull LintGradleClient client,
--            @NonNull AndroidProject modelProject,
--            @NonNull org.gradle.api.Project gradleProject,
--            @Nullable String variantName,
--            @NonNull List<File> files) {
--        super(client, files);
--        mLintClient = client;
--        mModelProject = modelProject;
--        mGradleProject = gradleProject;
--        mVariantName = variantName;
--    }
--
--    @Nullable
--    @Override
--    public Collection<Project> getProjects() {
--        if (mProjects == null) {
--            Variant variant = findVariant(mModelProject, mVariantName);
--            if (variant == null) {
--                mProjects = Collections.emptyList();
--                return mProjects;
--            }
--            Pair<LintGradleProject,List<File>> result = LintGradleProject.create(
--                    mLintClient, mModelProject, variant, mGradleProject);
--            mProjects = Collections.<Project>singletonList(result.getFirst());
--            mLintClient.setCustomRules(result.getSecond());
--        }
--
--        return mProjects;
--    }
--
--    private static Variant findVariant(@NonNull AndroidProject project,
--            @Nullable String variantName) {
--        if (variantName != null) {
--            for (Variant variant : project.getVariants()) {
--                if (variantName.equals(variant.getName())) {
--                    return variant;
--                }
--            }
--        }
--
--        if (!project.getVariants().isEmpty()) {
--            return project.getVariants().iterator().next();
--        }
--
--        return null;
--    }
--}
-diff --git a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/TaskManager.java b/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/TaskManager.java
-index 4c646b5..0db3585 100644
---- a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/TaskManager.java
-+++ b/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/TaskManager.java
-@@ -96,7 +96,6 @@ import com.android.build.gradle.tasks.GenerateSplitAbiRes;
- import com.android.build.gradle.tasks.JackTask;
- import com.android.build.gradle.tasks.JavaResourcesProvider;
- import com.android.build.gradle.tasks.JillTask;
--import com.android.build.gradle.tasks.Lint;
- import com.android.build.gradle.tasks.MergeAssets;
- import com.android.build.gradle.tasks.MergeManifests;
- import com.android.build.gradle.tasks.MergeResources;
-@@ -109,7 +108,7 @@ import com.android.build.gradle.tasks.ProcessAndroidResources;
- import com.android.build.gradle.tasks.ProcessManifest;
- import com.android.build.gradle.tasks.ProcessTestManifest;
- import com.android.build.gradle.tasks.RenderscriptCompile;
--import com.android.build.gradle.tasks.ShrinkResources;
-+//import com.android.build.gradle.tasks.ShrinkResources;
- import com.android.build.gradle.tasks.SplitZipAlign;
- import com.android.build.gradle.tasks.ZipAlign;
- import com.android.build.gradle.tasks.factory.JavaCompileConfigAction;
-@@ -368,24 +367,12 @@ public abstract class TaskManager {
-             }
-         });
- 
--        tasks.create(LINT, Lint.class, new Action<Lint>() {
--            @Override
--            public void execute(Lint lintTask) {
--                lintTask.setDescription("Runs lint on all variants.");
--                lintTask.setVariantName("");
--                lintTask.setGroup(JavaBasePlugin.VERIFICATION_GROUP);
--                lintTask.setLintOptions(getExtension().getLintOptions());
--                lintTask.setSdkHome(sdkHandler.getSdkFolder());
--                lintTask.setToolingRegistry(toolingRegistry);
--            }
--        });
-         tasks.named(JavaBasePlugin.CHECK_TASK_NAME, new Action<Task>() {
-             @Override
-             public void execute(Task it) {
-                 it.dependsOn(LINT);
-             }
-         });
--        createLintCompileTask(tasks);
-     }
- 
-     public void createMockableJarTask() {
-@@ -1166,100 +1153,21 @@ public abstract class TaskManager {
-     }
- 
-     // TODO - should compile src/lint/java from src/lint/java and jar it into build/lint/lint.jar
--    private void createLintCompileTask(TaskFactory tasks) {
--
--        // TODO: move doFirst into dedicated task class.
--        tasks.create(LINT_COMPILE, Task.class,
--                new Action<Task>() {
--                    @Override
--                    public void execute(Task lintCompile) {
--                        final File outputDir =
--                                new File(getGlobalScope().getIntermediatesDir(), "lint");
--
--                        lintCompile.doFirst(new Action<Task>() {
--                            @Override
--                            public void execute(Task task) {
--                                // create the directory for lint output if it does not exist.
--                                if (!outputDir.exists()) {
--                                    boolean mkdirs = outputDir.mkdirs();
--                                    if (!mkdirs) {
--                                        throw new GradleException(
--                                                "Unable to create lint output directory.");
--                                    }
--                                }
--                            }
--                        });
--                    }
--                });
--    }
- 
-     /**
-      * Is the given variant relevant for lint?
-      */
--    private static boolean isLintVariant(
--            @NonNull BaseVariantData<? extends BaseVariantOutputData> baseVariantData) {
--        // Only create lint targets for variants like debug and release, not debugTest
--        VariantConfiguration config = baseVariantData.getVariantConfiguration();
--        return !config.getType().isForTesting();
--    }
- 
-     /**
-      * Add tasks for running lint on individual variants. We've already added a
-      * lint task earlier which runs on all variants.
-      */
--    public void createLintTasks(TaskFactory tasks, final VariantScope scope) {
--        final BaseVariantData<? extends BaseVariantOutputData> baseVariantData =
--                scope.getVariantData();
--        if (!isLintVariant(baseVariantData)) {
--            return;
--        }
- 
-         // wire the main lint task dependency.
--        tasks.named(LINT, new Action<Task>() {
--            @Override
--            public void execute(Task it) {
--                it.dependsOn(LINT_COMPILE);
--                it.dependsOn(scope.getJavacTask().getName());
--            }
--        });
--
--        AndroidTask<Lint> variantLintCheck = androidTasks.create(
--                tasks, new Lint.ConfigAction(scope));
--        variantLintCheck.dependsOn(tasks, LINT_COMPILE, scope.getJavacTask());
--    }
- 
--    private void createLintVitalTask(@NonNull ApkVariantData variantData) {
--        checkState(getExtension().getLintOptions().isCheckReleaseBuilds());
--        // TODO: re-enable with Jack when possible
--        if (!variantData.getVariantConfiguration().getBuildType().isDebuggable() &&
--                !variantData.getVariantConfiguration().getUseJack()) {
--            String variantName = variantData.getVariantConfiguration().getFullName();
--            String capitalizedVariantName = StringHelper.capitalize(variantName);
--            String taskName = "lintVital" + capitalizedVariantName;
--            final Lint lintReleaseCheck = project.getTasks().create(taskName, Lint.class);
--            // TODO: Make this task depend on lintCompile too (resolve initialization order first)
--            optionalDependsOn(lintReleaseCheck, variantData.javacTask);
--            lintReleaseCheck.setLintOptions(getExtension().getLintOptions());
--            lintReleaseCheck.setSdkHome(sdkHandler.getSdkFolder());
--            lintReleaseCheck.setVariantName(variantName);
--            lintReleaseCheck.setToolingRegistry(toolingRegistry);
--            lintReleaseCheck.setFatalOnly(true);
--            lintReleaseCheck.setDescription(
--                    "Runs lint on just the fatal issues in the " + capitalizedVariantName
--                            + " build.");
--            //variantData.assembleVariantTask.dependsOn lintReleaseCheck
- 
-             // If lint is being run, we do not need to run lint vital.
-             // TODO: Find a better way to do this.
--            project.getGradle().getTaskGraph().whenReady(new Closure<Void>(this, this) {
--                public void doCall(TaskExecutionGraph taskGraph) {
--                    if (taskGraph.hasTask(LINT)) {
--                        lintReleaseCheck.setEnabled(false);
--                    }
--                }
--            });
--        }
--    }
- 
-     private void createUnitTestTask(@NonNull TaskFactory tasks,
-             @NonNull final TestVariantData variantData) {
-@@ -1981,16 +1889,6 @@ public abstract class TaskManager {
-             // we insert the ShrinkResources task into the chain, such that its
-             // input is the ProcessAndroidResources packageOutputFile, and its
-             // output is what the PackageApplication task reads.
--            AndroidTask<ShrinkResources> shrinkTask = null;
--
--            if (config.isMinifyEnabled() && config.getBuildType().isShrinkResources() &&
--                    !config.getUseJack()) {
--                shrinkTask = androidTasks.create(
--                        tasks, new ShrinkResources.ConfigAction(variantOutputScope));
--                shrinkTask.dependsOn(tasks, variantScope.getObfuscationTask(),
--                        variantOutputScope.getManifestProcessorTask(),
--                        variantOutputScope.getProcessResourcesTask());
--            }
- 
-             AndroidTask<PackageApplication> packageApp = androidTasks.create(
-                     tasks, new PackageApplication.ConfigAction(variantOutputScope));
-@@ -2001,7 +1899,6 @@ public abstract class TaskManager {
- 
-             packageApp.optionalDependsOn(
-                     tasks,
--                    shrinkTask,
-                     // TODO: When Jack is converted, add activeDexTask to VariantScope.
-                     variantOutputScope.getVariantScope().getDexTask(),
-                     variantOutputScope.getVariantScope().getJavaCompilerTask(),
-@@ -2157,9 +2054,6 @@ public abstract class TaskManager {
-             installTask.dependsOn(tasks, variantData.assembleVariantTask);
-         }
- 
--        if (getExtension().getLintOptions().isCheckReleaseBuilds()) {
--            createLintVitalTask(variantData);
--        }
- 
-         // add an uninstall task
-         final AndroidTask<UninstallTask> uninstallTask = androidTasks.create(
-diff --git a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/LintOptions.java b/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/LintOptions.java
-deleted file mode 100644
-index eff135e..0000000
---- a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/LintOptions.java
-+++ /dev/null
-@@ -1,798 +0,0 @@
--/*
-- * Copyright (C) 2013 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.build.gradle.internal.dsl;
--
--import static com.android.SdkConstants.DOT_XML;
--import static com.android.builder.model.AndroidProject.FD_OUTPUTS;
--import static com.android.tools.lint.detector.api.Severity.ERROR;
--import static com.android.tools.lint.detector.api.Severity.FATAL;
--import static com.android.tools.lint.detector.api.Severity.IGNORE;
--import static com.android.tools.lint.detector.api.Severity.INFORMATIONAL;
--import static com.android.tools.lint.detector.api.Severity.WARNING;
--
--import com.android.annotations.NonNull;
--import com.android.annotations.Nullable;
--import com.android.tools.lint.HtmlReporter;
--import com.android.tools.lint.LintCliClient;
--import com.android.tools.lint.LintCliFlags;
--import com.android.tools.lint.TextReporter;
--import com.android.tools.lint.XmlReporter;
--import com.android.tools.lint.checks.BuiltinIssueRegistry;
--import com.android.tools.lint.detector.api.Issue;
--import com.android.tools.lint.detector.api.Severity;
--import com.google.common.collect.Maps;
--import com.google.common.collect.Sets;
--
--import org.gradle.api.GradleException;
--import org.gradle.api.tasks.Input;
--import org.gradle.api.tasks.InputFile;
--import org.gradle.api.tasks.Optional;
--import org.gradle.api.tasks.OutputFile;
--
--import java.io.BufferedWriter;
--import java.io.File;
--import java.io.FileWriter;
--import java.io.IOException;
--import java.io.PrintWriter;
--import java.io.Serializable;
--import java.io.Writer;
--import java.util.Map;
--import java.util.Set;
--
--/**
-- * DSL object for configuring lint options.
-- */
--public class LintOptions implements com.android.builder.model.LintOptions, Serializable {
--    public static final String STDOUT = "stdout";
--    public static final String STDERR = "stderr";
--    private static final long serialVersionUID = 1L;
--
--    @NonNull
--    private Set<String> disable = Sets.newHashSet();
--    @NonNull
--    private Set<String> enable = Sets.newHashSet();
--    @Nullable
--    private Set<String> check = Sets.newHashSet();
--    private boolean abortOnError = true;
--    private boolean absolutePaths = true;
--    private boolean noLines;
--    private boolean quiet;
--    private boolean checkAllWarnings;
--    private boolean ignoreWarnings;
--    private boolean warningsAsErrors;
--    private boolean showAll;
--    private boolean checkReleaseBuilds = true;
--    private boolean explainIssues = true;
--    @Nullable
--    private File lintConfig;
--    private boolean textReport;
--    @Nullable
--    private File textOutput;
--    private boolean htmlReport = true;
--    @Nullable
--    private File htmlOutput;
--    private boolean xmlReport = true;
--    @Nullable
--    private File xmlOutput;
--
--    private Map<String,Severity> severities = Maps.newHashMap();
--
--    public LintOptions() {
--    }
--
--    public LintOptions(
--            @NonNull Set<String> disable,
--            @NonNull Set<String> enable,
--            @Nullable Set<String> check,
--            @Nullable File lintConfig,
--            boolean textReport,
--            @Nullable File textOutput,
--            boolean htmlReport,
--            @Nullable File htmlOutput,
--            boolean xmlReport,
--            @Nullable File xmlOutput,
--            boolean abortOnError,
--            boolean absolutePaths,
--            boolean noLines,
--            boolean quiet,
--            boolean checkAllWarnings,
--            boolean ignoreWarnings,
--            boolean warningsAsErrors,
--            boolean showAll,
--            boolean explainIssues,
--            boolean checkReleaseBuilds,
--            @Nullable Map<String,Integer> severityOverrides) {
--        this.disable = disable;
--        this.enable = enable;
--        this.check = check;
--        this.lintConfig = lintConfig;
--        this.textReport = textReport;
--        this.textOutput = textOutput;
--        this.htmlReport = htmlReport;
--        this.htmlOutput = htmlOutput;
--        this.xmlReport = xmlReport;
--        this.xmlOutput = xmlOutput;
--        this.abortOnError = abortOnError;
--        this.absolutePaths = absolutePaths;
--        this.noLines = noLines;
--        this.quiet = quiet;
--        this.checkAllWarnings = checkAllWarnings;
--        this.ignoreWarnings = ignoreWarnings;
--        this.warningsAsErrors = warningsAsErrors;
--        this.showAll = showAll;
--        this.explainIssues = explainIssues;
--        this.checkReleaseBuilds = checkReleaseBuilds;
--
--        if (severityOverrides != null) {
--            for (Map.Entry<String,Integer> entry : severityOverrides.entrySet()) {
--                severities.put(entry.getKey(), convert(entry.getValue()));
--            }
--        }
--    }
--
--    @NonNull
--    public static com.android.builder.model.LintOptions create(@NonNull com.android.builder.model.LintOptions source) {
--        return new LintOptions(
--                source.getDisable(),
--                source.getEnable(),
--                source.getCheck(),
--                source.getLintConfig(),
--                source.getTextReport(),
--                source.getTextOutput(),
--                source.getHtmlReport(),
--                source.getHtmlOutput(),
--                source.getXmlReport(),
--                source.getXmlOutput(),
--                source.isAbortOnError(),
--                source.isAbsolutePaths(),
--                source.isNoLines(),
--                source.isQuiet(),
--                source.isCheckAllWarnings(),
--                source.isIgnoreWarnings(),
--                source.isWarningsAsErrors(),
--                source.isShowAll(),
--                source.isExplainIssues(),
--                source.isCheckReleaseBuilds(),
--                source.getSeverityOverrides()
--        );
--    }
--
--    /**
--     * Returns the set of issue id's to suppress. Callers are allowed to modify this collection.
--     */
--    @Override
--    @NonNull
--    @Input
--    public Set<String> getDisable() {
--        return disable;
--    }
--
--    /**
--     * Sets the set of issue id's to suppress. Callers are allowed to modify this collection.
--     * Note that these ids add to rather than replace the given set of ids.
--     */
--    public void setDisable(@Nullable Set<String> ids) {
--        disable.addAll(ids);
--    }
--
--    /**
--     * Returns the set of issue id's to enable. Callers are allowed to modify this collection.
--     * To enable a given issue, add the issue ID to the returned set.
--     */
--    @Override
--    @NonNull
--    @Input
--    public Set<String> getEnable() {
--        return enable;
--    }
--
--    /**
--     * Sets the set of issue id's to enable. Callers are allowed to modify this collection.
--     * Note that these ids add to rather than replace the given set of ids.
--     */
--    public void setEnable(@Nullable Set<String> ids) {
--        enable.addAll(ids);
--    }
--
--    /**
--     * Returns the exact set of issues to check, or null to run the issues that are enabled
--     * by default plus any issues enabled via {@link #getEnable} and without issues disabled
--     * via {@link #getDisable}. If non-null, callers are allowed to modify this collection.
--     */
--    @Override
--    @Nullable
--    @Optional
--    @Input
--    public Set<String> getCheck() {
--        return check;
--    }
--
--    /**
--     * Sets the <b>exact</b> set of issues to check.
--     * @param ids the set of issue id's to check
--     */
--    public void setCheck(@NonNull Set<String> ids) {
--        check.addAll(ids);
--    }
--
--    /** Whether lint should set the exit code of the process if errors are found */
--    @Override
--    @Input
--    public boolean isAbortOnError() {
--        return abortOnError;
--    }
--
--    /** Sets whether lint should set the exit code of the process if errors are found */
--    public void setAbortOnError(boolean abortOnError) {
--        this.abortOnError = abortOnError;
--    }
--
--    /**
--     * Whether lint should display full paths in the error output. By default the paths
--     * are relative to the path lint was invoked from.
--     */
--    @Override
--    @Input
--    public boolean isAbsolutePaths() {
--        return absolutePaths;
--    }
--
--    /**
--     * Sets whether lint should display full paths in the error output. By default the paths
--     * are relative to the path lint was invoked from.
--     */
--    public void setAbsolutePaths(boolean absolutePaths) {
--        this.absolutePaths = absolutePaths;
--    }
--
--    /**
--     * Whether lint should include the source lines in the output where errors occurred
--     * (true by default)
--     */
--    @Override
--    @Input
--    public boolean isNoLines() {
--        return this.noLines;
--    }
--
--    /**
--     * Sets whether lint should include the source lines in the output where errors occurred
--     * (true by default)
--     */
--    public void setNoLines(boolean noLines) {
--        this.noLines = noLines;
--    }
--
--    /**
--     * Returns whether lint should be quiet (for example, not write informational messages
--     * such as paths to report files written)
--     */
--    @Override
--    @Input
--    public boolean isQuiet() {
--        return quiet;
--    }
--
--    /**
--     * Sets whether lint should be quiet (for example, not write informational messages
--     * such as paths to report files written)
--     */
--    public void setQuiet(boolean quiet) {
--        this.quiet = quiet;
--    }
--
--    /** Returns whether lint should check all warnings, including those off by default */
--    @Override
--    @Input
--    public boolean isCheckAllWarnings() {
--        return checkAllWarnings;
--    }
--
--    /** Sets whether lint should check all warnings, including those off by default */
--    public void setCheckAllWarnings(boolean warnAll) {
--        this.checkAllWarnings = warnAll;
--    }
--
--    /** Returns whether lint will only check for errors (ignoring warnings) */
--    @Override
--    @Input
--    public boolean isIgnoreWarnings() {
--        return ignoreWarnings;
--    }
--
--    /** Sets whether lint will only check for errors (ignoring warnings) */
--    public void setIgnoreWarnings(boolean noWarnings) {
--        this.ignoreWarnings = noWarnings;
--    }
--
--    /** Returns whether lint should treat all warnings as errors */
--    @Override
--    @Input
--    public boolean isWarningsAsErrors() {
--        return warningsAsErrors;
--    }
--
--    /** Sets whether lint should treat all warnings as errors */
--    public void setWarningsAsErrors(boolean allErrors) {
--        this.warningsAsErrors = allErrors;
--    }
--
--    /** Returns whether lint should include explanations for issue errors. (Note that
--     * HTML and XML reports intentionally do this unconditionally, ignoring this setting.) */
--    @Override
--    @Input
--    public boolean isExplainIssues() {
--        return explainIssues;
--    }
--
--    public void setExplainIssues(boolean explainIssues) {
--        this.explainIssues = explainIssues;
--    }
--
--    /**
--     * Returns whether lint should include all output (e.g. include all alternate
--     * locations, not truncating long messages, etc.)
--     */
--    @Override
--    @Input
--    public boolean isShowAll() {
--        return showAll;
--    }
--
--    /**
--     * Sets whether lint should include all output (e.g. include all alternate
--     * locations, not truncating long messages, etc.)
--     */
--    public void setShowAll(boolean showAll) {
--        this.showAll = showAll;
--    }
--
--    /**
--     * Returns whether lint should check for fatal errors during release builds. Default is true.
--     * If issues with severity "fatal" are found, the release build is aborted.
--     */
--    @Override
--    @Input
--    public boolean isCheckReleaseBuilds() {
--        return checkReleaseBuilds;
--    }
--
--    public void setCheckReleaseBuilds(boolean checkReleaseBuilds) {
--        this.checkReleaseBuilds = checkReleaseBuilds;
--    }
--
--    /**
--     * Returns the default configuration file to use as a fallback
--     */
--    @Override
--    @Optional @InputFile
--    public File getLintConfig() {
--        return lintConfig;
--    }
--
--    /** Whether we should write an text report. Default false. The location can be
--     * controlled by {@link #getTextOutput()}. */
--    @Override
--    @Input
--    public boolean getTextReport() {
--        return textReport;
--    }
--
--    public void setTextReport(boolean textReport) {
--        this.textReport = textReport;
--    }
--
--    public void setHtmlReport(boolean htmlReport) {
--        this.htmlReport = htmlReport;
--    }
--
--    public void setHtmlOutput(@NonNull File htmlOutput) {
--        this.htmlOutput = htmlOutput;
--    }
--
--    public void setXmlReport(boolean xmlReport) {
--        this.xmlReport = xmlReport;
--    }
--
--    public void setXmlOutput(@NonNull File xmlOutput) {
--        this.xmlOutput = xmlOutput;
--    }
--
--    /**
--     * The optional path to where a text report should be written. The special value
--     * "stdout" can be used to point to standard output.
--     */
--    @Override
--    @Nullable
--    @Optional
--    @Input
--    public File getTextOutput() {
--        return textOutput;
--    }
--
--    /** Whether we should write an HTML report. Default true. The location can be
--     * controlled by {@link #getHtmlOutput()}. */
--    @Override
--    @Input
--    public boolean getHtmlReport() {
--        return htmlReport;
--    }
--
--    /** The optional path to where an HTML report should be written */
--    @Override
--    @Nullable
--    @Optional
--    @OutputFile
--    public File getHtmlOutput() {
--        return htmlOutput;
--    }
--
--    /** Whether we should write an XML report. Default true. The location can be
--     * controlled by {@link #getXmlOutput()}. */
--    @Override
--    @Input
--    public boolean getXmlReport() {
--        return xmlReport;
--    }
--
--    /** The optional path to where an XML report should be written */
--    @Override
--    @Nullable
--    @Optional
--    @OutputFile
--    public File getXmlOutput() {
--        return xmlOutput;
--    }
--
--    /**
--     * Sets the default config file to use as a fallback. This corresponds to a {@code lint.xml}
--     * file with severities etc to use when a project does not have more specific information.
--     */
--    public void setLintConfig(@NonNull File lintConfig) {
--        this.lintConfig = lintConfig;
--    }
--
--    public void syncTo(
--            @NonNull LintCliClient client,
--            @NonNull LintCliFlags flags,
--            @Nullable String variantName,
--            @Nullable org.gradle.api.Project project,
--            boolean report) {
--        if (disable != null) {
--            flags.getSuppressedIds().addAll(disable);
--        }
--        if (enable != null) {
--            flags.getEnabledIds().addAll(enable);
--        }
--        if (check != null && !check.isEmpty()) {
--            flags.setExactCheckedIds(check);
--        }
--        flags.setSetExitCode(this.abortOnError);
--        flags.setFullPath(absolutePaths);
--        flags.setShowSourceLines(!noLines);
--        flags.setQuiet(quiet);
--        flags.setCheckAllWarnings(checkAllWarnings);
--        flags.setIgnoreWarnings(ignoreWarnings);
--        flags.setWarningsAsErrors(warningsAsErrors);
--        flags.setShowEverything(showAll);
--        flags.setDefaultConfiguration(lintConfig);
--        flags.setSeverityOverrides(severities);
--        flags.setExplainIssues(explainIssues);
--
--        if (report || flags.isFatalOnly() && this.abortOnError) {
--            if (textReport || flags.isFatalOnly()) {
--                File output = textOutput;
--                if (output == null) {
--                    output = new File(flags.isFatalOnly() ? STDERR: STDOUT);
--                } else if (!output.isAbsolute() && !isStdOut(output) && !isStdErr(output)) {
--                    output = project.file(output.getPath());
--                }
--                output = validateOutputFile(output);
--
--                Writer writer;
--                File file = null;
--                boolean closeWriter;
--                if (isStdOut(output)) {
--                    writer = new PrintWriter(System.out, true);
--                    closeWriter = false;
--                } else if (isStdErr(output)) {
--                    writer = new PrintWriter(System.err, true);
--                    closeWriter = false;
--                } else {
--                    file = output;
--                    try {
--                        writer = new BufferedWriter(new FileWriter(output));
--                    } catch (IOException e) {
--                        throw new org.gradle.api.GradleException("Text invalid argument.", e);
--                    }
--                    closeWriter = true;
--                }
--                flags.getReporters().add(new TextReporter(client, flags, file, writer,
--                        closeWriter));
--            }
--            if (htmlReport) {
--                File output = htmlOutput;
--                if (output == null || flags.isFatalOnly()) {
--                    output = createOutputPath(project, variantName, ".html", flags.isFatalOnly());
--                } else if (!output.isAbsolute()) {
--                    output = project.file(output.getPath());
--                }
--                output = validateOutputFile(output);
--                try {
--                    flags.getReporters().add(new HtmlReporter(client, output));
--                } catch (IOException e) {
--                    throw new GradleException("HTML invalid argument.", e);
--                }
--            }
--            if (xmlReport) {
--                File output = xmlOutput;
--                if (output == null || flags.isFatalOnly()) {
--                    output = createOutputPath(project, variantName, DOT_XML, flags.isFatalOnly());
--                } else if (!output.isAbsolute()) {
--                    output = project.file(output.getPath());
--                }
--                output = validateOutputFile(output);
--                try {
--                    flags.getReporters().add(new XmlReporter(client, output));
--                } catch (IOException e) {
--                    throw new org.gradle.api.GradleException("XML invalid argument.", e);
--                }
--            }
--        }
--    }
--
--    private static boolean isStdOut(@NonNull File output) {
--        return STDOUT.equals(output.getPath());
--    }
--
--    private static boolean isStdErr(@NonNull File output) {
--        return STDERR.equals(output.getPath());
--    }
--
--    @NonNull
--    private static File validateOutputFile(@NonNull File output) {
--        if (isStdOut(output) || isStdErr(output)) {
--            return output;
--        }
--
--        File parent = output.getParentFile();
--        if (!parent.exists()) {
--            parent.mkdirs();
--        }
--
--        output = output.getAbsoluteFile();
--        if (output.exists()) {
--            boolean delete = output.delete();
--            if (!delete) {
--                throw new org.gradle.api.GradleException("Could not delete old " + output);
--            }
--        }
--        if (output.getParentFile() != null && !output.getParentFile().canWrite()) {
--            throw new org.gradle.api.GradleException("Cannot write output file " + output);
--        }
--
--        return output;
--    }
--
--    private static File createOutputPath(
--            @NonNull org.gradle.api.Project project,
--            @NonNull String variantName,
--            @NonNull String extension,
--            boolean fatalOnly) {
--        StringBuilder base = new StringBuilder();
--        base.append(FD_OUTPUTS);
--        base.append(File.separator);
--        base.append("lint-results");
--        if (variantName != null) {
--            base.append("-");
--            base.append(variantName);
--        }
--        if (fatalOnly) {
--            base.append("-fatal");
--        }
--        base.append(extension);
--        return new File(project.getBuildDir(), base.toString());
--    }
--
--    /**
--     * An optional map of severity overrides. The map maps from issue id's to the corresponding
--     * severity to use, which must be "fatal", "error", "warning", or "ignore".
--     *
--     * @return a map of severity overrides, or null. The severities are one of the constants
--     *  {@link #SEVERITY_FATAL}, {@link #SEVERITY_ERROR}, {@link #SEVERITY_WARNING},
--     *  {@link #SEVERITY_INFORMATIONAL}, {@link #SEVERITY_IGNORE}
--     */
--    @Override
--    @Nullable
--    public Map<String, Integer> getSeverityOverrides() {
--        if (severities == null || severities.isEmpty()) {
--            return null;
--        }
--
--        Map<String, Integer> map =
--                Maps.newHashMapWithExpectedSize(severities.size());
--        for (Map.Entry<String,Severity> entry : severities.entrySet()) {
--            map.put(entry.getKey(), convert(entry.getValue()));
--        }
--
--        return map;
--    }
--
--    // -- DSL Methods.
--
--    /**
--     * Adds the id to the set of issues to check.
--     */
--    public void check(String id) {
--        check.add(id);
--    }
--
--    /**
--     * Adds the ids to the set of issues to check.
--     */
--    public void check(String... ids) {
--        for (String id : ids) {
--            check(id);
--        }
--    }
--
--    /**
--     * Adds the id to the set of issues to enable.
--     */
--    public void enable(String id) {
--        enable.add(id);
--        Issue issue = new BuiltinIssueRegistry().getIssue(id);
--        severities.put(id, issue != null ? issue.getDefaultSeverity() : WARNING);
--    }
--
--    /**
--     * Adds the ids to the set of issues to enable.
--     */
--    public void enable(String... ids) {
--        for (String id : ids) {
--            enable(id);
--        }
--    }
--
--    /**
--     * Adds the id to the set of issues to enable.
--     */
--    public void disable(String id) {
--        disable.add(id);
--        severities.put(id, IGNORE);
--    }
--
--    /**
--     * Adds the ids to the set of issues to enable.
--     */
--    public void disable(String... ids) {
--        for (String id : ids) {
--            disable(id);
--        }
--    }
--
--    // For textOutput 'stdout' or 'stderr' (normally a file)
--    public void textOutput(String textOutput) {
--        this.textOutput = new File(textOutput);
--    }
--
--    // For textOutput file()
--    public void textOutput(File textOutput) {
--        this.textOutput = textOutput;
--    }
--
--    /**
--     * Adds a severity override for the given issues.
--     */
--    public void fatal(String id) {
--        severities.put(id, FATAL);
--    }
--
--    /**
--     * Adds a severity override for the given issues.
--     */
--    public void fatal(String... ids) {
--        for (String id : ids) {
--            fatal(id);
--        }
--    }
--
--    /**
--     * Adds a severity override for the given issues.
--     */
--    public void error(String id) {
--        severities.put(id, ERROR);
--    }
--
--    /**
--     * Adds a severity override for the given issues.
--     */
--    public void error(String... ids) {
--        for (String id : ids) {
--            error(id);
--        }
--    }
--
--    /**
--     * Adds a severity override for the given issues.
--     */
--    public void warning(String id) {
--        severities.put(id, WARNING);
--    }
--
--    /**
--     * Adds a severity override for the given issues.
--     */
--    public void warning(String... ids) {
--        for (String id : ids) {
--            warning(id);
--        }
--    }
--
--    /**
--     * Adds a severity override for the given issues.
--     */
--    public void ignore(String id) {
--        severities.put(id, IGNORE);
--    }
--
--    /**
--     * Adds a severity override for the given issues.
--     */
--    public void ignore(String... ids) {
--        for (String id : ids) {
--            ignore(id);
--        }
--    }
--
--    // Without these qualifiers, Groovy compilation will fail with "Apparent variable
--    // 'SEVERITY_FATAL' was found in a static scope but doesn't refer to a local variable,
--    // static field or class"
--    //@SuppressWarnings("UnnecessaryQualifiedReference")
--    private static int convert(Severity s) {
--        switch (s) {
--            case FATAL:
--                return com.android.builder.model.LintOptions.SEVERITY_FATAL;
--            case ERROR:
--                return com.android.builder.model.LintOptions.SEVERITY_ERROR;
--            case WARNING:
--                return com.android.builder.model.LintOptions.SEVERITY_WARNING;
--            case INFORMATIONAL:
--                return com.android.builder.model.LintOptions.SEVERITY_INFORMATIONAL;
--            case IGNORE:
--            default:
--                return com.android.builder.model.LintOptions.SEVERITY_IGNORE;
--        }
--    }
--
--    //@SuppressWarnings("UnnecessaryQualifiedReference")
--    private static Severity convert(int s) {
--        switch (s) {
--            case com.android.builder.model.LintOptions.SEVERITY_FATAL:
--                return FATAL;
--            case com.android.builder.model.LintOptions.SEVERITY_ERROR:
--                return ERROR;
--            case com.android.builder.model.LintOptions.SEVERITY_WARNING:
--                return WARNING;
--            case com.android.builder.model.LintOptions.SEVERITY_INFORMATIONAL:
--                return INFORMATIONAL;
--            case com.android.builder.model.LintOptions.SEVERITY_IGNORE:
--            default:
--                return IGNORE;
--        }
--    }
--}
-diff --git a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/DefaultAndroidProject.java b/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/DefaultAndroidProject.java
-index 4528821..6224833 100644
---- a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/DefaultAndroidProject.java
-+++ b/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/DefaultAndroidProject.java
-@@ -24,7 +24,6 @@ import com.android.builder.model.AndroidProject;
- import com.android.builder.model.ArtifactMetaData;
- import com.android.builder.model.BuildTypeContainer;
- import com.android.builder.model.JavaCompileOptions;
--import com.android.builder.model.LintOptions;
- import com.android.builder.model.ProductFlavorContainer;
- import com.android.builder.model.SigningConfig;
- import com.android.builder.model.SyncIssue;
-@@ -65,8 +64,6 @@ class DefaultAndroidProject implements AndroidProject, Serializable {
-     @NonNull
-     private final JavaCompileOptions javaCompileOptions;
-     @NonNull
--    private final LintOptions lintOptions;
--    @NonNull
-     private final File buildFolder;
-     @Nullable
-     private final String resourcePrefix;
-@@ -97,7 +94,6 @@ class DefaultAndroidProject implements AndroidProject, Serializable {
-             @NonNull Collection<String> unresolvedDependencies,
-             @NonNull Collection<SyncIssue> syncIssues,
-             @NonNull CompileOptions compileOptions,
--            @NonNull LintOptions lintOptions,
-             @NonNull File buildFolder,
-             @Nullable String resourcePrefix,
-             @NonNull Collection<NativeToolchain> nativeToolchains,
-@@ -115,7 +111,6 @@ class DefaultAndroidProject implements AndroidProject, Serializable {
-         this.unresolvedDependencies = unresolvedDependencies;
-         this.syncIssues = syncIssues;
-         javaCompileOptions = new DefaultJavaCompileOptions(compileOptions);
--        this.lintOptions = lintOptions;
-         this.buildFolder = buildFolder;
-         this.resourcePrefix = resourcePrefix;
-         this.isLibrary = isLibrary;
-@@ -238,12 +233,6 @@ class DefaultAndroidProject implements AndroidProject, Serializable {
- 
-     @Override
-     @NonNull
--    public LintOptions getLintOptions() {
--        return lintOptions;
--    }
--
--    @Override
--    @NonNull
-     public Collection<String> getUnresolvedDependencies() {
-         return unresolvedDependencies;
-     }
-diff --git a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/ModelBuilder.java b/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/ModelBuilder.java
-index b6e18ee..4cb5424 100644
---- a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/ModelBuilder.java
-+++ b/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/ModelBuilder.java
-@@ -49,7 +49,6 @@ import com.android.builder.model.AndroidProject;
- import com.android.builder.model.ApiVersion;
- import com.android.builder.model.ArtifactMetaData;
- import com.android.builder.model.JavaArtifact;
--import com.android.builder.model.LintOptions;
- import com.android.builder.model.NativeLibrary;
- import com.android.builder.model.NativeToolchain;
- import com.android.builder.model.ProductFlavor;
-@@ -149,9 +148,6 @@ public class ModelBuilder implements ToolingModelBuilder {
-                     variantType.getArtifactType()));
-         }
- 
--        LintOptions lintOptions = com.android.build.gradle.internal.dsl.LintOptions.create(
--                config.getLintOptions());
--
-         AaptOptions aaptOptions = AaptOptionsImpl.create(config.getAaptOptions());
- 
-         List<SyncIssue> syncIssues = Lists.newArrayList(extraModelInfo.getSyncIssues().values());
-@@ -174,7 +170,6 @@ public class ModelBuilder implements ToolingModelBuilder {
-                 findUnresolvedDependencies(syncIssues),
-                 syncIssues,
-                 config.getCompileOptions(),
--                lintOptions,
-                 project.getBuildDir(),
-                 config.getResourcePrefix(),
-                 ImmutableList.copyOf(toolchains.values()),
-diff --git a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/LibraryVariantData.java b/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/LibraryVariantData.java
-index 2aeb1d6..3138cbc 100644
---- a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/LibraryVariantData.java
-+++ b/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/LibraryVariantData.java
-@@ -22,7 +22,7 @@ import com.android.build.OutputFile;
- import com.android.build.gradle.AndroidConfig;
- import com.android.build.gradle.internal.TaskManager;
- import com.android.build.gradle.internal.core.GradleVariantConfiguration;
--import com.android.build.gradle.tasks.ExtractAnnotations;
-+//import com.android.build.gradle.tasks.ExtractAnnotations;
- import com.android.builder.core.VariantType;
- import com.google.common.collect.Maps;
- 
-@@ -40,9 +40,6 @@ public class LibraryVariantData extends BaseVariantData<LibVariantOutputData> im
- 
-     private final Map<VariantType, TestVariantData> testVariants;
- 
--    @Nullable
--    public ExtractAnnotations generateAnnotationsTask = null;
--
-     public LibraryVariantData(
-             @NonNull AndroidConfig androidConfig,
-             @NonNull TaskManager taskManager,
-@@ -93,11 +90,6 @@ public class LibraryVariantData extends BaseVariantData<LibVariantOutputData> im
-     public void registerJavaGeneratingTask(
-             @NonNull Task task, @NonNull File... generatedSourceFolders) {
-         super.registerJavaGeneratingTask(task, generatedSourceFolders);
--        if (generateAnnotationsTask != null) {
--            for (File f : generatedSourceFolders) {
--                generateAnnotationsTask.source(f);
--            }
--        }
-     }
- 
-     // Overridden to add source folders to a generateAnnotationsTask, if it exists.
-@@ -105,10 +97,5 @@ public class LibraryVariantData extends BaseVariantData<LibVariantOutputData> im
-     public void registerJavaGeneratingTask(
-             @NonNull Task task, @NonNull Collection<File> generatedSourceFolders) {
-         super.registerJavaGeneratingTask(task, generatedSourceFolders);
--        if (generateAnnotationsTask != null) {
--            for (File f : generatedSourceFolders) {
--                generateAnnotationsTask.source(f);
--            }
--        }
-     }
- }
-diff --git a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ExtractAnnotations.groovy b/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ExtractAnnotations.groovy
-deleted file mode 100644
-index 4867946..0000000
---- a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ExtractAnnotations.groovy
-+++ /dev/null
-@@ -1,265 +0,0 @@
--/*
-- * Copyright (C) 2014 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.build.gradle.tasks
--
--import com.android.annotations.NonNull
--import com.android.build.gradle.internal.tasks.AbstractAndroidCompile
--import com.android.build.gradle.internal.variant.BaseVariantData
--import com.android.build.gradle.tasks.annotations.ApiDatabase
--import com.android.build.gradle.tasks.annotations.Extractor
--import com.android.tools.lint.EcjParser
--import com.android.utils.Pair
--import com.google.common.collect.Lists
--import com.google.common.collect.Maps
--import org.eclipse.jdt.core.compiler.IProblem
--import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration
--import org.eclipse.jdt.internal.compiler.batch.CompilationUnit
--import org.eclipse.jdt.internal.compiler.env.ICompilationUnit
--import org.eclipse.jdt.internal.compiler.env.INameEnvironment
--import org.eclipse.jdt.internal.compiler.impl.CompilerOptions
--import org.eclipse.jdt.internal.compiler.util.Util
--import org.gradle.api.file.EmptyFileVisitor
--import org.gradle.api.file.FileVisitDetails
--import org.gradle.api.logging.LogLevel
--import org.gradle.api.tasks.Input
--import org.gradle.api.tasks.InputFile
--import org.gradle.api.tasks.Optional
--import org.gradle.api.tasks.OutputFile
--import org.gradle.api.tasks.TaskAction
--import org.gradle.tooling.BuildException
--
--import static com.android.SdkConstants.DOT_JAVA
--import static com.android.SdkConstants.UTF_8
--
--/**
-- * Task which extracts annotations from the source files, and writes them to one of
-- * two possible destinations:
-- * <ul>
-- *     <li> A "external annotations" file (pointed to by {@link ExtractAnnotations#output})
-- *          which records the annotations in a zipped XML format for use by the IDE and by
-- *          lint to associate the (source retention) annotations back with the compiled code</li>
-- *     <li> For any {@code Keep} annotated elements, a Proguard keep file (pointed to by
-- *          {@link ExtractAnnotations#proguard}, which lists APIs (classes, methods and fields)
-- *          that should not be removed even if no references in code are found to those APIs.</li>
-- * <p>
-- * We typically only extract external annotations when building libraries; ProGuard annotations
-- * are extracted when building libraries (to record in the AAR), <b>or</b> when building an
-- * app module where ProGuarding is enabled.
-- * </ul>
-- */
--class ExtractAnnotations extends AbstractAndroidCompile {
--    public BaseVariantData variant
--
--    /** Boot classpath: typically android.jar */
--    @Input
--    public List<String> bootClasspath
--
--    /** The output .zip file to write the annotations database to, if any */
--    @Optional
--    @OutputFile
--    public File output
--
--    /** The output proguard file to write any @Keep rules into, if any */
--    @Optional
--    @OutputFile
--    public File proguard
--
--    /**
--     * An optional pointer to an API file to filter the annotations by (any annotations
--     * not found in the API file are considered hidden/not exposed.) This is in the same
--     * format as the api-versions.xml file found in the SDK.
--     */
--    @Optional
--    @InputFile
--    public File apiFilter
--
--    /**
--     * A list of existing annotation zip files (or dirs) to merge in. This can be used to merge in
--     * a hardcoded set of annotations that are not present in the source code, such as
--     * {@code @Contract} annotations we'd like to record without actually having a dependency
--     * on the IDEA annotations library.
--     */
--    @Optional
--    @InputFile
--    public List<File> mergeJars
--
--    /**
--     * The encoding to use when reading source files. The output file will ignore this and
--     * will always be a UTF-8 encoded .xml file inside the annotations zip file.
--     */
--    @Optional
--    @Input
--    public String encoding
--
--    /**
--     * Location of class files. If set, any non-public typedef source retention annotations
--     * will be removed prior to .jar packaging.
--     */
--    @Optional
--    @InputFile
--    public File classDir
--
--    /** Whether we allow extraction even in the presence of symbol resolution errors */
--    @InputFile
--    public boolean allowErrors = true
--
--    @Override
--    @TaskAction
--    protected void compile() {
--        if (!hasAndroidAnnotations()) {
--            return
--        }
--
--        if (encoding == null) {
--            encoding = UTF_8
--        }
--
--        Pair<Collection<CompilationUnitDeclaration>, INameEnvironment> result = parseSources()
--        def parsedUnits = result.first
--        def environment = result.second
--
--        try {
--            if (!allowErrors) {
--                for (CompilationUnitDeclaration unit : parsedUnits) {
--                    // so maybe I don't need my map!!
--                    def problems = unit.compilationResult().allProblems
--                    for (IProblem problem : problems) {
--                        if (problem.error) {
--                            println "Not extracting annotations (compilation problems encountered)";
--                            println "Error: " + problem.getOriginatingFileName() + ":" +
--                                    problem.getSourceLineNumber() + ": " + problem.getMessage()
--                            // TODO: Consider whether we abort the build at this point!
--                            return
--                        }
--                    }
--                }
--            }
--
--            // API definition file
--            ApiDatabase database = null;
--            if (apiFilter != null && apiFilter.exists()) {
--                try {
--                    database = new ApiDatabase(apiFilter);
--                } catch (IOException e) {
--                    throw new BuildException("Could not open API database " + apiFilter, e)
--                }
--            }
--
--
--            def displayInfo = project.logger.isEnabled(LogLevel.INFO)
--            def includeClassRetentionAnnotations = false
--            def sortAnnotations = false
--
--            Extractor extractor = new Extractor(database, classDir, displayInfo,
--                    includeClassRetentionAnnotations, sortAnnotations);
--            extractor.extractFromProjectSource(parsedUnits)
--            if (mergeJars != null) {
--                for (File jar : mergeJars) {
--                    extractor.mergeExisting(jar);
--                }
--            }
--            extractor.export(output, proguard)
--            extractor.removeTypedefClasses();
--        } finally {
--            if (environment != null) {
--                environment.cleanup()
--            }
--        }
--    }
--
--    @Input
--    public boolean hasAndroidAnnotations() {
--        return variant.variantDependency.annotationsPresent
--    }
--
--    @NonNull
--    private Pair<Collection<CompilationUnitDeclaration>,INameEnvironment> parseSources() {
--        List<ICompilationUnit> sourceUnits = Lists.newArrayListWithExpectedSize(100);
--
--        source.visit(new EmptyFileVisitor() {
--            @Override
--            void visitFile(FileVisitDetails fileVisitDetails) {
--                def file = fileVisitDetails.file;
--                def path = file.getPath()
--                if (path.endsWith(DOT_JAVA) && file.isFile()) {
--                    char[] contents = Util.getFileCharContent(file, encoding);
--                    ICompilationUnit unit = new CompilationUnit(contents, path, encoding);
--                    sourceUnits.add(unit);
--                }
--            }
--        })
--
--        Map<ICompilationUnit, CompilationUnitDeclaration> outputMap = Maps.
--                newHashMapWithExpectedSize(sourceUnits.size())
--        List<String> jars = Lists.newArrayList();
--        if (bootClasspath != null) {
--            jars.addAll(bootClasspath)
--        }
--        if (classpath != null) {
--            for (File jar : classpath) {
--                jars.add(jar.getPath());
--            }
--        }
--
--        CompilerOptions options = EcjParser.createCompilerOptions();
--        options.docCommentSupport = true; // So I can find @hide
--
--        // Note: We can *not* set options.ignoreMethodBodies=true because it disables
--        // type attribution!
--
--        def level = getLanguageLevel(sourceCompatibility)
--        options.sourceLevel = level
--        options.complianceLevel = options.sourceLevel
--        // We don't generate code, but just in case the parser consults this flag
--        // and makes sure that it's not greater than the source level:
--        options.targetJDK = options.sourceLevel
--        options.originalComplianceLevel = options.sourceLevel;
--        options.originalSourceLevel = options.sourceLevel;
--        options.inlineJsrBytecode = true; // >= 1.5
--
--        def environment = EcjParser.parse(options, sourceUnits, jars, outputMap, null);
--        Collection<CompilationUnitDeclaration> parsedUnits = outputMap.values()
--        Pair.of(parsedUnits, environment);
--    }
--
--    private static long getLanguageLevel(String version) {
--        if ("1.6".equals(version)) {
--            return EcjParser.getLanguageLevel(1, 6);
--        } else if ("1.7".equals(version)) {
--            return EcjParser.getLanguageLevel(1, 7);
--        } else if ("1.5") {
--            return EcjParser.getLanguageLevel(1, 5);
--        } else {
--            return EcjParser.getLanguageLevel(1, 7);
--        }
--    }
--
--    private def addSources(List<ICompilationUnit> sourceUnits, File file) {
--        if (file.isDirectory()) {
--            def files = file.listFiles();
--            if (files != null) {
--                for (File sub : files) {
--                    addSources(sourceUnits, sub);
--                }
--            }
--        } else if (file.getPath().endsWith(DOT_JAVA) && file.isFile()) {
--            char[] contents = Util.getFileCharContent(file, encoding);
--            ICompilationUnit unit = new CompilationUnit(contents, file.getPath(), encoding);
--            sourceUnits.add(unit);
--        }
--    }
--}
-diff --git a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/GroovyGradleDetector.java b/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/GroovyGradleDetector.java
-deleted file mode 100644
-index 16ff7d5..0000000
---- a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/GroovyGradleDetector.java
-+++ /dev/null
-@@ -1,242 +0,0 @@
--/*
-- * Copyright (C) 2014 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.build.gradle.tasks;
--
--import com.android.annotations.NonNull;
--import com.android.tools.lint.checks.GradleDetector;
--import com.android.tools.lint.detector.api.Context;
--import com.android.tools.lint.detector.api.DefaultPosition;
--import com.android.tools.lint.detector.api.Implementation;
--import com.android.tools.lint.detector.api.Location;
--import com.android.tools.lint.detector.api.Scope;
--import com.android.utils.Pair;
--import com.google.common.collect.Lists;
--import com.google.common.collect.Maps;
--
--import org.codehaus.groovy.ast.ASTNode;
--import org.codehaus.groovy.ast.CodeVisitorSupport;
--import org.codehaus.groovy.ast.GroovyCodeVisitor;
--import org.codehaus.groovy.ast.builder.AstBuilder;
--import org.codehaus.groovy.ast.expr.ArgumentListExpression;
--import org.codehaus.groovy.ast.expr.ClosureExpression;
--import org.codehaus.groovy.ast.expr.Expression;
--import org.codehaus.groovy.ast.expr.MapEntryExpression;
--import org.codehaus.groovy.ast.expr.MethodCallExpression;
--import org.codehaus.groovy.ast.expr.NamedArgumentListExpression;
--import org.codehaus.groovy.ast.expr.TupleExpression;
--import org.codehaus.groovy.ast.stmt.BlockStatement;
--import org.codehaus.groovy.ast.stmt.ExpressionStatement;
--import org.codehaus.groovy.ast.stmt.ReturnStatement;
--import org.codehaus.groovy.ast.stmt.Statement;
--
--import java.util.List;
--import java.util.Map;
--
--/**
-- * Implementation of the {@link GradleDetector} using a real Groovy AST,
-- * which the Gradle plugin has access to
-- */
--public class GroovyGradleDetector extends GradleDetector {
--    static final Implementation IMPLEMENTATION = new Implementation(
--            GroovyGradleDetector.class,
--            Scope.GRADLE_SCOPE);
--
--    @Override
--    public void visitBuildScript(@NonNull final Context context, Map<String, Object> sharedData) {
--        try {
--            visitQuietly(context, sharedData);
--        } catch (Throwable t) {
--            // ignore
--            // Parsing the build script can involve class loading that we sometimes can't
--            // handle. This happens for example when running lint in build-system/tests/api/.
--            // This is a lint limitation rather than a user error, so don't complain
--            // about these. Consider reporting a Issue#LINT_ERROR.
--        }
--    }
--
--    private void visitQuietly(@NonNull final Context context, Map<String, Object> sharedData) {
--        String source = context.getContents();
--        if (source == null) {
--            return;
--        }
--
--        List<ASTNode> astNodes = new AstBuilder().buildFromString(source);
--        GroovyCodeVisitor visitor = new CodeVisitorSupport() {
--            private List<MethodCallExpression> mMethodCallStack = Lists.newArrayList();
--            @Override
--            public void visitMethodCallExpression(MethodCallExpression expression) {
--                mMethodCallStack.add(expression);
--                super.visitMethodCallExpression(expression);
--                Expression arguments = expression.getArguments();
--                String parent = expression.getMethodAsString();
--                String parentParent = getParentParent();
--                if (arguments instanceof ArgumentListExpression) {
--                    ArgumentListExpression ale = (ArgumentListExpression)arguments;
--                    List<Expression> expressions = ale.getExpressions();
--                    if (expressions.size() == 1 &&
--                            expressions.get(0) instanceof ClosureExpression) {
--                        if (isInterestingBlock(parent, parentParent)) {
--                            ClosureExpression closureExpression =
--                                    (ClosureExpression)expressions.get(0);
--                            Statement block = closureExpression.getCode();
--                            if (block instanceof BlockStatement) {
--                                BlockStatement bs = (BlockStatement)block;
--                                for (Statement statement : bs.getStatements()) {
--                                    if (statement instanceof ExpressionStatement) {
--                                        ExpressionStatement e = (ExpressionStatement)statement;
--                                        if (e.getExpression() instanceof MethodCallExpression) {
--                                            checkDslProperty(parent,
--                                                    (MethodCallExpression)e.getExpression(),
--                                                    parentParent);
--                                        }
--                                    } else if (statement instanceof ReturnStatement) {
--                                        // Single item in block
--                                        ReturnStatement e = (ReturnStatement)statement;
--                                        if (e.getExpression() instanceof MethodCallExpression) {
--                                            checkDslProperty(parent,
--                                                    (MethodCallExpression)e.getExpression(),
--                                                    parentParent);
--                                        }
--                                    }
--                                }
--                            }
--                        }
--                    }
--                } else if (arguments instanceof TupleExpression) {
--                    if (isInterestingStatement(parent, parentParent)) {
--                        TupleExpression te = (TupleExpression) arguments;
--                        Map<String, String> namedArguments = Maps.newHashMap();
--                        List<String> unnamedArguments = Lists.newArrayList();
--                        for (Expression subExpr : te.getExpressions()) {
--                            if (subExpr instanceof NamedArgumentListExpression) {
--                                NamedArgumentListExpression nale = (NamedArgumentListExpression) subExpr;
--                                for (MapEntryExpression mae : nale.getMapEntryExpressions()) {
--                                    namedArguments.put(mae.getKeyExpression().getText(),
--                                            mae.getValueExpression().getText());
--                                }
--                            }
--                        }
--                        checkMethodCall(context, parent, parentParent, namedArguments, unnamedArguments, expression);
--                    }
--                }
--                assert !mMethodCallStack.isEmpty();
--                assert mMethodCallStack.get(mMethodCallStack.size() - 1) == expression;
--                mMethodCallStack.remove(mMethodCallStack.size() - 1);
--            }
--
--            private String getParentParent() {
--                for (int i = mMethodCallStack.size() - 2; i >= 0; i--) {
--                    MethodCallExpression expression = mMethodCallStack.get(i);
--                    Expression arguments = expression.getArguments();
--                    if (arguments instanceof ArgumentListExpression) {
--                        ArgumentListExpression ale = (ArgumentListExpression)arguments;
--                        List<Expression> expressions = ale.getExpressions();
--                        if (expressions.size() == 1 &&
--                                expressions.get(0) instanceof ClosureExpression) {
--                            return expression.getMethodAsString();
--                        }
--                    }
--                }
--
--                return null;
--            }
--
--            private void checkDslProperty(String parent, MethodCallExpression c,
--                    String parentParent) {
--                String property = c.getMethodAsString();
--                if (isInterestingProperty(property, parent, getParentParent())) {
--                    String value = getText(c.getArguments());
--                    checkDslPropertyAssignment(context, property, value, parent, parentParent, c, c);
--                }
--            }
--
--            private String getText(ASTNode node) {
--                String source = context.getContents();
--                Pair<Integer, Integer> offsets = getOffsets(node, context);
--                return source.substring(offsets.getFirst(), offsets.getSecond());
--            }
--        };
--
--        for (ASTNode node : astNodes) {
--            node.visit(visitor);
--        }
--    }
--
--    @NonNull
--    private static Pair<Integer, Integer> getOffsets(ASTNode node, Context context) {
--        if (node.getLastLineNumber() == -1 && node instanceof TupleExpression) {
--            // Workaround: TupleExpressions yield bogus offsets, so use its
--            // children instead
--            TupleExpression exp = (TupleExpression) node;
--            List<Expression> expressions = exp.getExpressions();
--            if (!expressions.isEmpty()) {
--                return Pair.of(
--                        getOffsets(expressions.get(0), context).getFirst(),
--                        getOffsets(expressions.get(expressions.size() - 1), context).getSecond());
--            }
--        }
--        String source = context.getContents();
--        assert source != null; // because we successfully parsed
--        int start = 0;
--        int end = source.length();
--        int line = 1;
--        int startLine = node.getLineNumber();
--        int startColumn = node.getColumnNumber();
--        int endLine = node.getLastLineNumber();
--        int endColumn = node.getLastColumnNumber();
--        int column = 1;
--        for (int index = 0, len = end; index < len; index++) {
--            if (line == startLine && column == startColumn) {
--                start = index;
--            }
--            if (line == endLine && column == endColumn) {
--                end = index;
--                break;
--            }
--
--            char c = source.charAt(index);
--            if (c == '\n') {
--                line++;
--                column = 1;
--            } else {
--                column++;
--            }
--        }
--
--        return Pair.of(start, end);
--    }
--
--    @Override
--    protected int getStartOffset(@NonNull Context context, @NonNull Object cookie) {
--        ASTNode node = (ASTNode) cookie;
--        Pair<Integer, Integer> offsets = getOffsets(node, context);
--        return offsets.getFirst();
--    }
--
--    @Override
--    protected Location createLocation(@NonNull Context context, @NonNull Object cookie) {
--        ASTNode node = (ASTNode) cookie;
--        Pair<Integer, Integer> offsets = getOffsets(node, context);
--        int fromLine = node.getLineNumber() - 1;
--        int fromColumn = node.getColumnNumber() - 1;
--        int toLine = node.getLastLineNumber() - 1;
--        int toColumn = node.getLastColumnNumber() - 1;
--        return Location.create(context.file,
--                new DefaultPosition(fromLine, fromColumn, offsets.getFirst()),
--                new DefaultPosition(toLine, toColumn, offsets.getSecond()));
--    }
--}
-\ No newline at end of file
-diff --git a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/Lint.groovy b/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/Lint.groovy
-deleted file mode 100644
-index d3b4e42..0000000
---- a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/Lint.groovy
-+++ /dev/null
-@@ -1,295 +0,0 @@
--/*
-- * Copyright (C) 2013 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.build.gradle.tasks
--import com.android.annotations.NonNull
--import com.android.annotations.Nullable
--import com.android.build.gradle.internal.LintGradleClient
--import com.android.build.gradle.internal.dsl.LintOptions
--import com.android.build.gradle.internal.scope.TaskConfigAction
--import com.android.build.gradle.internal.scope.VariantScope
--import com.android.build.gradle.internal.tasks.DefaultAndroidTask
--import com.android.builder.model.AndroidProject
--import com.android.builder.model.Variant
--import com.android.tools.lint.LintCliFlags
--import com.android.tools.lint.Reporter
--import com.android.tools.lint.Warning
--import com.android.tools.lint.checks.BuiltinIssueRegistry
--import com.android.tools.lint.checks.GradleDetector
--import com.android.tools.lint.client.api.IssueRegistry
--import com.android.tools.lint.detector.api.Issue
--import com.android.tools.lint.detector.api.Severity
--import com.google.common.collect.Maps
--import org.gradle.api.GradleException
--import org.gradle.api.Project
--import org.gradle.api.plugins.JavaBasePlugin
--import org.gradle.api.tasks.ParallelizableTask
--import org.gradle.api.tasks.TaskAction
--import org.gradle.tooling.provider.model.ToolingModelBuilder
--import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry
--
-- at ParallelizableTask
--public class Lint extends DefaultAndroidTask {
--    @NonNull private LintOptions mLintOptions
--    @Nullable private File mSdkHome
--    private boolean mFatalOnly
--    private ToolingModelBuilderRegistry mToolingRegistry
--
--    public void setLintOptions(@NonNull LintOptions lintOptions) {
--        mLintOptions = lintOptions
--    }
--
--    public void setSdkHome(@NonNull File sdkHome) {
--        mSdkHome = sdkHome
--    }
--
--    void setToolingRegistry(ToolingModelBuilderRegistry toolingRegistry) {
--        mToolingRegistry = toolingRegistry
--    }
--
--    public void setFatalOnly(boolean fatalOnly) {
--        mFatalOnly = fatalOnly
--    }
--
--    @SuppressWarnings("GroovyUnusedDeclaration")
--    @TaskAction
--    public void lint() {
--        def modelProject = createAndroidProject(project)
--        if (getVariantName() != null) {
--            lintSingleVariant(modelProject, getVariantName())
--        } else {
--            lintAllVariants(modelProject)
--        }
--    }
--
--    /**
--     * Runs lint individually on all the variants, and then compares the results
--     * across variants and reports these
--     */
--    public void lintAllVariants(@NonNull AndroidProject modelProject) {
--        Map<Variant,List<Warning>> warningMap = Maps.newHashMap()
--        for (Variant variant : modelProject.getVariants()) {
--            try {
--                List<Warning> warnings = runLint(modelProject, variant.getName(), false)
--                warningMap.put(variant, warnings)
--            } catch (IOException e) {
--                throw new GradleException("Invalid arguments.", e)
--            }
--        }
--
--        // Compute error matrix
--        def quiet = mLintOptions.quiet
--
--
--        for (Map.Entry<Variant,List<Warning>> entry : warningMap.entrySet()) {
--            def variant = entry.getKey()
--            def warnings = entry.getValue()
--            if (!mFatalOnly && !quiet) {
--                println "Ran lint on variant " + variant.getName() + ": " + warnings.size() +
--                        " issues found"
--            }
--        }
--
--        List<Warning> mergedWarnings = LintGradleClient.merge(warningMap, modelProject)
--        int errorCount = 0
--        int warningCount = 0
--        for (Warning warning : mergedWarnings) {
--            if (warning.severity == Severity.ERROR || warning.severity == Severity.FATAL) {
--                errorCount++
--            } else if (warning.severity == Severity.WARNING) {
--                warningCount++
--            }
--        }
--
--        IssueRegistry registry = new BuiltinIssueRegistry()
--        LintCliFlags flags = new LintCliFlags()
--        LintGradleClient client = new LintGradleClient(registry, flags, project, modelProject,
--                mSdkHome, null)
--        syncOptions(mLintOptions, client, flags, null, project, true, mFatalOnly)
--
--        for (Reporter reporter : flags.getReporters()) {
--            reporter.write(errorCount, warningCount, mergedWarnings)
--        }
--
--        if (flags.isSetExitCode() && errorCount > 0) {
--            abort()
--        }
--    }
--
--    private void abort() {
--        def message;
--        if (mFatalOnly) {
--            message = "" +
--                    "Lint found fatal errors while assembling a release target.\n" +
--                    "\n" +
--                    "To proceed, either fix the issues identified by lint, or modify your build script as follows:\n" +
--                    "...\n" +
--                    "android {\n" +
--                    "    lintOptions {\n" +
--                    "        checkReleaseBuilds false\n" +
--                    "        // Or, if you prefer, you can continue to check for errors in release builds,\n" +
--                    "        // but continue the build even when errors are found:\n" +
--                    "        abortOnError false\n" +
--                    "    }\n" +
--                    "}\n" +
--                    "..."
--                    ""
--        } else {
--            message = "" +
--                    "Lint found errors in the project; aborting build.\n" +
--                    "\n" +
--                    "Fix the issues identified by lint, or add the following to your build script to proceed with errors:\n" +
--                    "...\n" +
--                    "android {\n" +
--                    "    lintOptions {\n" +
--                    "        abortOnError false\n" +
--                    "    }\n" +
--                    "}\n" +
--                    "..."
--        }
--        throw new GradleException(message);
--    }
--
--    /**
--     * Runs lint on a single specified variant
--     */
--    public void lintSingleVariant(@NonNull AndroidProject modelProject, String variantName) {
--        runLint(modelProject, variantName, true)
--    }
--
--    /** Runs lint on the given variant and returns the set of warnings */
--    private List<Warning> runLint(
--            @NonNull AndroidProject modelProject,
--            @NonNull String variantName,
--            boolean report) {
--        IssueRegistry registry = createIssueRegistry()
--        LintCliFlags flags = new LintCliFlags()
--        LintGradleClient client = new LintGradleClient(registry, flags, project, modelProject,
--                mSdkHome, variantName)
--        if (mFatalOnly) {
--            if (!mLintOptions.isCheckReleaseBuilds()) {
--                return
--            }
--            flags.setFatalOnly(true)
--        }
--        syncOptions(mLintOptions, client, flags, variantName, project, report, mFatalOnly)
--        if (!report || mFatalOnly) {
--            flags.setQuiet(true)
--        }
--
--        List<Warning> warnings;
--        try {
--            warnings = client.run(registry)
--        } catch (IOException e) {
--            throw new GradleException("Invalid arguments.", e)
--        }
--
--        if (report && client.haveErrors() && flags.isSetExitCode()) {
--            abort()
--        }
--
--        return warnings;
--    }
--
--    private static syncOptions(
--            @NonNull LintOptions options,
--            @NonNull LintGradleClient client,
--            @NonNull LintCliFlags flags,
--            @NonNull String variantName,
--            @NonNull Project project,
--            boolean report,
--            boolean fatalOnly) {
--        options.syncTo(client, flags, variantName, project, report)
--
--        if (fatalOnly || flags.quiet) {
--            for (Reporter reporter : flags.getReporters()) {
--                reporter.setDisplayEmpty(false)
--            }
--        }
--    }
--
--    private AndroidProject createAndroidProject(@NonNull Project gradleProject) {
--        String modelName = AndroidProject.class.getName()
--        ToolingModelBuilder modelBuilder = mToolingRegistry.getBuilder(modelName)
--        assert modelBuilder != null
--        return (AndroidProject) modelBuilder.buildAll(modelName, gradleProject)
--    }
--
--    private static BuiltinIssueRegistry createIssueRegistry() {
--        return new LintGradleIssueRegistry()
--    }
--
--    // Issue registry when Lint is run inside Gradle: we replace the Gradle
--    // detector with a local implementation which directly references Groovy
--    // for parsing. In Studio on the other hand, the implementation is replaced
--    // by a PSI-based check. (This is necessary for now since we don't have a
--    // tool-agnostic API for the Groovy AST and we don't want to add a 6.3MB dependency
--    // on Groovy itself quite yet.
--    public static class LintGradleIssueRegistry extends BuiltinIssueRegistry {
--        private boolean mInitialized;
--
--        public LintGradleIssueRegistry() {
--        }
--
--        @NonNull
--        @Override
--        public List<Issue> getIssues() {
--            List<Issue> issues = super.getIssues();
--            if (!mInitialized) {
--                mInitialized = true;
--                for (Issue issue : issues) {
--                    if (issue.getImplementation().getDetectorClass() == GradleDetector.class) {
--                        issue.setImplementation(GroovyGradleDetector.IMPLEMENTATION);
--                    }
--                }
--            }
--
--            return issues;
--        }
--    }
--
--    public static class ConfigAction implements TaskConfigAction<Lint> {
--
--        @NonNull
--        VariantScope scope
--
--        ConfigAction(@NonNull VariantScope scope) {
--            this.scope = scope
--        }
--
--        @Override
--        @NonNull
--        String getName() {
--            return scope.getTaskName("lint")
--        }
--
--        @Override
--        @NonNull
--        Class<Lint> getType() {
--            return Lint
--        }
--
--        @Override
--        void execute(Lint lint) {
--            lint.setLintOptions(scope.globalScope.getExtension().lintOptions)
--            lint.setSdkHome(scope.globalScope.sdkHandler.getSdkFolder())
--            lint.setVariantName(scope.variantConfiguration.fullName)
--            lint.setToolingRegistry(scope.globalScope.toolingRegistry)
--            lint.description = "Runs lint on the " + scope.variantConfiguration.fullName.capitalize() + " build."
--            lint.group = JavaBasePlugin.VERIFICATION_GROUP
--        }
--    }
--}
-diff --git a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/PackageApplication.java b/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/PackageApplication.java
-index 16ab90c..52f085b 100644
---- a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/PackageApplication.java
-+++ b/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/PackageApplication.java
-@@ -286,22 +286,6 @@ public class PackageApplication extends IncrementalTask implements FileSupplier
-             packageApp.setVariantName(
-                     scope.getVariantScope().getVariantConfiguration().getFullName());
- 
--            if (config.isMinifyEnabled() && config.getBuildType().isShrinkResources() && !config
--                    .getUseJack()) {
--                ConventionMappingHelper.map(packageApp, "resourceFile", new Callable<File>() {
--                    @Override
--                    public File call() {
--                        return scope.getCompressedResourceFile();
--                    }
--                });
--            } else {
--                ConventionMappingHelper.map(packageApp, "resourceFile", new Callable<File>() {
--                    @Override
--                    public File call() {
--                        return variantOutputData.processResourcesTask.getPackageOutputFile();
--                    }
--                });
--            }
- 
-             ConventionMappingHelper.map(packageApp, "dexFolder", new Callable<File>() {
-                 @Override
-@@ -437,36 +421,6 @@ public class PackageApplication extends IncrementalTask implements FileSupplier
-             });
-         }
- 
--        private ShrinkResources createShrinkResourcesTask(
--                final ApkVariantOutputData variantOutputData) {
--            BaseVariantData<?> variantData = (BaseVariantData<?>) variantOutputData.variantData;
--            ShrinkResources task = scope.getGlobalScope().getProject().getTasks()
--                    .create("shrink" + StringGroovyMethods
--                            .capitalize(variantOutputData.getFullName())
--                            + "Resources", ShrinkResources.class);
--            task.setAndroidBuilder(scope.getGlobalScope().getAndroidBuilder());
--            task.setVariantName(scope.getVariantScope().getVariantConfiguration().getFullName());
--            task.variantOutputData = variantOutputData;
--
--            final String outputBaseName = variantOutputData.getBaseName();
--            task.setCompressedResources(new File(
--                    scope.getGlobalScope().getBuildDir() + "/" + FD_INTERMEDIATES + "/res/" +
--                            "resources-" + outputBaseName + "-stripped.ap_"));
--
--            ConventionMappingHelper.map(task, "uncompressedResources", new Callable<File>() {
--                @Override
--                public File call() {
--                    return variantOutputData.processResourcesTask.getPackageOutputFile();
--                }
--            });
--
--            task.dependsOn(
--                    scope.getVariantScope().getObfuscationTask().getName(),
--                    scope.getManifestProcessorTask().getName(),
--                    variantOutputData.processResourcesTask);
--
--            return task;
--        }
- 
-         private static File getOptionalDir(File dir) {
-             if (dir.isDirectory()) {
-diff --git a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ResourceUsageAnalyzer.java b/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ResourceUsageAnalyzer.java
-deleted file mode 100644
-index 649a8d0..0000000
---- a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ResourceUsageAnalyzer.java
-+++ /dev/null
-@@ -1,2568 +0,0 @@
--/*
-- * Copyright (C) 2014 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.build.gradle.tasks;
--
--import static com.android.SdkConstants.ANDROID_STYLE_RESOURCE_PREFIX;
--import static com.android.SdkConstants.ANDROID_URI;
--import static com.android.SdkConstants.ATTR_NAME;
--import static com.android.SdkConstants.ATTR_PARENT;
--import static com.android.SdkConstants.ATTR_TYPE;
--import static com.android.SdkConstants.DOT_CLASS;
--import static com.android.SdkConstants.DOT_GIF;
--import static com.android.SdkConstants.DOT_JPEG;
--import static com.android.SdkConstants.DOT_JPG;
--import static com.android.SdkConstants.DOT_PNG;
--import static com.android.SdkConstants.DOT_XML;
--import static com.android.SdkConstants.FD_RES_VALUES;
--import static com.android.SdkConstants.PREFIX_ANDROID;
--import static com.android.SdkConstants.STYLE_RESOURCE_PREFIX;
--import static com.android.SdkConstants.TAG_ITEM;
--import static com.android.SdkConstants.TAG_RESOURCES;
--import static com.android.SdkConstants.TAG_STYLE;
--import static com.android.SdkConstants.TOOLS_URI;
--import static com.android.utils.SdkUtils.endsWith;
--import static com.android.utils.SdkUtils.endsWithIgnoreCase;
--import static com.google.common.base.Charsets.UTF_8;
--import static org.objectweb.asm.ClassReader.SKIP_DEBUG;
--import static org.objectweb.asm.ClassReader.SKIP_FRAMES;
--
--import com.android.SdkConstants;
--import com.android.annotations.NonNull;
--import com.android.annotations.Nullable;
--import com.android.annotations.VisibleForTesting;
--import com.android.ide.common.resources.ResourceUrl;
--import com.android.ide.common.resources.configuration.DensityQualifier;
--import com.android.ide.common.resources.configuration.FolderConfiguration;
--import com.android.ide.common.resources.configuration.ResourceQualifier;
--import com.android.ide.common.xml.XmlPrettyPrinter;
--import com.android.resources.FolderTypeRelationship;
--import com.android.resources.ResourceFolderType;
--import com.android.resources.ResourceType;
--import com.android.tools.lint.checks.StringFormatDetector;
--import com.android.tools.lint.client.api.DefaultConfiguration;
--import com.android.tools.lint.detector.api.LintUtils;
--import com.android.utils.XmlUtils;
--import com.google.common.base.Joiner;
--import com.google.common.base.Splitter;
--import com.google.common.collect.Lists;
--import com.google.common.collect.Maps;
--import com.google.common.collect.Sets;
--import com.google.common.io.ByteStreams;
--import com.google.common.io.Closeables;
--import com.google.common.io.Files;
--
--import org.objectweb.asm.AnnotationVisitor;
--import org.objectweb.asm.ClassReader;
--import org.objectweb.asm.ClassVisitor;
--import org.objectweb.asm.FieldVisitor;
--import org.objectweb.asm.MethodVisitor;
--import org.objectweb.asm.Opcodes;
--import org.w3c.dom.Attr;
--import org.w3c.dom.Document;
--import org.w3c.dom.Element;
--import org.w3c.dom.NamedNodeMap;
--import org.w3c.dom.Node;
--import org.w3c.dom.NodeList;
--import org.xml.sax.SAXException;
--
--import java.io.File;
--import java.io.FileInputStream;
--import java.io.FileOutputStream;
--import java.io.IOException;
--import java.util.ArrayList;
--import java.util.Collections;
--import java.util.Comparator;
--import java.util.IdentityHashMap;
--import java.util.List;
--import java.util.Map;
--import java.util.Set;
--import java.util.jar.JarEntry;
--import java.util.jar.JarInputStream;
--import java.util.jar.JarOutputStream;
--import java.util.regex.Matcher;
--import java.util.regex.Pattern;
--import java.util.regex.PatternSyntaxException;
--import java.util.zip.ZipEntry;
--import java.util.zip.ZipInputStream;
--
--import javax.xml.parsers.ParserConfigurationException;
--
--/**
-- * Class responsible for searching through a Gradle built tree (after resource merging,
-- * compilation and ProGuarding has been completed, but before final .apk assembly), which
-- * figures out which resources if any are unused, and removes them.
-- * <p>
-- * It does this by examining
-- * <ul>
-- *     <li>The merged manifest, to find root resource references (such as drawables
-- *         used for activity icons)</li>
-- *     <li>The merged R class (to find the actual integer constants assigned to resources)</li>
-- *     <li>The ProGuard log files (to find the mapping from original symbol names to
-- *         short names)</li>*
-- *     <li>The merged resources (to find which resources reference other resources, e.g.
-- *         drawable state lists including other drawables, or layouts including other
-- *         layouts, or styles referencing other drawables, or menus items including action
-- *         layouts, etc.)</li>
-- *     <li>The ProGuard output classes (to find resource references in code that are
-- *         actually reachable)</li>
-- * </ul>
-- * From all this, it builds up a reference graph, and based on the root references (e.g.
-- * from the manifest and from the remaining code) it computes which resources are actually
-- * reachable in the app, and anything that is not reachable is then marked for deletion.
-- * <p>
-- * A resource is referenced in code if either the field R.type.name is referenced (which
-- * is the case for non-final resource references, e.g. in libraries), or if the corresponding
-- * int value is referenced (for final resource values). We check this by looking at the
-- * ProGuard output classes with an ASM visitor. One complication is that code can also
-- * call {@code Resources#getIdentifier(String,String,String)} where they can pass in the names
-- * of resources to look up. To handle this scenario, we use the ClassVisitor to see if
-- * there are any calls to the specific {@code Resources#getIdentifier} method. If not,
-- * great, the usage analysis is completely accurate. If we <b>do</b> find one, we check
-- * <b>all</b> the string constants found anywhere in the app, and look to see if any look
-- * relevant. For example, if we find the string "string/foo" or "my.pkg:string/foo", we
-- * will then mark the string resource named foo (if any) as potentially used. Similarly,
-- * if we find just "foo" or "/foo", we will mark <b>all</b> resources named "foo" as
-- * potentially used. However, if the string is "bar/foo" or " foo " these strings are
-- * ignored. This means we can potentially miss resources usages where the resource name
-- * is completed computed (e.g. by concatenating individual characters or taking substrings
-- * of strings that do not look like resource names), but that seems extremely unlikely
-- * to be a real-world scenario.
-- * <p>
-- * For now, for reasons detailed in the code, this only applies to file-based resources
-- * like layouts, menus and drawables, not value-based resources like strings and dimensions.
-- */
--public class ResourceUsageAnalyzer {
--    private static final String ANDROID_RES = "android_res/";
--
--    /**
--     Whether we support running aapt twice, to regenerate the resources.arsc file
--     such that we can strip out value resources as well. We don't do this yet, for
--     reasons detailed in the ShrinkResources task
--
--     We have two options:
--     (1) Copy the resource files over to a new destination directory, filtering out
--     removed file resources and rewriting value resource files by stripping out
--     the declarations for removed value resources. We then re-run aapt on this
--     new destination directory.
--
--     The problem with this approach is that when we re-run aapt it will assign new
--     id's to all the resources, so we have to create dummy placeholders for all the
--     removed resources. (The alternative would be to then run compilation one more
--     time -- regenerating classes.jar, regenerating .dex) -- this would really slow
--     down builds.)
--
--     A cleaner solution than this is to get aapt to support using a predefined set
--     of id's. It can emit R.txt symbol files now; if we can get it to read R.txt
--     and use those numbers in its assignment, we can solve this cleanly. This request
--     is tracked in https://code.google.com/p/android/issues/detail?id=70869
--
--     (2) Just rewrite the .ap_ file directly. It's just a .zip file which contains
--     (a) binary files for bitmaps and XML file resources such as layouts and menus
--     (b) a binary file, resources.arsc, containing all the values.
--     The resources.arsc format is opaque to us. However, MOST of the resource bulk
--     comes from the bitmap and other resource files.
--
--     So here we don't even need to run aapt a second time; we simply rewrite the
--     .ap_ zip file directly, filtering out res/ files we know to be unused.
--
--     Approach #2 gives us most of the space savings without the risk of #1 (running aapt
--     a second time introduces the possibility of aapt compilation errors if we haven't
--     been careful enough to insert resource aliases for all necessary items (such as
--     inline @+id declarations), or if we haven't carefully not created aliases for items
--     already defined in other value files as aliases, and perhaps most importantly,
--     introduces risk that aapt will pick a different resource order anyway, which we can
--     only guard against by doing a full compilation over again.
--
--     Therefore, for now the below code uses #2, but since we can solve #1 with support
--     from aapt), we're preserving all the code to rewrite resource files since that will
--     give additional space savings, particularly for apps with a lot of strings or a lot
--     of translations.
--     */
--    @SuppressWarnings("SpellCheckingInspection") // arsc
--    public static final boolean TWO_PASS_AAPT = false;
--    public static final int TYPICAL_RESOURCE_COUNT = 200;
--
--    /** Name of keep attribute in XML */
--    private static final String ATTR_KEEP = "keep";
--    /** Name of discard attribute in XML (to mark resources as not referenced, despite guesses) */
--    private static final String ATTR_DISCARD = "discard";
--    /** Name of attribute in XML to control whether we should guess resources to keep */
--    private static final String ATTR_SHRINK_MODE = "shrinkMode";
--    /** @{linkplain #ATTR_SHRINK_MODE} value to only shrink explicitly encountered resources */
--    private static final String VALUE_STRICT = "strict";
--    /** @{linkplain #ATTR_SHRINK_MODE} value to keep possibly referenced resources */
--    private static final String VALUE_SAFE = "safe";
--
--    /** Special marker regexp which does not match a resource name */
--    static final String NO_MATCH = "-nomatch-";
--
--    private final File mResourceClassDir;
--    private final File mProguardMapping;
--    private final File mClassesJar;
--    private final File mMergedManifest;
--    private final File mMergedResourceDir;
--
--    private boolean mVerbose;
--    private boolean mDebug;
--    private boolean mDryRun;
--
--    /** The computed set of unused resources */
--    private List<Resource> mUnused;
--
--    /** List of all known resources (parsed from R.java) */
--    private List<Resource> mResources = Lists.newArrayListWithExpectedSize(TYPICAL_RESOURCE_COUNT);
--    /** Map from R field value to corresponding resource */
--    private Map<Integer, Resource> mValueToResource =
--            Maps.newHashMapWithExpectedSize(TYPICAL_RESOURCE_COUNT);
--    /** Map from resource type to map from resource name to resource object */
--    private Map<ResourceType, Map<String, Resource>> mTypeToName =
--            Maps.newEnumMap(ResourceType.class);
--    /** Map from resource class owners (VM format class) to corresponding resource types.
--     * This will typically be the fully qualified names of the R classes, as well as
--     * any renamed versions of those discovered in the mapping.txt file from ProGuard */
--    private Map<String, ResourceType> mResourceClassOwners = Maps.newHashMapWithExpectedSize(20);
--
--    /**
--     * Whether we should attempt to guess resources that should be kept based on looking
--     * at the string pool and assuming some of the strings can be used to dynamically construct
--     * the resource names. Can be turned off via {@code tools:guessKeep="false"}.
--     */
--    private boolean mGuessKeep = true;
--
--    public ResourceUsageAnalyzer(
--            @NonNull File rDir,
--            @NonNull File classesJar,
--            @NonNull File manifest,
--            @Nullable File mapping,
--            @NonNull File resources) {
--        mResourceClassDir = rDir;
--        mProguardMapping = mapping;
--        mClassesJar = classesJar;
--        mMergedManifest = manifest;
--        mMergedResourceDir = resources;
--    }
--
--    public void analyze() throws IOException, ParserConfigurationException, SAXException {
--        gatherResourceValues(mResourceClassDir);
--        recordMapping(mProguardMapping);
--        recordUsages(mClassesJar);
--        recordManifestUsages(mMergedManifest);
--        recordResources(mMergedResourceDir);
--        keepPossiblyReferencedResources();
--        dumpReferences();
--        findUnused();
--    }
--
--    public boolean isDryRun() {
--        return mDryRun;
--    }
--
--    public void setDryRun(boolean dryRun) {
--        mDryRun = dryRun;
--    }
--
--    public boolean isVerbose() {
--        return mVerbose;
--    }
--
--    public void setVerbose(boolean verbose) {
--        mVerbose = verbose;
--    }
--
--
--    public boolean isDebug() {
--        return mDebug;
--    }
--
--    public void setDebug(boolean verbose) {
--        mDebug = verbose;
--    }
--
--    /**
--     * "Removes" resources from an .ap_ file by writing it out while filtering out
--     * unused resources. This won't touch the values XML data (resources.arsc) but
--     * will remove the individual file-based resources, which is where most of
--     * the data is anyway (usually in drawable bitmaps)
--     *
--     * @param source the .ap_ file created by aapt
--     * @param dest a new .ap_ file with unused file-based resources removed
--     */
--    public void rewriteResourceZip(@NonNull File source, @NonNull File dest)
--            throws IOException {
--        if (dest.exists()) {
--            boolean deleted = dest.delete();
--            if (!deleted) {
--                throw new IOException("Could not delete " + dest);
--            }
--        }
--
--        JarInputStream zis = null;
--        try {
--            FileInputStream fis = new FileInputStream(source);
--            try {
--                FileOutputStream fos = new FileOutputStream(dest);
--                zis = new JarInputStream(fis);
--                JarOutputStream zos = new JarOutputStream(fos);
--                try {
--                    // Rather than using Deflater.DEFAULT_COMPRESSION we use 9 here,
--                    // since that seems to match the compressed sizes we observe in source
--                    // .ap_ files encountered by the resource shrinker:
--                    zos.setLevel(9);
--
--                    ZipEntry entry = zis.getNextEntry();
--                    while (entry != null) {
--                        String name = entry.getName();
--                        boolean directory = entry.isDirectory();
--                        Resource resource = getResourceByJarPath(name);
--                        if (resource == null || resource.reachable) {
--                            // We can't just compress all files; files that are not
--                            // compressed in the source .ap_ file must be left uncompressed
--                            // here, since for example RAW files need to remain uncompressed in
--                            // the APK such that they can be mmap'ed at runtime.
--                            // Preserve the STORED method of the input entry.
--                            JarEntry outEntry;
--                            if (entry.getMethod() == JarEntry.STORED) {
--                                outEntry = new JarEntry(entry);
--                            } else {
--                                // Create a new entry so that the compressed len is recomputed.
--                                outEntry = new JarEntry(name);
--                                if (entry.getTime() != -1L) {
--                                    outEntry.setTime(entry.getTime());
--                                }
--                            }
--
--                            zos.putNextEntry(outEntry);
--
--                            if (!directory) {
--                                byte[] bytes = ByteStreams.toByteArray(zis);
--                                if (bytes != null) {
--                                    zos.write(bytes);
--                                }
--                            }
--
--                            zos.closeEntry();
--                        } else if (isVerbose()) {
--                            System.out.println("Skipped unused resource " + name + ": "
--                                    + entry.getSize() + " bytes");
--                        }
--                        entry = zis.getNextEntry();
--                    }
--                    zos.flush();
--                } finally {
--                    Closeables.close(zos, false);
--                }
--            } finally {
--                Closeables.close(fis, true);
--            }
--        } finally {
--            Closeables.close(zis, false);
--        }
--    }
--
--    /**
--     * Remove resources (already identified by {@link #analyze()}).
--     *
--     * This task will copy all remaining used resources over from the full resource
--     * directory to a new reduced resource directory. However, it can't just
--     * delete the resources, because it has no way to tell aapt to continue to use
--     * the same id's for the resources. When we re-run aapt on the stripped resource
--     * directory, it will assign new id's to some of the resources (to fill the gaps)
--     * which means the resource id's no longer match the constants compiled into the
--     * dex files, and as a result, the app crashes at runtime.
--     * <p>
--     * Therefore, it needs to preserve all id's by actually keeping all the resource
--     * names. It can still save a lot of space by making these resources tiny; e.g.
--     * all strings are set to empty, all styles, arrays and plurals are set to not contain
--     * any children, and most importantly, all file based resources like bitmaps and
--     * layouts are replaced by simple resource aliases which just point to @null.
--     *
--     * @param destination directory to copy resources into; if null, delete resources in place
--     * @throws IOException
--     * @throws ParserConfigurationException
--     * @throws SAXException
--     */
--    public void removeUnused(@Nullable File destination) throws IOException,
--            ParserConfigurationException, SAXException {
--        if (TWO_PASS_AAPT) {
--            assert mUnused != null; // should always call analyze() first
--
--            int resourceCount = mUnused.size()
--                    * 4; // *4: account for some resource folder repetition
--            boolean inPlace = destination == null;
--            Set<File> skip = inPlace ? null : Sets.<File>newHashSetWithExpectedSize(resourceCount);
--            Set<File> rewrite = Sets.newHashSetWithExpectedSize(resourceCount);
--            for (Resource resource : mUnused) {
--                if (resource.declarations != null) {
--                    for (File file : resource.declarations) {
--                        String folder = file.getParentFile().getName();
--                        ResourceFolderType folderType = ResourceFolderType.getFolderType(folder);
--                        if (folderType != null && folderType != ResourceFolderType.VALUES) {
--                            if (isVerbose()) {
--                                System.out.println("Deleted unused resource " + file);
--                            }
--                            if (inPlace) {
--                                if (!isDryRun()) {
--                                    boolean delete = file.delete();
--                                    if (!delete) {
--                                        System.err.println("Could not delete " + file);
--                                    }
--                                }
--                            } else {
--                                assert skip != null;
--                                skip.add(file);
--                            }
--                        } else {
--                            // Can't delete values immediately; there can be many resources
--                            // in this file, so we have to process them all
--                            rewrite.add(file);
--                        }
--                    }
--                }
--            }
--
--            // Special case the base values.xml folder
--            File values = new File(mMergedResourceDir,
--                    FD_RES_VALUES + File.separatorChar + "values.xml");
--            boolean valuesExists = values.exists();
--            if (valuesExists) {
--                rewrite.add(values);
--            }
--
--            Map<File, String> rewritten = Maps.newHashMapWithExpectedSize(rewrite.size());
--
--            // Delete value resources: Must rewrite the XML files
--            for (File file : rewrite) {
--                String xml = Files.toString(file, UTF_8);
--                Document document = XmlUtils.parseDocument(xml, true);
--                Element root = document.getDocumentElement();
--                if (root != null && TAG_RESOURCES.equals(root.getTagName())) {
--                    List<String> removed = Lists.newArrayList();
--                    stripUnused(root, removed);
--                    if (isVerbose()) {
--                        System.out.println("Removed " + removed.size() +
--                                " unused resources from " + file + ":\n  " +
--                                Joiner.on(", ").join(removed));
--                    }
--
--                    String formatted = XmlPrettyPrinter.prettyPrint(document, xml.endsWith("\n"));
--                    rewritten.put(file, formatted);
--                }
--            }
--
--            if (isDryRun()) {
--                return;
--            }
--
--            if (valuesExists) {
--                String xml = rewritten.get(values);
--                if (xml == null) {
--                    xml = Files.toString(values, UTF_8);
--                }
--                Document document = XmlUtils.parseDocument(xml, true);
--                Element root = document.getDocumentElement();
--
--                for (Resource resource : mResources) {
--                    if (resource.type == ResourceType.ID && !resource.hasDefault) {
--                        Element item = document.createElement(TAG_ITEM);
--                        item.setAttribute(ATTR_TYPE, resource.type.getName());
--                        item.setAttribute(ATTR_NAME, resource.name);
--                        root.appendChild(item);
--                    } else if (!resource.reachable
--                            && !resource.hasDefault
--                            && resource.type != ResourceType.DECLARE_STYLEABLE
--                            && resource.type != ResourceType.STYLE
--                            && resource.type != ResourceType.PLURALS
--                            && resource.type != ResourceType.ARRAY
--                            && resource.isRelevantType()) {
--                        Element item = document.createElement(TAG_ITEM);
--                        item.setAttribute(ATTR_TYPE, resource.type.getName());
--                        item.setAttribute(ATTR_NAME, resource.name);
--                        root.appendChild(item);
--                        String s = "@null";
--                        item.appendChild(document.createTextNode(s));
--                    }
--                }
--
--                String formatted = XmlPrettyPrinter.prettyPrint(document, xml.endsWith("\n"));
--                rewritten.put(values, formatted);
--            }
--
--            if (inPlace) {
--                for (Map.Entry<File, String> entry : rewritten.entrySet()) {
--                    File file = entry.getKey();
--                    String formatted = entry.getValue();
--                    Files.write(formatted, file, UTF_8);
--                }
--            } else {
--                filteredCopy(mMergedResourceDir, destination, skip, rewritten);
--            }
--        } else {
--            assert false;
--        }
--    }
--
--    /**
--     * Copies one resource directory tree into another; skipping some files, replacing
--     * the contents of some, and passing everything else through unmodified
--     */
--    private static void filteredCopy(File source, File destination, Set<File> skip,
--            Map<File, String> replace) throws IOException {
--        if (TWO_PASS_AAPT) {
--            if (source.isDirectory()) {
--                File[] children = source.listFiles();
--                if (children != null) {
--                    if (!destination.exists()) {
--                        boolean success = destination.mkdirs();
--                        if (!success) {
--                            throw new IOException("Could not create " + destination);
--                        }
--                    }
--                    for (File child : children) {
--                        filteredCopy(child, new File(destination, child.getName()), skip, replace);
--                    }
--                }
--            } else if (!skip.contains(source) && source.isFile()) {
--                String contents = replace.get(source);
--                if (contents != null) {
--                    Files.write(contents, destination, UTF_8);
--                } else {
--                    Files.copy(source, destination);
--                }
--            }
--        } else {
--            assert false;
--        }
--    }
--
--    private void stripUnused(Element element, List<String> removed) {
--        if (TWO_PASS_AAPT) {
--            ResourceType type = getResourceType(element);
--            if (type == ResourceType.ATTR) {
--                // Not yet properly handled
--                return;
--            }
--
--            Resource resource = getResource(element);
--            if (resource != null) {
--                if (resource.type == ResourceType.DECLARE_STYLEABLE ||
--                        resource.type == ResourceType.ATTR) {
--                    // Don't strip children of declare-styleable; we're not correctly
--                    // tracking field references of the R_styleable_attr fields yet
--                    return;
--                }
--
--                if (!resource.reachable &&
--                        (resource.type == ResourceType.STYLE ||
--                                resource.type == ResourceType.PLURALS ||
--                                resource.type == ResourceType.ARRAY)) {
--                    NodeList children = element.getChildNodes();
--                    for (int i = children.getLength() - 1; i >= 0; i--) {
--                        Node child = children.item(i);
--                        element.removeChild(child);
--                    }
--                    return;
--                }
--            }
--
--            NodeList children = element.getChildNodes();
--            for (int i = children.getLength() - 1; i >= 0; i--) {
--                Node child = children.item(i);
--                if (child.getNodeType() == Node.ELEMENT_NODE) {
--                    stripUnused((Element) child, removed);
--                }
--            }
--
--            if (resource != null && !resource.reachable) {
--                if (mVerbose) {
--                    removed.add(resource.getUrl());
--                }
--                // for themes etc where .'s have been replaced by _'s
--                String name = element.getAttribute(ATTR_NAME);
--                if (name.isEmpty()) {
--                    name = resource.name;
--                }
--                Node nextSibling = element.getNextSibling();
--                Node parent = element.getParentNode();
--                NodeList oldChildren = element.getChildNodes();
--                parent.removeChild(element);
--                Document document = element.getOwnerDocument();
--                element = document.createElement("item");
--                for (int i = 0; i < oldChildren.getLength(); i++) {
--                    element.appendChild(oldChildren.item(i));
--                }
--
--                element.setAttribute(ATTR_NAME, name);
--                element.setAttribute(ATTR_TYPE, resource.type.getName());
--                String text = null;
--                switch (resource.type) {
--                    case BOOL:
--                        text = "true";
--                        break;
--                    case DIMEN:
--                        text = "0dp";
--                        break;
--                    case INTEGER:
--                        text = "0";
--                        break;
--                }
--                element.setTextContent(text);
--                parent.insertBefore(element, nextSibling);
--            }
--        } else {
--            assert false;
--        }
--    }
--
--    private static String getFieldName(Element element) {
--        return getFieldName(element.getAttribute(ATTR_NAME));
--    }
--
--    @Nullable
--    private Resource getResource(Element element) {
--        ResourceType type = getResourceType(element);
--        if (type != null) {
--            String name = getFieldName(element);
--            return getResource(type, name);
--        }
--
--        return null;
--    }
--
--    @Nullable
--    private Resource getResourceByJarPath(String path) {
--        // Jars use forward slash paths, not File.separator
--        if (path.startsWith("res/")) {
--            int folderStart = 4; // "res/".length
--            int folderEnd = path.indexOf('/', folderStart);
--            if (folderEnd != -1) {
--                String folderName = path.substring(folderStart, folderEnd);
--                ResourceFolderType folderType = ResourceFolderType.getFolderType(folderName);
--                if (folderType != null) {
--                    int nameStart = folderEnd + 1;
--                    int nameEnd = path.indexOf('.', nameStart);
--                    if (nameEnd != -1) {
--                        String name = path.substring(nameStart, nameEnd);
--                        List<ResourceType> types =
--                                FolderTypeRelationship.getRelatedResourceTypes(folderType);
--                        for (ResourceType type : types) {
--                            if (type != ResourceType.ID) {
--                                Resource resource = getResource(type, name);
--                                if (resource != null) {
--                                    return resource;
--                                }
--                            }
--                        }
--                    }
--                }
--            }
--        }
--
--        return null;
--    }
--
--    private static ResourceType getResourceType(Element element) {
--        String tagName = element.getTagName();
--        if (tagName.equals(TAG_ITEM)) {
--            String typeName = element.getAttribute(ATTR_TYPE);
--            if (!typeName.isEmpty()) {
--                return ResourceType.getEnum(typeName);
--            }
--        } else if ("string-array".equals(tagName) || "integer-array".equals(tagName)) {
--            return ResourceType.ARRAY;
--        } else {
--            return ResourceType.getEnum(tagName);
--        }
--        return null;
--    }
--
--    private void findUnused() {
--        List<Resource> roots = Lists.newArrayList();
--
--        for (Resource resource : mResources) {
--            if (resource.reachable && resource.type != ResourceType.ID
--                    && resource.type != ResourceType.ATTR) {
--                roots.add(resource);
--            }
--        }
--
--        if (mDebug) {
--            System.out.println("The root reachable resources are:\n" +
--                    Joiner.on(",\n   ").join(roots));
--        }
--
--        Map<Resource,Boolean> seen = new IdentityHashMap<Resource,Boolean>(mResources.size());
--        for (Resource root : roots) {
--            visit(root, seen);
--        }
--
--        List<Resource> unused = Lists.newArrayListWithExpectedSize(mResources.size());
--        for (Resource resource : mResources) {
--            if (!resource.reachable && resource.isRelevantType()) {
--                unused.add(resource);
--            }
--        }
--
--        mUnused = unused;
--
--        if (mDebug) {
--            System.out.println(dumpResourceModel());
--        }
--    }
--
--    private static void visit(Resource root, Map<Resource, Boolean> seen) {
--        if (seen.containsKey(root)) {
--            return;
--        }
--        seen.put(root, Boolean.TRUE);
--        root.reachable = true;
--        if (root.references != null) {
--            for (Resource referenced : root.references) {
--                visit(referenced, seen);
--            }
--        }
--    }
--
--    private void dumpReferences() {
--        if (mDebug) {
--            System.out.println("Resource Reference Graph:");
--            for (Resource resource : mResources) {
--                if (resource.references != null) {
--                    System.out.println(resource + " => " + resource.references);
--                }
--            }
--        }
--    }
--
--    private void keepPossiblyReferencedResources() {
--        if ((!mFoundGetIdentifier && !mFoundWebContent) || mStrings == null) {
--            // No calls to android.content.res.Resources#getIdentifier; no need
--            // to worry about string references to resources
--            return;
--        }
--
--        if (!mGuessKeep) {
--            // User specifically asked for us not to guess resources to keep; they will
--            // explicitly mark them as kept if necessary instead
--            return;
--        }
--
--        if (mDebug) {
--            List<String> strings = new ArrayList<String>(mStrings);
--            Collections.sort(strings);
--            System.out.println("android.content.res.Resources#getIdentifier present: "
--                    + mFoundGetIdentifier);
--            System.out.println("Web content present: " + mFoundWebContent);
--            System.out.println("Referenced Strings:");
--            for (String s : strings) {
--                s = s.trim().replace("\n", "\\n");
--                if (s.length() > 40) {
--                    s = s.substring(0, 37) + "...";
--                } else if (s.isEmpty()) {
--                    continue;
--                }
--                System.out.println("  " + s);
--            }
--        }
--
--        int shortest = Integer.MAX_VALUE;
--        Set<String> names = Sets.newHashSetWithExpectedSize(50);
--        for (Map<String, Resource> map : mTypeToName.values()) {
--            for (String name : map.keySet()) {
--                names.add(name);
--                int length = name.length();
--                if (length < shortest) {
--                    shortest = length;
--                }
--            }
--        }
--
--        for (String string : mStrings) {
--            if (string.length() < shortest) {
--                continue;
--            }
--
--            // Check whether the string looks relevant
--            // We consider four types of strings:
--            //  (1) simple resource names, e.g. "foo" from @layout/foo
--            //      These might be the parameter to a getIdentifier() call, or could
--            //      be composed into a fully qualified resource name for the getIdentifier()
--            //      method. We match these for *all* resource types.
--            //  (2) Relative source names, e.g. layout/foo, from @layout/foo
--            //      These might be composed into a fully qualified resource name for
--            //      getIdentifier().
--            //  (3) Fully qualified resource names of the form package:type/name.
--            //  (4) If mFoundWebContent is true, look for android_res/ URL strings as well
--
--            if (mFoundWebContent) {
--                Resource resource = getResourceFromFilePath(string);
--                if (resource != null) {
--                    markReachable(resource);
--                    continue;
--                } else {
--                    int start = 0;
--                    int slash = string.lastIndexOf('/');
--                    if (slash != -1) {
--                        start = slash + 1;
--                    }
--                    int dot = string.indexOf('.', start);
--                    String name = string.substring(start, dot != -1 ? dot : string.length());
--                    if (names.contains(name)) {
--                        for (Map<String, Resource> map : mTypeToName.values()) {
--                            resource = map.get(name);
--                            if (mDebug && resource != null) {
--                                System.out.println("Marking " + resource + " used because it "
--                                        + "matches string pool constant " + string);
--                            }
--                            markReachable(resource);
--                        }
--                    }
--                }
--            }
--
--            // Look for normal getIdentifier resource URLs
--            int n = string.length();
--            boolean justName = true;
--            boolean formatting = false;
--            boolean haveSlash = false;
--            for (int i = 0; i < n; i++) {
--                char c = string.charAt(i);
--                if (c == '/') {
--                    haveSlash = true;
--                    justName = false;
--                } else if (c == '.' || c == ':' || c == '%') {
--                    justName = false;
--                    if (c == '%') {
--                        formatting = true;
--                    }
--                } else if (!Character.isJavaIdentifierPart(c)) {
--                    // This shouldn't happen; we've filtered out these strings in
--                    // the {@link #referencedString} method
--                    assert false : string;
--                    break;
--                }
--            }
--
--            String name;
--            if (justName) {
--                // Check name (below)
--                name = string;
--
--                // Check for a simple prefix match, e.g. as in
--                // getResources().getIdentifier("ic_video_codec_" + codecName, "drawable", ...)
--                for (Map<String, Resource> map : mTypeToName.values()) {
--                    for (Resource resource : map.values()) {
--                        if (resource.name.startsWith(name)) {
--                            if (mDebug) {
--                                System.out.println("Marking " + resource + " used because its "
--                                        + "prefix matches string pool constant " + string);
--                            }
--                            markReachable(resource);
--                        }
--                    }
--                }
--            } else if (!haveSlash) {
--                if (formatting) {
--                    // Possibly a formatting string, e.g.
--                    //   String name = String.format("my_prefix_%1d", index);
--                    //   int res = getContext().getResources().getIdentifier(name, "drawable", ...)
--
--                    try {
--                        Pattern pattern = Pattern.compile(convertFormatStringToRegexp(string));
--                        for (Map<String, Resource> map : mTypeToName.values()) {
--                            for (Resource resource : map.values()) {
--                                if (pattern.matcher(resource.name).matches()) {
--                                    if (mDebug) {
--                                        System.out.println("Marking " + resource + " used because "
--                                                + "it format-string matches string pool constant "
--                                                + string);
--                                    }
--                                    markReachable(resource);
--                                }
--                            }
--                        }
--                    } catch (PatternSyntaxException ignored) {
--                        // Might not have been a formatting string after all!
--                    }
--                }
--
--                // If we have more than just a symbol name, we expect to also see a slash
--                //noinspection UnnecessaryContinue
--                continue;
--            } else {
--                // Try to pick out the resource name pieces; if we can find the
--                // resource type unambiguously; if not, just match on names
--                int slash = string.indexOf('/');
--                assert slash != -1; // checked with haveSlash above
--                name = string.substring(slash + 1);
--                if (name.isEmpty() || !names.contains(name)) {
--                    continue;
--                }
--                // See if have a known specific resource type
--                if (slash > 0) {
--                    int colon = string.indexOf(':');
--                    String typeName = string.substring(colon != -1 ? colon + 1 : 0, slash);
--                    ResourceType type = ResourceType.getEnum(typeName);
--                    if (type == null) {
--                        continue;
--                    }
--                    Resource resource = getResource(type, name);
--                    if (mDebug && resource != null) {
--                        System.out.println("Marking " + resource + " used because it "
--                                + "matches string pool constant " + string);
--                    }
--                    markReachable(resource);
--                    continue;
--                }
--
--                // fall through and check the name
--            }
--
--            if (names.contains(name)) {
--                for (Map<String, Resource> map : mTypeToName.values()) {
--                    Resource resource = map.get(name);
--                    if (mDebug && resource != null) {
--                        System.out.println("Marking " + resource + " used because it "
--                                + "matches string pool constant " + string);
--                    }
--                    markReachable(resource);
--                }
--            } else if (Character.isDigit(name.charAt(0))) {
--                // Just a number? There are cases where it calls getIdentifier by
--                // a String number; see for example SuggestionsAdapter in the support
--                // library which reports supporting a string like "2130837524" and
--                // "android.resource://com.android.alarmclock/2130837524".
--                try {
--                    int id = Integer.parseInt(name);
--                    if (id != 0) {
--                        markReachable(mValueToResource.get(id));
--                    }
--                } catch (NumberFormatException e) {
--                    // pass
--                }
--            }
--        }
--    }
--
--    @VisibleForTesting
--    static String convertFormatStringToRegexp(String formatString) {
--        StringBuilder regexp = new StringBuilder();
--        int from = 0;
--        boolean hasEscapedLetters = false;
--        Matcher matcher = StringFormatDetector.FORMAT.matcher(formatString);
--        int length = formatString.length();
--        while (matcher.find(from)) {
--            int start = matcher.start();
--            int end = matcher.end();
--            if (start == 0 && end == length) {
--                // Don't match if the entire string literal starts with % and ends with
--                // the a formatting character, such as just "%d": this just matches absolutely
--                // everything and is unlikely to be used in a resource lookup
--                return NO_MATCH;
--            }
--            if (start > from) {
--                hasEscapedLetters |= appendEscapedPattern(formatString, regexp, from, start);
--            }
--            // If the wildcard follows a previous wildcard, just skip it
--            // (e.g. don't convert %s%s into .*.*; .* is enough.
--            int regexLength = regexp.length();
--            if (regexLength < 2
--                    || regexp.charAt(regexLength - 1) != '*'
--                    || regexp.charAt(regexLength - 2) != '.') {
--                regexp.append(".*");
--            }
--            from = end;
--        }
--
--        if (from < length) {
--            hasEscapedLetters |= appendEscapedPattern(formatString, regexp, from, length);
--        }
--
--        if (!hasEscapedLetters) {
--            // If the regexp contains *only* formatting characters, e.g. "%.0f%d", or
--            // if it contains only formatting characters and punctuation, e.g. "%s_%d",
--            // don't treat this as a possible resource name pattern string: it is unlikely
--            // to be intended for actual resource names, and has the side effect of matching
--            // most names.
--            return NO_MATCH;
--        }
--
--        return regexp.toString();
--    }
--
--    /**
--     * Appends the characters in the range [from,to> from formatString as escaped
--     * regexp characters into the given string builder. Returns true if there were
--     * any letters in the appended text.
--     */
--    private static boolean appendEscapedPattern(@NonNull String formatString,
--            @NonNull StringBuilder regexp, int from, int to) {
--        regexp.append(Pattern.quote(formatString.substring(from, to)));
--
--        for (int i = from; i < to; i++) {
--            if (Character.isLetter(formatString.charAt(i))) {
--                return true;
--            }
--        }
--
--        return false;
--    }
--
--    private void recordResources(File resDir)
--            throws IOException, SAXException, ParserConfigurationException {
--        File[] resourceFolders = resDir.listFiles();
--        if (resourceFolders != null) {
--            for (File folder : resourceFolders) {
--                ResourceFolderType folderType = ResourceFolderType.getFolderType(folder.getName());
--                if (folderType != null) {
--                    recordResources(folderType, folder);
--                }
--            }
--        }
--    }
--
--    private void recordResources(@NonNull ResourceFolderType folderType, File folder)
--            throws ParserConfigurationException, SAXException, IOException {
--        File[] files = folder.listFiles();
--        FolderConfiguration config = FolderConfiguration.getConfigForFolder(folder.getName());
--        boolean isDefaultFolder = false;
--        if (config != null) {
--            isDefaultFolder = true;
--            for (int i = 0, n = FolderConfiguration.getQualifierCount(); i < n; i++) {
--                ResourceQualifier qualifier = config.getQualifier(i);
--                // Densities are special: even if they're present in just (say) drawable-hdpi
--                // we'll match it on any other density
--                if (qualifier != null && !(qualifier instanceof DensityQualifier)) {
--                    isDefaultFolder = false;
--                    break;
--                }
--            }
--        }
--
--        if (files != null) {
--            for (File file : files) {
--                String path = file.getPath();
--                boolean isXml = endsWithIgnoreCase(path, DOT_XML);
--
--                Resource from = null;
--                // Record resource for the whole file
--                if (folderType != ResourceFolderType.VALUES
--                        && (isXml
--                            || endsWith(path, DOT_PNG) //also true for endsWith(name, DOT_9PNG)
--                            || endsWith(path, DOT_JPG)
--                            || endsWith(path, DOT_GIF)
--                            || endsWith(path, DOT_JPEG))) {
--                    List<ResourceType> types = FolderTypeRelationship.getRelatedResourceTypes(
--                            folderType);
--                    ResourceType type = types.get(0);
--                    assert type != ResourceType.ID : folderType;
--                    String name = file.getName();
--                    name = name.substring(0, name.indexOf('.'));
--                    Resource resource = getResource(type, name);
--                    if (resource != null) {
--                        resource.addLocation(file);
--                        if (isDefaultFolder) {
--                            resource.hasDefault = true;
--                        }
--                        from = resource;
--                    }
--                }
--
--                if (isXml) {
--                    // For value files, and drawables and colors etc also pull in resource
--                    // references inside the file
--                    recordXmlResourcesUsages(file, isDefaultFolder, from);
--                    if (folderType == ResourceFolderType.XML) {
--                        tokenizeUnknownText(Files.toString(file, UTF_8));
--                    }
--                } else if (folderType == ResourceFolderType.RAW) {
--                    // Is this an HTML, CSS or JavaScript document bundled with the app?
--                    // If so tokenize and look for resource references.
--                    if (endsWithIgnoreCase(path, ".html") || endsWithIgnoreCase(path, ".htm")) {
--                        tokenizeHtml(from, Files.toString(file, UTF_8));
--                    } else if (endsWithIgnoreCase(path, ".css")) {
--                        tokenizeCss(from, Files.toString(file, UTF_8));
--                    } else if (endsWithIgnoreCase(path, ".js")) {
--                        tokenizeJs(from, Files.toString(file, UTF_8));
--                    } else if (file.isFile() && !LintUtils.isBitmapFile(file)) {
--                        tokenizeUnknownBinary(file);
--                    }
--                }
--            }
--        }
--    }
--
--    private void recordMapping(@Nullable File mapping) throws IOException {
--        if (mapping == null || !mapping.exists()) {
--            return;
--        }
--        final String ARROW = " -> ";
--        final String RESOURCE = ".R$";
--        for (String line : Files.readLines(mapping, UTF_8)) {
--            if (line.startsWith(" ") || line.startsWith("\t")) {
--                continue;
--            }
--            int index = line.indexOf(RESOURCE);
--            if (index == -1) {
--                continue;
--            }
--            int arrow = line.indexOf(ARROW, index + 3);
--            if (arrow == -1) {
--                continue;
--            }
--            String typeName = line.substring(index + RESOURCE.length(), arrow);
--            ResourceType type = ResourceType.getEnum(typeName);
--            if (type == null) {
--                continue;
--            }
--            int end = line.indexOf(':', arrow + ARROW.length());
--            if (end == -1) {
--                end = line.length();
--            }
--            String target = line.substring(arrow + ARROW.length(), end).trim();
--            String ownerName = target.replace('.', '/');
--            mResourceClassOwners.put(ownerName, type);
--        }
--    }
--
--    private void recordManifestUsages(File manifest)
--            throws IOException, ParserConfigurationException, SAXException {
--        String xml = Files.toString(manifest, UTF_8);
--        Document document = XmlUtils.parseDocument(xml, true);
--        recordManifestUsages(document.getDocumentElement());
--    }
--
--    private void recordXmlResourcesUsages(@NonNull File file, boolean isDefaultFolder,
--            @Nullable Resource from)
--            throws IOException, ParserConfigurationException, SAXException {
--        String xml = Files.toString(file, UTF_8);
--        Document document = XmlUtils.parseDocument(xml, true);
--        recordResourceReferences(file, isDefaultFolder, document.getDocumentElement(), from);
--    }
--
--    private void tokenizeHtml(@Nullable Resource from, @NonNull String  html) {
--        // Look for
--        //    (1) URLs of the form /android_res/drawable/foo.ext
--        //        which we will use to keep R.drawable.foo
--        // and
--        //    (2) Filenames. If the web content is loaded with something like
--        //        WebView.loadDataWithBaseURL("file:///android_res/drawable/", ...)
--        //        this is similar to Resources#getIdentifier handling where all
--        //        *potentially* aliased filenames are kept to play it safe.
--
--        // Simple HTML tokenizer
--        int length = html.length();
--        final int STATE_TEXT = 1;
--        final int STATE_SLASH = 2;
--        final int STATE_ATTRIBUTE_NAME = 3;
--        final int STATE_BEFORE_TAG = 4;
--        final int STATE_IN_TAG = 5;
--        final int STATE_BEFORE_ATTRIBUTE = 6;
--        final int STATE_ATTRIBUTE_BEFORE_EQUALS = 7;
--        final int STATE_ATTRIBUTE_AFTER_EQUALS = 8;
--        final int STATE_ATTRIBUTE_VALUE_NONE = 9;
--        final int STATE_ATTRIBUTE_VALUE_SINGLE = 10;
--        final int STATE_ATTRIBUTE_VALUE_DOUBLE = 11;
--        final int STATE_CLOSE_TAG = 12;
--
--        int state = STATE_TEXT;
--        int offset = 0;
--        int valueStart = 0;
--        int tagStart = 0;
--        String tag = null;
--        String attribute = null;
--        int attributeStart = 0;
--        int prev = -1;
--        while (offset < length) {
--            if (offset == prev) {
--                // Purely here to prevent potential bugs in the state machine from looping
--                // infinitely
--                offset++;
--            }
--            prev = offset;
--
--
--            char c = html.charAt(offset);
--
--            // MAke sure I handle doctypes properly.
--            // Make sure I handle cdata properly.
--            // Oh and what about <style> tags? tokenize everything inside as CSS!
--            // ANd <script> tag content as js!
--            switch (state) {
--                case STATE_TEXT: {
--                    if (c == '<') {
--                        state = STATE_SLASH;
--                        offset++;
--                        continue;
--                    }
--
--                    // Other text is just ignored
--                    offset++;
--                    break;
--                }
--
--                case STATE_SLASH: {
--                    if (c == '!') {
--                        if (html.startsWith("!--", offset)) {
--                            // Comment
--                            int end = html.indexOf("-->", offset + 3);
--                            if (end == -1) {
--                                offset = length;
--                                break;
--                            }
--                            offset = end + 3;
--                            continue;
--                        } else if (html.startsWith("![CDATA[", offset)) {
--                            // Skip CDATA text content; HTML text is irrelevant to this tokenizer
--                            // anyway
--                            int end = html.indexOf("]]>", offset + 8);
--                            if (end == -1) {
--                                offset = length;
--                                break;
--                            }
--                            offset = end + 3;
--                            continue;
--                        }
--                    } else if (c == '/') {
--                        state = STATE_CLOSE_TAG;
--                        offset++;
--                        continue;
--                    } else if (c == '?') {
--                        // XML Prologue
--                        int end = html.indexOf('>', offset + 2);
--                        if (end == -1) {
--                            offset = length;
--                            break;
--                        }
--                        offset = end + 1;
--                        continue;
--                    }
--                    state = STATE_IN_TAG;
--                    tagStart = offset;
--                    break;
--                }
--
--                case STATE_CLOSE_TAG: {
--                    if (c == '>') {
--                        state = STATE_TEXT;
--                    }
--                    offset++;
--                    break;
--                }
--
--                case STATE_BEFORE_TAG: {
--                    if (!Character.isWhitespace(c)) {
--                        state = STATE_IN_TAG;
--                        tagStart = offset;
--                    }
--                    // (For an end tag we'll include / in the tag name here)
--                    offset++;
--                    break;
--                }
--                case STATE_IN_TAG: {
--                    if (Character.isWhitespace(c)) {
--                        state = STATE_BEFORE_ATTRIBUTE;
--                        tag = html.substring(tagStart, offset).trim();
--                    } else if (c == '>') {
--                        tag = html.substring(tagStart, offset).trim();
--                        endHtmlTag(from, html, offset, tag);
--                        state = STATE_TEXT;
--                    }
--                    offset++;
--                    break;
--                }
--                case STATE_BEFORE_ATTRIBUTE: {
--                    if (c == '>') {
--                        endHtmlTag(from, html, offset, tag);
--                        state = STATE_TEXT;
--                    } else //noinspection StatementWithEmptyBody
--                        if (c == '/') {
--                        // we expect an '>' next to close the tag
--                    } else if (!Character.isWhitespace(c)) {
--                        state = STATE_ATTRIBUTE_NAME;
--                        attributeStart = offset;
--                    }
--                    offset++;
--                    break;
--                }
--                case STATE_ATTRIBUTE_NAME: {
--                    if (c == '>') {
--                        endHtmlTag(from, html, offset, tag);
--                        state = STATE_TEXT;
--                    } else if (c == '=') {
--                        attribute = html.substring(attributeStart, offset);
--                        state = STATE_ATTRIBUTE_AFTER_EQUALS;
--                    } else if (Character.isWhitespace(c)) {
--                        attribute = html.substring(attributeStart, offset);
--                        state = STATE_ATTRIBUTE_BEFORE_EQUALS;
--                    }
--                    offset++;
--                    break;
--                }
--                case STATE_ATTRIBUTE_BEFORE_EQUALS: {
--                    if (c == '=') {
--                        state = STATE_ATTRIBUTE_AFTER_EQUALS;
--                    } else if (c == '>') {
--                        endHtmlTag(from, html, offset, tag);
--                        state = STATE_TEXT;
--                    } else if (!Character.isWhitespace(c)) {
--                        // Attribute value not specified (used for some boolean attributes)
--                        state = STATE_ATTRIBUTE_NAME;
--                        attributeStart = offset;
--                    }
--                    offset++;
--                    break;
--                }
--
--                case STATE_ATTRIBUTE_AFTER_EQUALS: {
--                    if (c == '\'') {
--                        // a='b'
--                        state = STATE_ATTRIBUTE_VALUE_SINGLE;
--                        valueStart = offset + 1;
--                    } else if (c == '"') {
--                        // a="b"
--                        state = STATE_ATTRIBUTE_VALUE_DOUBLE;
--                        valueStart = offset + 1;
--                    } else if (!Character.isWhitespace(c)) {
--                        // a=b
--                        state = STATE_ATTRIBUTE_VALUE_NONE;
--                        valueStart = offset + 1;
--                    }
--                    offset++;
--                    break;
--                }
--
--                case STATE_ATTRIBUTE_VALUE_SINGLE: {
--                    if (c == '\'') {
--                        state = STATE_BEFORE_ATTRIBUTE;
--                        recordHtmlAttributeValue(from, tag, attribute,
--                                html.substring(valueStart, offset));
--                    }
--                    offset++;
--                    break;
--                }
--                case STATE_ATTRIBUTE_VALUE_DOUBLE: {
--                    if (c == '"') {
--                        state = STATE_BEFORE_ATTRIBUTE;
--                        recordHtmlAttributeValue(from, tag, attribute,
--                                html.substring(valueStart, offset));
--                    }
--                    offset++;
--                    break;
--                }
--                case STATE_ATTRIBUTE_VALUE_NONE: {
--                    if (c == '>') {
--                        recordHtmlAttributeValue(from, tag, attribute,
--                                html.substring(valueStart, offset));
--                        endHtmlTag(from, html, offset, tag);
--                        state = STATE_TEXT;
--                    } else if (Character.isWhitespace(c)) {
--                        state = STATE_BEFORE_ATTRIBUTE;
--                        recordHtmlAttributeValue(from, tag, attribute,
--                                html.substring(valueStart, offset));
--                    }
--                    offset++;
--                    break;
--                }
--                default:
--                    assert false : state;
--            }
--        }
--    }
--
--    private void endHtmlTag(@Nullable Resource from, @NonNull String html, int offset,
--            @Nullable String tag) {
--        if ("script".equals(tag)) {
--            int end = html.indexOf("</script>", offset + 1);
--            if (end != -1) {
--                // Attempt to tokenize the text as JavaScript
--                String js = html.substring(offset + 1, end);
--                tokenizeJs(from, js);
--            }
--        } else if ("style".equals(tag)) {
--            int end = html.indexOf("</style>", offset + 1);
--            if (end != -1) {
--                // Attempt to tokenize the text as CSS
--                String css = html.substring(offset + 1, end);
--                tokenizeCss(from, css);
--            }
--        }
--    }
--
--    private void tokenizeJs(@Nullable Resource from, @NonNull String js) {
--        // Simple JavaScript tokenizer: only looks for literal strings,
--        // and records those as string references
--        int length = js.length();
--        final int STATE_INIT = 1;
--        final int STATE_SLASH = 2;
--        final int STATE_STRING_DOUBLE = 3;
--        final int STATE_STRING_DOUBLE_QUOTED = 4;
--        final int STATE_STRING_SINGLE = 5;
--        final int STATE_STRING_SINGLE_QUOTED = 6;
--
--        int state = STATE_INIT;
--        int offset = 0;
--        int stringStart = 0;
--        int prev = -1;
--        while (offset < length) {
--            if (offset == prev) {
--                // Purely here to prevent potential bugs in the state machine from looping
--                // infinitely
--                offset++;
--            }
--            prev = offset;
--
--            char c = js.charAt(offset);
--            switch (state) {
--                case STATE_INIT: {
--                    if (c == '/') {
--                        state = STATE_SLASH;
--                    } else if (c == '"') {
--                        stringStart = offset + 1;
--                        state = STATE_STRING_DOUBLE;
--                    } else if (c == '\'') {
--                        stringStart = offset + 1;
--                        state = STATE_STRING_SINGLE;
--                    }
--                    offset++;
--                    break;
--                }
--                case STATE_SLASH: {
--                    if (c == '*') {
--                        // Comment block
--                        state = STATE_INIT;
--                        int end = js.indexOf("*/", offset + 1);
--                        if (end == -1) {
--                            offset = length; // unterminated
--                            break;
--                        }
--                        offset = end + 2;
--                        continue;
--                    } else if (c == '/') {
--                        // Line comment
--                        state = STATE_INIT;
--                        int end = js.indexOf('\n', offset + 1);
--                        if (end == -1) {
--                            offset = length;
--                            break;
--                        }
--                        offset = end + 1;
--                        continue;
--                    } else {
--                        // division - just continue
--                        state = STATE_INIT;
--                        offset++;
--                        break;
--                    }
--                }
--                case STATE_STRING_DOUBLE: {
--                    if (c == '"') {
--                        recordJsString(js.substring(stringStart, offset));
--                        state = STATE_INIT;
--                    } else if (c == '\\') {
--                        state = STATE_STRING_DOUBLE_QUOTED;
--                    }
--                    offset++;
--                    break;
--                }
--                case STATE_STRING_DOUBLE_QUOTED: {
--                    state = STATE_STRING_DOUBLE;
--                    offset++;
--                    break;
--                }
--                case STATE_STRING_SINGLE: {
--                    if (c == '\'') {
--                        recordJsString(js.substring(stringStart, offset));
--                        state = STATE_INIT;
--                    } else if (c == '\\') {
--                        state = STATE_STRING_SINGLE_QUOTED;
--                    }
--                    offset++;
--                    break;
--                }
--                case STATE_STRING_SINGLE_QUOTED: {
--                    state = STATE_STRING_SINGLE;
--                    offset++;
--                    break;
--                }
--                default:
--                    assert false : state;
--            }
--        }
--    }
--
--    private void tokenizeCss(@Nullable Resource from, @NonNull String  css) {
--        // Simple CSS tokenizer: Only looks for URL references, and records those
--        // filenames. Skips everything else (unrelated to images).
--        int length = css.length();
--        final int STATE_INIT = 1;
--        final int STATE_SLASH = 2;
--        int state = STATE_INIT;
--        int offset = 0;
--        int prev = -1;
--        while (offset < length) {
--            if (offset == prev) {
--                // Purely here to prevent potential bugs in the state machine from looping
--                // infinitely
--                offset++;
--            }
--            prev = offset;
--
--            char c = css.charAt(offset);
--            switch (state) {
--                case STATE_INIT: {
--                    if (c == '/') {
--                        state = STATE_SLASH;
--                    } else if (c == 'u' && css.startsWith("url(", offset) && offset > 0) {
--                        char prevChar = css.charAt(offset-1);
--                        if (Character.isWhitespace(prevChar) || prevChar == ':') {
--                            int end = css.indexOf(')', offset);
--                            offset += 4; // skip url(
--                            while (offset < length && Character.isWhitespace(css.charAt(offset))) {
--                                offset++;
--                            }
--                            if (end != -1 && end > offset + 1) {
--                                while (end > offset
--                                        && Character.isWhitespace(css.charAt(end - 1))) {
--                                    end--;
--                                }
--                                if ((css.charAt(offset) == '"'
--                                        && css.charAt(end - 1) == '"')
--                                        || (css.charAt(offset) == '\''
--                                        && css.charAt(end - 1) == '\'')) {
--                                    // Strip " or '
--                                    offset++;
--                                    end--;
--                                }
--                                recordCssUrl(from, css.substring(offset, end).trim());
--                            }
--                            offset = end + 1;
--                            continue;
--                        }
--
--                    }
--                    offset++;
--                    break;
--                }
--                case STATE_SLASH: {
--                    if (c == '*') {
--                        // CSS comment? Skip the whole block rather than staying within the
--                        // character tokenizer.
--                        int end = css.indexOf("*/", offset + 1);
--                        if (end == -1) {
--                            offset = length;
--                            break;
--                        }
--                        offset = end + 2;
--                        continue;
--                    }
--                    state = STATE_INIT;
--                    offset++;
--                    break;
--                }
--                default:
--                    assert false : state;
--            }
--        }
--    }
--
--    private static byte[] sAndroidResBytes;
--
--    /** Look through binary/unknown files looking for resource URLs */
--    private void tokenizeUnknownBinary(@NonNull File file) {
--        try {
--            if (sAndroidResBytes == null) {
--                sAndroidResBytes = ANDROID_RES.getBytes(SdkConstants.UTF_8);
--            }
--            byte[] bytes = Files.toByteArray(file);
--            int index = 0;
--            while (index != -1) {
--                index = indexOf(bytes, sAndroidResBytes, index);
--                if (index != -1) {
--                    index += sAndroidResBytes.length;
--
--                    // Find the end of the URL
--                    int begin = index;
--                    int end = begin;
--                    for (; end < bytes.length; end++) {
--                        byte c = bytes[end];
--                        if (c != '/' && !Character.isJavaIdentifierPart((char)c)) {
--                            // android_res/raw/my_drawable.png => @raw/my_drawable
--                            String url = "@" + new String(bytes, begin, end - begin, UTF_8);
--                            markReachable(getResourceFromUrl(url));
--                            break;
--                        }
--                    }
--                }
--            }
--        } catch (IOException e) {
--            // Ignore
--        }
--    }
--
--    /**
--     * Returns the index of the given target array in the first array, looking from the given
--     * index
--     */
--    private static int indexOf(byte[] array, byte[] target, int fromIndex) {
--        outer:
--        for (int i = fromIndex; i < array.length - target.length + 1; i++) {
--            for (int j = 0; j < target.length; j++) {
--                if (array[i + j] != target[j]) {
--                    continue outer;
--                }
--            }
--            return i;
--        }
--        return -1;
--    }
--
--    /** Look through text files of unknown structure looking for resource URLs */
--    private void tokenizeUnknownText(@NonNull String text) {
--        int index = 0;
--        while (index != -1) {
--            index = text.indexOf(ANDROID_RES, index);
--            if (index != -1) {
--                index += ANDROID_RES.length();
--
--                // Find the end of the URL
--                int begin = index;
--                int end = begin;
--                int length = text.length();
--                for (; end < length; end++) {
--                    char c = text.charAt(end);
--                    if (c != '/' && !Character.isJavaIdentifierPart(c)) {
--                        // android_res/raw/my_drawable.png => @raw/my_drawable
--                        markReachable(getResourceFromUrl("@" + text.substring(begin, end)));
--                        break;
--                    }
--                }
--            }
--        }
--    }
--
--    private void recordCssUrl(@Nullable Resource from, @NonNull String value) {
--        if (!referencedUrl(from, value)) {
--            referencedString(value);
--            mFoundWebContent = true;
--        }
--    }
--
--    /**
--     * See if the given URL is a URL that we can resolve to a specific resource; if so,
--     * record it and return true, otherwise returns false.
--     */
--    private boolean referencedUrl(@Nullable Resource from, @NonNull String url) {
--        Resource resource = getResourceFromFilePath(url);
--        if (resource != null) {
--            if (from != null) {
--                from.addReference(resource);
--            } else {
--                // We don't have an inclusion context, so just assume this resource is reachable
--                markReachable(resource);
--            }
--            return true;
--        }
--
--        return false;
--    }
--
--    private void recordHtmlAttributeValue(@Nullable Resource from, @Nullable String tagName,
--            @Nullable String attribute, @NonNull String value) {
--        if ("href".equals(attribute) || "src".equals(attribute)) {
--            // In general we'd need to unescape the HTML here (e.g. remove entities) but
--            // those wouldn't be valid characters in the resource name anyway
--            if (!referencedUrl(from, value)) {
--                referencedString(value);
--                mFoundWebContent = true;
--            }
--
--            // If this document includes another, record the reachability of that script/resource
--            if (from != null) {
--                from.addReference(getResourceFromFilePath(attribute));
--            }
--        }
--    }
--
--    private void recordJsString(@NonNull String string) {
--        referencedString(string);
--    }
--
--    @Nullable
--    private Resource getResource(@NonNull ResourceType type, @NonNull String name) {
--        Map<String, Resource> nameMap = mTypeToName.get(type);
--        if (nameMap != null) {
--            return nameMap.get(getFieldName(name));
--        }
--        return null;
--    }
--
--    @Nullable
--    private Resource getResourceFromUrl(@NonNull String possibleUrlReference) {
--        ResourceUrl url = ResourceUrl.parse(possibleUrlReference);
--        if (url != null && !url.framework) {
--            return getResource(url.type, url.name);
--        }
--
--        return null;
--    }
--
--    @Nullable
--    private Resource getResourceFromFilePath(@NonNull String url) {
--        int nameSlash = url.lastIndexOf('/');
--        if (nameSlash == -1) {
--            return null;
--        }
--
--        // Look for
--        //   (1) a full resource URL: /android_res/type/name.ext
--        //   (2) a partial URL that uniquely identifies a given resource: drawable/name.ext
--        // e.g. file:///android_res/drawable/bar.png
--        int androidRes = url.indexOf(ANDROID_RES);
--        if (androidRes != -1) {
--            androidRes += ANDROID_RES.length();
--            int slash = url.indexOf('/', androidRes);
--            if (slash != -1) {
--                String folderName = url.substring(androidRes, slash);
--                ResourceFolderType folderType = ResourceFolderType.getFolderType(folderName);
--                if (folderType != null) {
--                    List<ResourceType> types = FolderTypeRelationship.getRelatedResourceTypes(
--                            folderType);
--                    if (!types.isEmpty()) {
--                        ResourceType type = types.get(0);
--                        int nameBegin = slash + 1;
--                        int dot = url.indexOf('.', nameBegin);
--                        String name = url.substring(nameBegin, dot != -1 ? dot : url.length());
--                        return getResource(type, name);
--                    }
--                }
--            }
--        }
--
--        // Some other relative path. Just look from the end:
--        int typeSlash = url.lastIndexOf('/', nameSlash - 1);
--        ResourceType type = ResourceType.getEnum(url.substring(typeSlash + 1, nameSlash));
--        if (type != null) {
--            int nameBegin = nameSlash + 1;
--            int dot = url.indexOf('.', nameBegin);
--            String name = url.substring(nameBegin, dot != -1 ? dot : url.length());
--            return getResource(type, name);
--        }
--
--        return null;
--    }
--
--    private void recordManifestUsages(Node node) {
--        short nodeType = node.getNodeType();
--        if (nodeType == Node.ELEMENT_NODE) {
--            Element element = (Element) node;
--            NamedNodeMap attributes = element.getAttributes();
--            for (int i = 0, n = attributes.getLength(); i < n; i++) {
--                Attr attr = (Attr) attributes.item(i);
--                markReachable(getResourceFromUrl(attr.getValue()));
--            }
--        } else if (nodeType == Node.TEXT_NODE) {
--            // Does this apply to any manifests??
--            String text = node.getNodeValue().trim();
--            markReachable(getResourceFromUrl(text));
--        }
--
--        NodeList children = node.getChildNodes();
--        for (int i = 0, n = children.getLength(); i < n; i++) {
--            Node child = children.item(i);
--            recordManifestUsages(child);
--        }
--    }
--
--
--    private void recordResourceReferences(@NonNull File file, boolean isDefaultFolder,
--            @NonNull Node node, @Nullable Resource from) {
--        short nodeType = node.getNodeType();
--        if (nodeType == Node.ELEMENT_NODE) {
--            Element element = (Element) node;
--            if (from != null) {
--                NamedNodeMap attributes = element.getAttributes();
--                for (int i = 0, n = attributes.getLength(); i < n; i++) {
--                    Attr attr = (Attr) attributes.item(i);
--
--                    // Ignore tools: namespace attributes, unless it's
--                    // a keep attribute
--                    if (TOOLS_URI.equals(attr.getNamespaceURI())) {
--                        handleToolsAttribute(attr);
--                        // Skip all other tools: attributes
--                        continue;
--                    }
--
--                    Resource resource = getResourceFromUrl(attr.getValue());
--                    if (resource != null) {
--                        from.addReference(resource);
--                    }
--                }
--
--                // Android Wear. We *could* limit ourselves to only doing this in files
--                // referenced from a manifest meta-data element, e.g.
--                // <meta-data android:name="com.google.android.wearable.beta.app"
--                //    android:resource="@xml/wearable_app_desc"/>
--                // but given that that property has "beta" in the name, it seems likely
--                // to change and therefore hardcoding it for that key risks breakage
--                // in the future.
--                if ("rawPathResId".equals(element.getTagName())) {
--                    StringBuilder sb = new StringBuilder();
--                    NodeList children = node.getChildNodes();
--                    for (int i = 0, n = children.getLength(); i < n; i++) {
--                        Node child = children.item(i);
--                        if (child.getNodeType() == Element.TEXT_NODE
--                                || child.getNodeType() == Element.CDATA_SECTION_NODE) {
--                            sb.append(child.getNodeValue());
--                        }
--                    }
--                    if (sb.length() > 0) {
--                        Resource resource = getResource(ResourceType.RAW, sb.toString().trim());
--                        from.addReference(resource);
--                    }
--                }
--            } else {
--                // Look for keep attributes everywhere else since they don't require a source
--                handleToolsAttribute(element.getAttributeNodeNS(TOOLS_URI, ATTR_KEEP));
--                handleToolsAttribute(element.getAttributeNodeNS(TOOLS_URI, ATTR_DISCARD));
--                handleToolsAttribute(element.getAttributeNodeNS(TOOLS_URI, ATTR_SHRINK_MODE));
--            }
--
--            Resource definition = getResource(element);
--            if (definition != null) {
--                from = definition;
--                definition.addLocation(file);
--                if (isDefaultFolder) {
--                    definition.hasDefault = true;
--                }
--            }
--
--            String tagName = element.getTagName();
--            if (TAG_STYLE.equals(tagName)) {
--                if (element.hasAttribute(ATTR_PARENT)) {
--                    String parent = element.getAttribute(ATTR_PARENT);
--                    if (!parent.isEmpty() && !parent.startsWith(ANDROID_STYLE_RESOURCE_PREFIX) &&
--                            !parent.startsWith(PREFIX_ANDROID)) {
--                        String parentStyle = parent;
--                        if (!parentStyle.startsWith(STYLE_RESOURCE_PREFIX)) {
--                            parentStyle = STYLE_RESOURCE_PREFIX + parentStyle;
--                        }
--                        Resource ps = getResourceFromUrl(getFieldName(parentStyle));
--                        if (ps != null && definition != null) {
--                            definition.addReference(ps);
--                        }
--                    }
--                } else {
--                    // Implicit parent styles by name
--                    String name = getFieldName(element);
--                    while (true) {
--                        int index = name.lastIndexOf('_');
--                        if (index != -1) {
--                            name = name.substring(0, index);
--                            Resource ps = getResourceFromUrl(
--                                    STYLE_RESOURCE_PREFIX + getFieldName(name));
--                            if (ps != null && definition != null) {
--                                definition.addReference(ps);
--                            }
--                        } else {
--                            break;
--                        }
--                    }
--                }
--            }
--
--            if (TAG_ITEM.equals(tagName)) {
--                // In style? If so the name: attribute can be a reference
--                if (element.getParentNode() != null
--                        && element.getParentNode().getNodeName().equals(TAG_STYLE)) {
--                    String name = element.getAttributeNS(ANDROID_URI, ATTR_NAME);
--                    if (!name.isEmpty() && !name.startsWith("android:")) {
--                        Resource resource = getResource(ResourceType.ATTR, name);
--                        if (definition == null) {
--                            Element style = (Element) element.getParentNode();
--                            definition = getResource(style);
--                            if (definition != null) {
--                                from = definition;
--                                definition.addReference(resource);
--                            }
--                        }
--                    }
--                }
--            }
--        } else if (nodeType == Node.TEXT_NODE || nodeType == Node.CDATA_SECTION_NODE) {
--            String text = node.getNodeValue().trim();
--            Resource textResource = getResourceFromUrl(getFieldName(text));
--            if (textResource != null && from != null) {
--                from.addReference(textResource);
--            }
--        }
--
--        NodeList children = node.getChildNodes();
--        for (int i = 0, n = children.getLength(); i < n; i++) {
--            Node child = children.item(i);
--            recordResourceReferences(file, isDefaultFolder, child, from);
--        }
--    }
--
--    private void handleToolsAttribute(@Nullable Attr attr) {
--        if (attr == null) {
--            return;
--        }
--        String localName = attr.getLocalName();
--        String value = attr.getValue();
--        if (ATTR_KEEP.equals(localName)) {
--            handleKeepAttribute(value);
--        } else if (ATTR_DISCARD.equals(localName)) {
--            handleRemoveAttribute(value);
--        } else if (ATTR_SHRINK_MODE.equals(localName)) {
--            if (VALUE_STRICT.equals(value)) {
--                mGuessKeep = false;
--            } else if (VALUE_SAFE.equals(value)) {
--                mGuessKeep = true;
--            } else if (mDebug) {
--                System.out.println("Ignoring unknown " + ATTR_SHRINK_MODE + " " + value);
--            }
--            if (mDebug) {
--                System.out.println("Setting shrink mode to " + value);
--            }
--        }
--    }
--
--    public static String getFieldName(@NonNull String styleName) {
--        return styleName.replace('.', '_').replace('-', '_').replace(':', '_');
--    }
--
--    /**
--     * Marks the given resource (if non-null) as reachable, and returns true if
--     * this is the first time the resource is marked reachable
--     */
--    private static boolean markReachable(@Nullable Resource resource) {
--        if (resource != null) {
--            boolean wasReachable = resource.reachable;
--            resource.reachable = true;
--            return !wasReachable;
--        }
--
--        return false;
--    }
--
--    private static void markUnreachable(@Nullable Resource resource) {
--        if (resource != null) {
--            resource.reachable = false;
--        }
--    }
--
--    /**
--     * Called for a tools:keep attribute containing a resource URL where that resource name
--     * is not referencing a known resource
--     *
--     * @param value The keep value
--     */
--    private void handleKeepAttribute(@NonNull String value) {
--        // Handle comma separated lists of URLs and globs
--        if (value.indexOf(',') != -1) {
--            for (String portion : Splitter.on(',').omitEmptyStrings().trimResults().split(value)) {
--                handleKeepAttribute(portion);
--            }
--            return;
--        }
--
--        ResourceUrl url = ResourceUrl.parse(value);
--        if (url == null || url.framework) {
--            return;
--        }
--
--        Resource resource = getResource(url.type, url.name);
--        if (resource != null) {
--            if (mDebug) {
--                System.out.println("Marking " + resource + " used because it "
--                        + "matches keep attribute " + value);
--            }
--            markReachable(resource);
--        } else if (url.name.contains("*") || url.name.contains("?")) {
--            // Look for globbing patterns
--            String regexp = DefaultConfiguration.globToRegexp(getFieldName(url.name));
--            try {
--                Pattern pattern = Pattern.compile(regexp);
--                Map<String, Resource> nameMap = mTypeToName.get(url.type);
--                if (nameMap != null) {
--                    for (Resource r : nameMap.values()) {
--                        if (pattern.matcher(r.name).matches()) {
--                            if (mDebug) {
--                                System.out.println("Marking " + r + " used because it "
--                                        + "matches keep globbing pattern " + url.name);
--                            }
--
--                            markReachable(r);
--                        }
--                    }
--                }
--            } catch (PatternSyntaxException ignored) {
--                if (mDebug) {
--                    System.out.println("Could not compute keep globbing pattern for " +
--                            url.name + ": tried regexp " + regexp + "(" + ignored + ")");
--                }
--            }
--        }
--    }
--
--    private void handleRemoveAttribute(@NonNull String value) {
--        // Handle comma separated lists of URLs and globs
--        if (value.indexOf(',') != -1) {
--            for (String portion : Splitter.on(',').omitEmptyStrings().trimResults().split(value)) {
--                handleRemoveAttribute(portion);
--            }
--            return;
--        }
--
--        ResourceUrl url = ResourceUrl.parse(value);
--        if (url == null || url.framework) {
--            return;
--        }
--
--        Resource resource = getResource(url.type, url.name);
--        if (resource != null) {
--            if (mDebug) {
--                System.out.println("Marking " + resource + " used because it "
--                        + "matches remove attribute " + value);
--            }
--            markUnreachable(resource);
--        } else if (url.name.contains("*") || url.name.contains("?")) {
--            // Look for globbing patterns
--            String regexp = DefaultConfiguration.globToRegexp(getFieldName(url.name));
--            try {
--                Pattern pattern = Pattern.compile(regexp);
--                Map<String, Resource> nameMap = mTypeToName.get(url.type);
--                if (nameMap != null) {
--                    for (Resource r : nameMap.values()) {
--                        if (pattern.matcher(r.name).matches()) {
--                            if (mDebug) {
--                                System.out.println("Marking " + r + " used because it "
--                                        + "matches remove globbing pattern " + url.name);
--                            }
--
--                            markUnreachable(r);
--                        }
--                    }
--                }
--            } catch (PatternSyntaxException ignored) {
--                if (mDebug) {
--                    System.out.println("Could not compute remove globbing pattern for " +
--                            url.name + ": tried regexp " + regexp + "(" + ignored + ")");
--                }
--            }
--        }
--    }
--
--    private Set<String> mStrings;
--    private boolean mFoundGetIdentifier;
--    private boolean mFoundWebContent;
--
--    private void referencedString(@NonNull String string) {
--        // See if the string is at all eligible; ignore strings that aren't
--        // identifiers (has java identifier chars and nothing but .:/), or are empty or too long
--        // We also allow "%", used for formatting strings.
--        if (string.isEmpty() || string.length() > 80) {
--            return;
--        }
--        boolean haveIdentifierChar = false;
--        for (int i = 0, n = string.length(); i < n; i++) {
--            char c = string.charAt(i);
--            boolean identifierChar = Character.isJavaIdentifierPart(c);
--            if (!identifierChar && c != '.' && c != ':' && c != '/' && c != '%') {
--                // .:/ are for the fully qualified resource names, or for resource URLs or
--                // relative file names
--                return;
--            } else if (identifierChar) {
--                haveIdentifierChar = true;
--            }
--        }
--        if (!haveIdentifierChar) {
--            return;
--        }
--
--        if (mStrings == null) {
--            mStrings = Sets.newHashSetWithExpectedSize(300);
--        }
--        mStrings.add(string);
--
--        if (!mFoundWebContent && string.contains(ANDROID_RES)) {
--            mFoundWebContent = true;
--        }
--    }
--
--    private void recordUsages(File jarFile) throws IOException {
--        if (!jarFile.exists()) {
--            return;
--        }
--        ZipInputStream zis = null;
--        try {
--            FileInputStream fis = new FileInputStream(jarFile);
--            try {
--                zis = new ZipInputStream(fis);
--                ZipEntry entry = zis.getNextEntry();
--                while (entry != null) {
--                    String name = entry.getName();
--                    if (name.endsWith(DOT_CLASS) &&
--                            // Skip resource type classes like R$drawable; they will
--                            // reference the integer id's we're looking for, but these aren't
--                            // actual usages we need to track; if somebody references the
--                            // field elsewhere, we'll catch that
--                            !isResourceClass(name)) {
--                        byte[] bytes = ByteStreams.toByteArray(zis);
--                        if (bytes != null) {
--                            ClassReader classReader = new ClassReader(bytes);
--                            classReader.accept(new UsageVisitor(jarFile, name),
--                                    SKIP_DEBUG | SKIP_FRAMES);
--                        }
--                    }
--
--                    entry = zis.getNextEntry();
--                }
--            } finally {
--                Closeables.close(fis, true);
--            }
--        } finally {
--            Closeables.close(zis, true);
--        }
--    }
--
--    /** Returns whether the given class path points to an aapt-generated compiled R class */
--    @VisibleForTesting
--    static boolean isResourceClass(@NonNull String name) {
--        assert name.endsWith(DOT_CLASS) : name;
--        int index = name.lastIndexOf('/');
--        if (index != -1 && name.startsWith("R$", index + 1)) {
--            String typeName = name.substring(index + 3, name.length() - DOT_CLASS.length());
--            return ResourceType.getEnum(typeName) != null;
--        }
--
--        return false;
--    }
--
--    private void gatherResourceValues(File file) throws IOException {
--        if (file.isDirectory()) {
--            File[] children = file.listFiles();
--            if (children != null) {
--                for (File child : children) {
--                    gatherResourceValues(child);
--                }
--            }
--        } else if (file.isFile() && file.getName().equals(SdkConstants.FN_RESOURCE_CLASS)) {
--            parseResourceClass(file);
--        }
--    }
--
--    // TODO: Use Lombok/ECJ here
--    private void parseResourceClass(File file) throws IOException {
--        String s = Files.toString(file, UTF_8);
--        // Simple parser which handles only aapt's special R output
--        String pkg = null;
--        int index = s.indexOf("package ");
--        if (index != -1) {
--            int end = s.indexOf(';', index);
--            pkg = s.substring(index + "package ".length(), end).trim().replace('.', '/');
--        }
--        index = 0;
--        int length = s.length();
--        String classDeclaration = "public static final class ";
--        while (true) {
--            index = s.indexOf(classDeclaration, index);
--            if (index == -1) {
--                break;
--            }
--            int start = index + classDeclaration.length();
--            int end = s.indexOf(' ', start);
--            if (end == -1) {
--                break;
--            }
--            String typeName = s.substring(start, end);
--            ResourceType type = ResourceType.getEnum(typeName);
--            if (type == null) {
--                break;
--            }
--
--            if (pkg != null) {
--                mResourceClassOwners.put(pkg + "/R$" + type.getName(), type);
--            }
--
--            index = end;
--
--            // Find next declaration
--            for (; index < length - 1; index++) {
--                char c = s.charAt(index);
--                if (Character.isWhitespace(c)) {
--                    //noinspection UnnecessaryContinue
--                    continue;
--                } else if (c == '/') {
--                    char next = s.charAt(index + 1);
--                    if (next == '*') {
--                        // Scan forward to comment end
--                        end = index + 2;
--                        while (end < length -2) {
--                            c = s.charAt(end);
--                            if (c == '*' && s.charAt(end + 1) == '/') {
--                                end++;
--                                break;
--                            } else {
--                                end++;
--                            }
--                        }
--                        index = end;
--                    } else if (next == '/') {
--                        // Scan forward to next newline
--                        assert false : s.substring(index - 1, index + 50); // we don't put line comments in R files
--                    } else {
--                        assert false : s.substring(index - 1, index + 50); // unexpected division
--                    }
--                } else if (c == 'p' && s.startsWith("public ", index)) {
--                    if (type == ResourceType.STYLEABLE) {
--                        start = s.indexOf(" int", index);
--                        if (s.startsWith(" int[] ", start)) {
--                            end = s.indexOf('=', start);
--                            assert end != -1;
--                            String styleable = s.substring(start, end).trim();
--                            addResource(ResourceType.DECLARE_STYLEABLE, styleable, null);
--
--                            // TODO: Read in all the action bar ints!
--                            // For now, we're simply treating all R.attr fields as used
--                        } else if (s.startsWith(" int ")) {
--                            // Read these fields in and correlate with the attr R's. Actually
--                            // we don't need this for anything; the local attributes are
--                            // found by the R attr thing. I just need to record the class
--                            // (style).
--                            // public static final int ActionBar_background = 10;
--                            // ignore - jump to end
--                            index = s.indexOf(';', index);
--                            if (index == -1) {
--                                break;
--                            }
--                            // For now, we're simply treating all R.attr fields as used
--                        }
--                    } else {
--                        start = s.indexOf(" int ", index);
--                        if (start != -1) {
--                            start += " int ".length();
--                            // e.g. abc_fade_in=0x7f040000;
--                            end = s.indexOf('=', start);
--                            assert end != -1;
--                            String name = s.substring(start, end).trim();
--                            start = end + 1;
--                            end = s.indexOf(';', start);
--                            assert end != -1;
--                            String value = s.substring(start, end).trim();
--                            addResource(type, name, value);
--                        }
--                    }
--                } else if (c == '}') {
--                    // Done with resource class
--                    break;
--                }
--            }
--        }
--    }
--
--    private void addResource(@NonNull ResourceType type, @NonNull String name,
--            @Nullable String value) {
--        int realValue = value != null ? Integer.decode(value) : -1;
--        Resource resource = getResource(type, name);
--        if (resource != null) {
--            //noinspection VariableNotUsedInsideIf
--            if (value != null) {
--                if (resource.value == -1) {
--                    resource.value = realValue;
--                } else {
--                    assert realValue == resource.value;
--                }
--            }
--            return;
--        }
--
--        resource = new Resource(type, name, realValue);
--        mResources.add(resource);
--        if (realValue != -1) {
--            mValueToResource.put(realValue, resource);
--        }
--        Map<String, Resource> nameMap = mTypeToName.get(type);
--        if (nameMap == null) {
--            nameMap = Maps.newHashMapWithExpectedSize(30);
--            mTypeToName.put(type, nameMap);
--        }
--        nameMap.put(name, resource);
--
--        // TODO: Assert that we don't set the same resource multiple times to different values.
--        // Could happen if you pass in stale data!
--    }
--
--    public int getUnusedResourceCount() {
--        return mUnused.size();
--    }
--
--    @VisibleForTesting
--    List<Resource> getAllResources() {
--        return mResources;
--    }
--
--    public static class Resource {
--        /** Type of resource */
--        public ResourceType type;
--        /** Name of resource */
--        public String name;
--        /** Integer id location */
--        public int value;
--        /** Whether this resource can be reached from one of the roots (manifest, code) */
--        public boolean reachable;
--        /** Whether this resource has a default definition (e.g. present in a resource folder
--         * with no qualifiers). For id references, an inline definition (@+id) does not count as
--         * a default definition.*/
--        public boolean hasDefault;
--        /** Resources this resource references. For example, a layout can reference another via
--         * an include; a style reference in a layout references that layout style, and so on. */
--        public List<Resource> references;
--        public final List<File> declarations = Lists.newArrayList();
--
--        private Resource(ResourceType type, String name, int value) {
--            this.type = type;
--            this.name = name;
--            this.value = value;
--        }
--
--        @Override
--        public String toString() {
--            return type + ":" + name + ":" + value;
--        }
--
--        @SuppressWarnings("RedundantIfStatement") // Generated by IDE
--        @Override
--        public boolean equals(Object o) {
--            if (this == o) {
--                return true;
--            }
--            if (o == null || getClass() != o.getClass()) {
--                return false;
--            }
--
--            Resource resource = (Resource) o;
--
--            if (name != null ? !name.equals(resource.name) : resource.name != null) {
--                return false;
--            }
--            if (type != resource.type) {
--                return false;
--            }
--
--            return true;
--        }
--
--        @Override
--        public int hashCode() {
--            int result = type != null ? type.hashCode() : 0;
--            result = 31 * result + (name != null ? name.hashCode() : 0);
--            return result;
--        }
--
--        public void addLocation(@NonNull File file) {
--            declarations.add(file);
--        }
--
--        public void addReference(@Nullable Resource resource) {
--            if (resource != null) {
--                if (references == null) {
--                    references = Lists.newArrayList();
--                } else if (references.contains(resource)) {
--                    return;
--                }
--                references.add(resource);
--            }
--        }
--
--        public String getUrl() {
--            return '@' + type.getName() + '/' + name;
--        }
--
--        public boolean isRelevantType() {
--            return type != ResourceType.ID; // && getFolderType() != ResourceFolderType.VALUES;
--        }
--    }
--
--    /**
--     * Class visitor responsible for looking for resource references in code.
--     * It looks for R.type.name references (as well as inlined constants for these,
--     * in the case of non-library code), as well as looking both for Resources#getIdentifier
--     * calls and recording string literals, used to handle dynamic lookup of resources.
--     */
--    private class UsageVisitor extends ClassVisitor {
--        private final File mJarFile;
--        private final String mCurrentClass;
--
--        public UsageVisitor(File jarFile, String name) {
--            super(Opcodes.ASM5);
--            mJarFile = jarFile;
--            mCurrentClass = name;
--        }
--
--        @Override
--        public MethodVisitor visitMethod(int access, final String name,
--                String desc, String signature, String[] exceptions) {
--            return new MethodVisitor(Opcodes.ASM5) {
--                @Override
--                public void visitLdcInsn(Object cst) {
--                    handleCodeConstant(cst, "ldc");
--                }
--
--                @Override
--                public void visitFieldInsn(int opcode, String owner, String name, String desc) {
--                    if (opcode == Opcodes.GETSTATIC) {
--                        ResourceType type = mResourceClassOwners.get(owner);
--                        if (type != null) {
--                            Resource resource = getResource(type, name);
--                            if (resource != null) {
--                                markReachable(resource);
--                            }
--                        }
--                    }
--                }
--
--                @Override
--                public void visitMethodInsn(int opcode, String owner, String name,
--                        String desc, boolean itf) {
--                    super.visitMethodInsn(opcode, owner, name, desc, itf);
--                    if (owner.equals("android/content/res/Resources")
--                            && name.equals("getIdentifier")
--                            && desc.equals(
--                            "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I")) {
--                        mFoundGetIdentifier = true;
--                        // TODO: Check previous instruction and see if we can find a literal
--                        // String; if so, we can more accurately dispatch the resource here
--                        // rather than having to check the whole string pool!
--                    }
--                    if (owner.equals("android/webkit/WebView") && name.startsWith("load")) {
--                        mFoundWebContent = true;
--                    }
--                }
--
--                @Override
--                public AnnotationVisitor visitAnnotationDefault() {
--                    return new AnnotationUsageVisitor();
--                }
--
--                @Override
--                public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
--                    return new AnnotationUsageVisitor();
--                }
--
--                @Override
--                public AnnotationVisitor visitParameterAnnotation(int parameter, String desc,
--                        boolean visible) {
--                    return new AnnotationUsageVisitor();
--                }
--            };
--        }
--
--        @Override
--        public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
--            return new AnnotationUsageVisitor();
--        }
--
--        @Override
--        public FieldVisitor visitField(int access, String name, String desc, String signature,
--                Object value) {
--            handleCodeConstant(value, "field");
--            return new FieldVisitor(Opcodes.ASM5) {
--                @Override
--                public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
--                    return new AnnotationUsageVisitor();
--                }
--            };
--        }
--
--        private class AnnotationUsageVisitor extends AnnotationVisitor {
--            public AnnotationUsageVisitor() {
--                super(Opcodes.ASM5);
--            }
--
--            @Override
--            public AnnotationVisitor visitAnnotation(String name, String desc) {
--                return new AnnotationUsageVisitor();
--            }
--
--            @Override
--            public AnnotationVisitor visitArray(String name) {
--                return new AnnotationUsageVisitor();
--            }
--
--            @Override
--            public void visit(String name, Object value) {
--                handleCodeConstant(value, "annotation");
--                super.visit(name, value);
--            }
--        }
--
--        /** Invoked when an ASM visitor encounters a constant: record corresponding reference */
--        private void handleCodeConstant(@Nullable Object cst, @NonNull String context) {
--            if (cst instanceof Integer) {
--                Integer value = (Integer) cst;
--                Resource resource = mValueToResource.get(value);
--                if (markReachable(resource) && mDebug) {
--                    System.out.println("Marking " + resource + " reachable: referenced from " +
--                            context + " in " + mJarFile + ":" + mCurrentClass);
--                }
--            } else if (cst instanceof int[]) {
--                int[] values = (int[]) cst;
--                for (int value : values) {
--                    Resource resource = mValueToResource.get(value);
--                    if (markReachable(resource) && mDebug) {
--                        System.out.println("Marking " + resource + " reachable: referenced from " +
--                                context + " in " + mJarFile + ":" + mCurrentClass);
--                    }
--                }
--            } else if (cst instanceof String) {
--                String string = (String) cst;
--                referencedString(string);
--            }
--        }
--    }
--
--    @VisibleForTesting
--    String dumpResourceModel() {
--        StringBuilder sb = new StringBuilder(1000);
--        Collections.sort(mResources, new Comparator<Resource>() {
--            @Override
--            public int compare(Resource resource1,
--                    Resource resource2) {
--                int delta = resource1.type.compareTo(resource2.type);
--                if (delta != 0) {
--                    return delta;
--                }
--                return resource1.name.compareTo(resource2.name);
--            }
--        });
--
--        for (Resource resource : mResources) {
--            sb.append(resource.getUrl()).append(" : reachable=").append(resource.reachable);
--            sb.append("\n");
--            if (resource.references != null) {
--                for (Resource referenced : resource.references) {
--                    sb.append("    ");
--                    sb.append(referenced.getUrl());
--                    sb.append("\n");
--                }
--            }
--        }
--
--        return sb.toString();
--    }
--}
-diff --git a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ShrinkResources.groovy b/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ShrinkResources.groovy
-deleted file mode 100644
-index 4031e6c..0000000
---- a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ShrinkResources.groovy
-+++ /dev/null
-@@ -1,220 +0,0 @@
--/*
-- * Copyright (C) 2014 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.build.gradle.tasks
--
--import com.android.build.gradle.internal.scope.ConventionMappingHelper
--import com.android.build.gradle.internal.scope.TaskConfigAction
--import com.android.build.gradle.internal.scope.VariantOutputScope
--import com.android.build.gradle.internal.tasks.BaseTask
--import com.android.build.gradle.internal.variant.BaseVariantData
--import com.android.build.gradle.internal.variant.BaseVariantOutputData
--import com.android.builder.core.AaptPackageProcessBuilder
--import com.android.ide.common.process.LoggedProcessOutputHandler
--import org.gradle.api.logging.LogLevel
--import org.gradle.api.tasks.InputFile
--import org.gradle.api.tasks.OutputFile
--import org.gradle.api.tasks.ParallelizableTask
--import org.gradle.api.tasks.TaskAction
--
--import java.util.concurrent.Callable
--
--/**
-- * Task which strips out unused resources
-- * <p>
-- * The process works as follows:
-- * <ul>
-- * <li> Collect R id <b>values</b> from the final merged R class, which incorporates
-- *      the final id's of all the libraries (if ProGuard hasn't inlined these,
-- *      we don't need to do this; we can look for actual R.id's instead!)
-- * <li> Collect <b>used<b> R values from all the .class files, and R.x.y references too!
-- * <li> Compute the set of remaining/used id’s
-- * <li> Add in any found in the manifest
-- * <li> Look through all resources and produce a graph of reachable resources
-- * <li> Compute unused resources by visiting all resources and ignoring those that
-- *      were reachable
-- * <li> In addition, if we find a call to Resources#getIdentifier(), we collect all
-- *      strings in the class files, and also mark as used any resources that match
-- *      potential string lookups
-- * </ul>
-- */
-- at ParallelizableTask
--public class ShrinkResources extends BaseTask {
--    /**
--     * Associated variant data that the strip task will be run against. Used to locate
--     * not only locations the task needs (e.g. for resources and generated R classes)
--     * but also to obtain the resource merging task, since we will run it a second time
--     * here to generate a new .ap_ file with fewer resources
--     */
--    public BaseVariantOutputData variantOutputData
--
--    @InputFile
--    File uncompressedResources
--
--    @OutputFile
--    File compressedResources
--
--    /** Whether we've already warned about how to turn off shrinking. Used to avoid
--     * repeating the same multi-line message for every repeated abi split. */
--    private static ourWarned;
--
--    @SuppressWarnings("GroovyUnusedDeclaration")
--    @TaskAction
--    void shrink() {
--        def variantData = variantOutputData.variantData
--        try {
--            def processResourcesTask = variantData.generateRClassTask
--            File sourceDir = processResourcesTask.sourceOutputDir
--            File resourceDir = variantData.getScope().getFinalResourcesDir()
--            File mergedManifest = variantOutputData.manifestProcessorTask.manifestOutputFile
--
--            // Analyze resources and usages and strip out unused
--            def analyzer = new ResourceUsageAnalyzer(
--                    sourceDir,
--                    variantData.getScope().getProguardOutputFile(),
--                    mergedManifest,
--                    variantData.getMappingFile(),
--                    resourceDir)
--            analyzer.verbose = project.logger.isEnabled(LogLevel.INFO)
--            analyzer.debug = project.logger.isEnabled(LogLevel.DEBUG)
--            analyzer.analyze();
--
--            //noinspection GroovyConstantIfStatement
--            if (ResourceUsageAnalyzer.TWO_PASS_AAPT) {
--                // This is currently not working; we need support from aapt to be able
--                // to assign a stable set of resources that it should use.
--                def destination = new File(resourceDir.parentFile, resourceDir.name + "-stripped")
--                analyzer.removeUnused(destination)
--
--                File sourceOutputs = processResourcesTask.getSourceOutputDir();
--                sourceOutputs = new File(sourceOutputs.getParentFile(),
--                        sourceOutputs.getName() + "-stripped")
--                sourceOutputs.mkdirs()
--
--                // We don't need to emit R files again, but we can do this here such that
--                // we can *verify* that the R classes generated in the second aapt pass
--                // matches those we saw the first time around.
--                //String sourceOutputPath = sourceOutputs?.getAbsolutePath();
--                String sourceOutputPath = null
--
--                // Repackage the resources:
--                AaptPackageProcessBuilder aaptPackageCommandBuilder =
--                        new AaptPackageProcessBuilder(processResourcesTask.getManifestFile(),
--                                processResourcesTask.getAaptOptions())
--                                .setAssetsFolder(processResourcesTask.getAssetsDir())
--                                .setResFolder(destination)
--                                .setLibraries(processResourcesTask.getLibraries())
--                                .setPackageForR(processResourcesTask.getPackageForR())
--                                .setSourceOutputDir(sourceOutputPath)
--                                .setResPackageOutput(getCompressedResources().absolutePath)
--                                .setType(processResourcesTask.getType())
--                                .setDebuggable(processResourcesTask.getDebuggable())
--                                .setResourceConfigs(processResourcesTask.getResourceConfigs())
--                                .setSplits(processResourcesTask.getSplits())
--
--                getBuilder().processResources(
--                        aaptPackageCommandBuilder,
--                        processResourcesTask.getEnforceUniquePackageName(),
--                        new LoggedProcessOutputHandler(getBuilder().getLogger())
--                )
--            } else {
--                // Just rewrite the .ap_ file to strip out the res/ files for unused resources
--                analyzer.rewriteResourceZip(getUncompressedResources(), getCompressedResources())
--            }
--
--            // Dump some stats
--            int unused = analyzer.getUnusedResourceCount()
--            if (unused > 0) {
--                StringBuilder sb = new StringBuilder(200);
--                sb.append("Removed unused resources")
--
--                // This is a bit misleading until we can strip out all resource types:
--                //int total = analyzer.getTotalResourceCount()
--                //sb.append("(" + unused + "/" + total + ")")
--
--                int before = getUncompressedResources().length()
--                int after = getCompressedResources().length()
--                int percent = (before - after) * 100 / before
--                sb.append(": Binary resource data reduced from ").
--                        append(toKbString(before)).
--                        append("KB to ").
--                        append(toKbString(after)).
--                        append("KB: Removed " + percent + "%");
--                if (!ourWarned) {
--                    ourWarned = true;
--                    sb.append(
--                        "\nNote: If necessary, you can disable resource shrinking by adding\n" +
--                        "android {\n" +
--                        "    buildTypes {\n" +
--                        "        " + variantData.variantConfiguration.buildType.name + " {\n" +
--                        "            shrinkResources false\n" +
--                        "        }\n" +
--                        "    }\n" +
--                        "}")
--                }
--
--                println sb.toString();
--            }
--
--        } catch (Exception e) {
--            println 'Failed to shrink resources: ' + e.toString() + '; ignoring'
--            logger.quiet("Failed to shrink resources: ignoring", e)
--        }
--    }
--
--    private static String toKbString(long size) {
--        return Integer.toString((int)size/1024);
--    }
--
--    public static class ConfigAction implements TaskConfigAction<ShrinkResources> {
--
--        private VariantOutputScope scope;
--
--        public ConfigAction(VariantOutputScope scope) {
--            this.scope = scope;
--        }
--
--        @Override
--        String getName() {
--            return scope.getTaskName("shrink", "Resources");
--        }
--
--        @Override
--        Class<ShrinkResources> getType() {
--            return ShrinkResources.class
--        }
--
--        @Override
--        void execute(ShrinkResources task) {
--            BaseVariantData<? extends BaseVariantOutputData> variantData =
--                    scope.variantScope.variantData
--            task.setAndroidBuilder(scope.getGlobalScope().getAndroidBuilder());
--            task.setVariantName(scope.getVariantScope().getVariantConfiguration().getFullName());
--            task.variantOutputData = scope.variantOutputData;
--
--            final String outputBaseName = scope.variantOutputData.getBaseName();
--            task.setCompressedResources(scope.getCompressedResourceFile());
--
--            ConventionMappingHelper.map(task, "uncompressedResources", new Callable<File>() {
--                @Override
--                public File call() {
--                    return scope.variantOutputData.processResourcesTask.getPackageOutputFile();
--                }
--            });
--
--        }
--    }
--}
-diff --git a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/ApiDatabase.java b/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/ApiDatabase.java
-deleted file mode 100644
-index 67d47e1..0000000
---- a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/ApiDatabase.java
-+++ /dev/null
-@@ -1,356 +0,0 @@
--/*
-- * Copyright (C) 2014 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.build.gradle.tasks.annotations;
--
--import com.android.annotations.NonNull;
--import com.android.annotations.VisibleForTesting;
--import com.google.common.base.Charsets;
--import com.google.common.base.Splitter;
--import com.google.common.collect.Lists;
--import com.google.common.collect.Maps;
--import com.google.common.collect.Sets;
--import com.google.common.io.Files;
--
--import java.io.File;
--import java.io.IOException;
--import java.util.List;
--import java.util.Map;
--import java.util.Set;
--import java.util.regex.Matcher;
--import java.util.regex.Pattern;
--
--/** Reads a signature file in the format of the new API files in frameworks/base/api */
--public class ApiDatabase {
--    @NonNull
--    private final List<String> lines;
--    /** Map from class name to set of field names */
--    @NonNull private final  Map<String,Set<String>> fieldMap =
--            Maps.newHashMapWithExpectedSize(1000);
--    /** Map from class name to map of method names whose values are overloaded signatures */
--    @NonNull private final  Map<String,Map<String,List<String>>> methodMap =
--            Maps.newHashMapWithExpectedSize(1000);
--    @NonNull private final Map<String, List<String>> inheritsFrom =
--            Maps.newHashMapWithExpectedSize(1000);
--    @NonNull private final  Map<String,Set<String>> intFieldMap =
--            Maps.newHashMapWithExpectedSize(1000);
--    @NonNull private final  Set<String> classSet =
--            Sets.newHashSetWithExpectedSize(1000);
--
--    public ApiDatabase(@NonNull List<String> lines) {
--        this.lines = lines;
--        readApi();
--    }
--
--    public ApiDatabase(@NonNull File api) throws IOException {
--        this(Files.readLines(api, Charsets.UTF_8));
--    }
--
--    public boolean hasMethod(String className, String methodName, String arguments) {
--        // Perform raw lookup
--        className = getRawClass(className);
--        methodName = getRawMethod(methodName);
--        arguments = getRawParameterList(arguments);
--
--        Map<String, List<String>> methods = methodMap.get(className);
--        if (methods != null) {
--            List<String> strings = methods.get(methodName);
--            if (strings != null && strings.contains(arguments)) {
--                return true;
--            }
--        }
--
--        List<String> inheritsFrom = this.inheritsFrom.get(className);
--        if (inheritsFrom != null) {
--            for (String clz : inheritsFrom) {
--                if (hasMethod(clz, methodName, arguments)) {
--                    return true;
--                }
--            }
--        }
--
--        return false;
--    }
--
--    public boolean hasField(String className, String fieldName) {
--        Set<String> fields = fieldMap.get(className);
--        if (fields != null && fields.contains(fieldName)) {
--            return true;
--        }
--
--        List<String> inheritsFrom = this.inheritsFrom.get(className);
--        if (inheritsFrom != null) {
--            for (String clz : inheritsFrom) {
--                if (hasField(clz, fieldName)) {
--                    return true;
--                }
--            }
--        }
--
--        return false;
--    }
--
--    public boolean hasClass(String className) {
--        return classSet.contains(className);
--    }
--
--    public Set<String> getDeclaredIntFields(String className) {
--        return intFieldMap.get(className);
--    }
--
--    private void readApi() {
--        String MODIFIERS =
--                "((deprecated|public|static|private|protected|final|abstract|\\s*)\\s+)*";
--        Pattern PACKAGE = Pattern.compile("package (\\S+) \\{");
--        Pattern CLASS =
--                Pattern.compile(MODIFIERS
--                        + "(class|interface|enum)\\s+(\\S+)\\s+(extends (.+))?(implements (.+))?(.*)\\{");
--        Pattern METHOD = Pattern.compile("(method|ctor)\\s+" +
--                MODIFIERS + "(.+)??\\s+(\\S+)\\s*\\((.*)\\)(.*);");
--        Pattern CTOR = Pattern.compile("(method|ctor)\\s+.*\\((.*)\\)(.*);");
--        Pattern FIELD = Pattern.compile("(enum_constant|field)\\s+" +
--                MODIFIERS + "(.+)\\s+(\\S+)\\s*;");
--
--        String currentPackage = null;
--        String currentClass = null;
--
--        for (String line : lines) {
--            line = line.trim();
--            if (line.isEmpty() || line.equals("}")) {
--                continue;
--            }
--            if (line.startsWith("method ")) {
--                Matcher matcher = METHOD.matcher(line);
--                if (!matcher.matches()) {
--                    Extractor.warning("Warning: Did not match as a member: " + line);
--                } else {
--                    assert currentClass != null;
--                    Map<String,List<String>> memberMap = methodMap.get(currentClass);
--                    if (memberMap == null) {
--                        memberMap = Maps.newHashMap();
--                        methodMap.put(currentClass, memberMap);
--                        methodMap.put(getRawClass(currentClass), memberMap);
--                    }
--                    String methodName = matcher.group(5);
--                    List<String> signatures = memberMap.get(methodName);
--                    if (signatures == null) {
--                        signatures = Lists.newArrayList();
--                        memberMap.put(methodName, signatures);
--                        memberMap.put(getRawMethod(methodName), signatures);
--                    }
--                    String signature = matcher.group(6);
--                    signature = signature.trim().replace(" ", "").replace(" ", "");
--                    // normalize varargs: allow lookup with both formats
--                    signatures.add(signature);
--                    if (signature.endsWith("...")) {
--                        signatures.add(signature.substring(0, signature.length() - 3) + "[]");
--                    } else if (signature.endsWith("[]") && !signature.endsWith("[][]")) {
--                        signatures.add(signature.substring(0, signature.length() - 2) + "...");
--                    }
--                    String raw = getRawParameterList(signature);
--                    if (!signatures.contains(raw)) {
--                        signatures.add(raw);
--                    }
--                }
--            } else if (line.startsWith("ctor ")) {
--                Matcher matcher = CTOR.matcher(line);
--                if (!matcher.matches()) {
--                    Extractor.warning("Warning: Did not match as a member: " + line);
--                } else {
--                    assert currentClass != null;
--                    Map<String,List<String>> memberMap = methodMap.get(currentClass);
--                    if (memberMap == null) {
--                        memberMap = Maps.newHashMap();
--                        methodMap.put(currentClass, memberMap);
--                        methodMap.put(getRawClass(currentClass), memberMap);
--                    }
--                    @SuppressWarnings("UnnecessaryLocalVariable")
--                    String methodName = currentClass;
--                    List<String> signatures = memberMap.get(methodName);
--                    if (signatures == null) {
--                        signatures = Lists.newArrayList();
--                        memberMap.put(methodName, signatures);
--                        String constructor = methodName.substring(methodName.lastIndexOf('.') + 1);
--                        memberMap.put(constructor, signatures);
--                        memberMap.put(getRawMethod(methodName), signatures);
--                        memberMap.put(getRawMethod(constructor), signatures);
--                    }
--                    String signature = matcher.group(2);
--                    signature = signature.trim().replace(" ", "").replace(" ", "");
--                    if (signature.endsWith("...")) {
--                        signatures.add(signature.substring(0, signature.length() - 3) + "[]");
--                    } else if (signature.endsWith("[]") && !signature.endsWith("[][]")) {
--                        signatures.add(signature.substring(0, signature.length() - 2) + "...");
--                    }
--                    signatures.add(signature);
--                    String raw = getRawMethod(signature);
--                    if (!signatures.contains(raw)) {
--                        signatures.add(raw);
--                    }
--                }
--            } else if (line.startsWith("enum_constant ") || line.startsWith("field ")) {
--                int equals = line.indexOf('=');
--                if (equals != -1) {
--                    line = line.substring(0, equals).trim();
--                    int semi = line.indexOf(';');
--                    if (semi == -1) {
--                        line = line + ';';
--                    }
--                } else if (!line.endsWith(";")) {
--                    int semi = line.indexOf(';');
--                    if (semi != -1) {
--                        line = line.substring(0, semi + 1);
--                    }
--                }
--                Matcher matcher = FIELD.matcher(line);
--                if (!matcher.matches()) {
--                    Extractor.warning("Warning: Did not match as a member: " + line);
--                } else {
--                    assert currentClass != null;
--                    String fieldName = matcher.group(5);
--                    Set<String> fieldSet = fieldMap.get(currentClass);
--                    if (fieldSet == null) {
--                        fieldSet = Sets.newHashSet();
--                        fieldMap.put(currentClass, fieldSet);
--                    }
--                    fieldSet.add(fieldName);
--                    String type = matcher.group(4);
--                    if (type.equals("int")) {
--                        fieldSet = intFieldMap.get(currentClass);
--                        if (fieldSet == null) {
--                            fieldSet = Sets.newHashSet();
--                            intFieldMap.put(currentClass, fieldSet);
--                        }
--                        fieldSet.add(fieldName);
--                    }
--                }
--            } else if (line.startsWith("package ")) {
--                Matcher matcher = PACKAGE.matcher(line);
--                if (!matcher.matches()) {
--                    Extractor.warning("Warning: Did not match as a package: " + line);
--                } else {
--                    currentPackage = matcher.group(1);
--                }
--            } else {
--                Matcher matcher = CLASS.matcher(line);
--                if (!matcher.matches()) {
--                    Extractor.warning("Warning: Did not match as a class/interface: " + line);
--                } else {
--                    currentClass = currentPackage + '.' + matcher.group(4);
--                    classSet.add(currentClass);
--
--                    String superClass = matcher.group(6);
--                    if (superClass != null) {
--                        Splitter splitter = Splitter.on(' ').trimResults().omitEmptyStrings();
--                        for (String from : splitter.split(superClass)) {
--                            if (from.equals("implements")) {  // workaround for broken regexp
--                                continue;
--                            }
--                            addInheritsFrom(currentClass, from);
--                        }
--                        addInheritsFrom(currentClass, superClass.trim());
--                    }
--                    String implementsList = matcher.group(8);
--                    if (implementsList != null) {
--                        Splitter splitter = Splitter.on(' ').trimResults().omitEmptyStrings();
--                        for (String from : splitter.split(implementsList)) {
--                            addInheritsFrom(currentClass, from);
--                        }
--                    }
--                }
--            }
--        }
--    }
--
--    private void addInheritsFrom(String cls, String inheritsFrom) {
--        List<String> list = this.inheritsFrom.get(cls);
--        if (list == null) {
--            list = Lists.newArrayList();
--            this.inheritsFrom.put(cls, list);
--        }
--        list.add(inheritsFrom);
--    }
--
--    /** Drop generic type variables from a class name */
--    @VisibleForTesting
--    static String getRawClass(@NonNull String name) {
--        int index = name.indexOf('<');
--        if (index != -1) {
--            int end = name.indexOf('>', index + 1);
--            if (end == -1 || end == name.length() - 1) {
--                return name.substring(0, index);
--            } else {
--                // e.g. test.pkg.ArrayAdapter<T>.Inner
--                return name.substring(0, index) + name.substring(end + 1);
--            }
--        }
--        return name;
--    }
--
--    /** Drop generic type variables from a method or constructor name */
--    @VisibleForTesting
--    static String getRawMethod(@NonNull String name) {
--        int index = name.indexOf('<');
--        if (index != -1) {
--            return name.substring(0, index);
--        }
--        return name;
--    }
--
--    /** Drop generic type variables and varargs to produce a raw signature */
--    @VisibleForTesting
--    static String getRawParameterList(String signature) {
--        if (signature.indexOf('<') == -1 && !signature.endsWith("...")) {
--            return signature;
--        }
--
--        int n = signature.length();
--        StringBuilder sb = new StringBuilder(n);
--        int start = 0;
--        while (true) {
--            int index = signature.indexOf('<', start);
--            if (index == -1) {
--                sb.append(signature.substring(start));
--                break;
--            }
--            sb.append(signature.substring(start, index));
--            int balance = 1;
--            for (int i = index + 1; i < n; i++) {
--                char c = signature.charAt(i);
--                if (c == '<') {
--                    balance++;
--                } else if (c == '>') {
--                    balance--;
--                    if (balance == 0) {
--                        start = i + 1;
--                        break;
--                    }
--                }
--            }
--        }
--
--        // Normalize varargs... to []
--        if (sb.length() > 3
--                && sb.charAt(sb.length() - 1) == '.'
--                && sb.charAt(sb.length() - 2) == '.'
--                && sb.charAt(sb.length() - 3) == '.') {
--            sb.setLength(sb.length() - 3);
--            sb.append('[').append(']');
--        }
--
--        return sb.toString();
--    }
--}
-diff --git a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/ExtractAnnotationsDriver.java b/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/ExtractAnnotationsDriver.java
-deleted file mode 100644
-index 78ec03d..0000000
---- a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/ExtractAnnotationsDriver.java
-+++ /dev/null
-@@ -1,400 +0,0 @@
--/*
-- * Copyright (C) 2015 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.build.gradle.tasks.annotations;
--
--import static com.android.SdkConstants.DOT_JAVA;
--import static java.io.File.pathSeparator;
--import static java.io.File.pathSeparatorChar;
--
--import com.android.annotations.NonNull;
--import com.android.tools.lint.EcjParser;
--import com.android.utils.Pair;
--import com.google.common.base.Charsets;
--import com.google.common.base.Splitter;
--import com.google.common.collect.Lists;
--import com.google.common.collect.Maps;
--import com.google.common.io.Files;
--
--import org.eclipse.jdt.core.compiler.IProblem;
--import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
--import org.eclipse.jdt.internal.compiler.batch.CompilationUnit;
--import org.eclipse.jdt.internal.compiler.env.ICompilationUnit;
--import org.eclipse.jdt.internal.compiler.env.INameEnvironment;
--import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
--import org.eclipse.jdt.internal.compiler.util.Util;
--
--import java.io.File;
--import java.io.IOException;
--import java.io.PrintStream;
--import java.util.Collection;
--import java.util.List;
--import java.util.Map;
--
--/**
-- * The extract annotations driver is a command line interface to extracting annotations
-- * from a source tree. It's similar to the gradle
-- * {@link com.android.build.gradle.tasks.ExtractAnnotations} task,
-- * but usable from the command line and outside Gradle, for example
-- * to extract annotations from the Android framework itself (which is not built with
-- * Gradle). It also allows other options only interesting for extracting
-- * platform annotations, such as filtering all APIs and constants through an
-- * API white-list (such that we for example can pull annotations from the master
-- * branch which has the latest metadata, but only expose APIs that are actually in
-- * a released platform), as well as translating android.annotation annotations into
-- * android.support.annotations.
-- */
--public class ExtractAnnotationsDriver {
--    public static void main(String[] args) {
--        new ExtractAnnotationsDriver().run(args);
--    }
--
--    private static void usage(PrintStream output) {
--        output.println("Usage: " + ExtractAnnotationsDriver.class.getSimpleName() + " <flags>");
--        output.println(" --sources <paths>       : Source directories to extract annotations from. ");
--        output.println("                           Separate paths with " + pathSeparator + ", and you can use @ ");
--        output.println("                           as a filename prefix to have the filenames fed from a file");
--        output.println("--classpath <paths>      : Directories and .jar files to resolve symbols from");
--        output.println("--output <zip path>      : The .zip file to write the extracted annotations to, if any");
--        output.println("--proguard <path>        : The proguard.cfg file to write the keep rules to, if any");
--        output.println();
--        output.println("Optional flags:");
--        output.println("--merge-zips <paths>     : Existing external annotation files to merge in");
--        output.println("--quiet                  : Don't print summary information");
--        output.println("--rmtypedefs <folder>    : Remove typedef classes found in the given folder");
--        output.println("--allow-missing-types    : Don't fail even if some types can't be resolved");
--        output.println("--allow-errors           : Don't fail even if there are some compiler errors");
--        output.println("--encoding <encoding>    : Encoding (defaults to utf-8)");
--        output.println("--language-level <level> : Java source language level, typically 1.6 (default) or 1.7");
--        output.println("--api-filter <api.txt>   : A framework API definition to restrict included APIs to");
--        output.println("--hide-filtered          : If filtering out non-APIs, supply this flag to hide listing matches");
--        output.println("--skip-class-retention   : Don't extract annotations that have class retention");
--        System.exit(-1);
--    }
--
--    @SuppressWarnings("MethodMayBeStatic")
--    public void run(@NonNull String[] args) {
--        List<String> classpath = Lists.newArrayList();
--        List<File> sources = Lists.newArrayList();
--        List<File> mergePaths = Lists.newArrayList();
--        List<File> apiFilters = null;
--        File rmTypeDefs = null;
--        boolean verbose = true;
--        boolean allowMissingTypes = false;
--        boolean allowErrors = false;
--        boolean listFiltered = true;
--        boolean skipClassRetention = false;
--
--        String encoding = Charsets.UTF_8.name();
--        File output = null;
--        File proguard = null;
--        long languageLevel = EcjParser.getLanguageLevel(1, 7);
--        if (args.length == 1 && "--help".equals(args[0])) {
--            usage(System.out);
--        }
--        if (args.length < 2) {
--            usage(System.err);
--        }
--        for (int i = 0, n = args.length; i < n; i++) {
--            String flag = args[i];
--
--            if (flag.equals("--quiet")) {
--                verbose = false;
--                continue;
--            } else if (flag.equals("--allow-missing-types")) {
--                allowMissingTypes = true;
--                continue;
--            } else if (flag.equals("--allow-errors")) {
--                allowErrors = true;
--                continue;
--            } else if (flag.equals("--hide-filtered")) {
--                listFiltered = false;
--                continue;
--            } else if (flag.equals("--skip-class-retention")) {
--                skipClassRetention = true;
--                continue;
--            }
--            if (i == n - 1) {
--                usage(System.err);
--            }
--            String value = args[i + 1];
--            i++;
--
--            if (flag.equals("--sources")) {
--                sources = getFiles(value);
--            } else if (flag.equals("--classpath")) {
--                classpath = getPaths(value);
--            } else if (flag.equals("--merge-zips")) {
--                mergePaths = getFiles(value);
--            } else if (flag.equals("--output")) {
--                output = new File(value);
--                if (output.exists()) {
--                    if (output.isDirectory()) {
--                        abort(output + " is a directory");
--                    }
--                    boolean deleted = output.delete();
--                    if (!deleted) {
--                        abort("Could not delete previous version of " + output);
--                    }
--                } else if (output.getParentFile() != null && !output.getParentFile().exists()) {
--                    abort(output.getParentFile() + " does not exist");
--                }
--            } else if (flag.equals("--proguard")) {
--                proguard = new File(value);
--                if (proguard.exists()) {
--                    if (proguard.isDirectory()) {
--                        abort(proguard + " is a directory");
--                    }
--                    boolean deleted = proguard.delete();
--                    if (!deleted) {
--                        abort("Could not delete previous version of " + proguard);
--                    }
--                } else if (proguard.getParentFile() != null && !proguard.getParentFile().exists()) {
--                    abort(proguard.getParentFile() + " does not exist");
--                }
--            } else if (flag.equals("--encoding")) {
--                encoding = value;
--            } else if (flag.equals("--api-filter")) {
--                if (apiFilters == null) {
--                    apiFilters = Lists.newArrayList();
--                }
--                for (String path : Splitter.on(",").omitEmptyStrings().split(value)) {
--                    File apiFilter = new File(path);
--                    if (!apiFilter.isFile()) {
--                        String message = apiFilter + " does not exist or is not a file";
--                        abort(message);
--                    }
--                    apiFilters.add(apiFilter);
--                }
--            } else if (flag.equals("--language-level")) {
--                if ("1.6".equals(value)) {
--                    languageLevel = EcjParser.getLanguageLevel(1, 6);
--                } else if ("1.7".equals(value)) {
--                    languageLevel = EcjParser.getLanguageLevel(1, 7);
--                } else {
--                    abort("Unsupported language level " + value);
--                }
--            } else if (flag.equals("--rmtypedefs")) {
--                rmTypeDefs = new File(value);
--                if (!rmTypeDefs.isDirectory()) {
--                    abort(rmTypeDefs + " is not a directory");
--                }
--            } else {
--                System.err.println("Unknown flag " + flag + ": Use --help for usage information");
--            }
--        }
--
--        if (sources.isEmpty()) {
--            abort("Must specify at least one source path");
--        }
--        if (classpath.isEmpty()) {
--            abort("Must specify classpath pointing to at least android.jar or the framework");
--        }
--        if (output == null && proguard == null) {
--            abort("Must specify output path with --output or a proguard path with --proguard");
--        }
--
--        // API definition files
--        ApiDatabase database = null;
--        if (apiFilters != null && !apiFilters.isEmpty()) {
--            try {
--                List<String> lines = Lists.newArrayList();
--                for (File file : apiFilters) {
--                    lines.addAll(Files.readLines(file, Charsets.UTF_8));
--                }
--                database = new ApiDatabase(lines);
--            } catch (IOException e) {
--                abort("Could not open API database " + apiFilters + ": " + e.getLocalizedMessage());
--            }
--        }
--
--        Extractor extractor = new Extractor(database, rmTypeDefs, verbose, !skipClassRetention,
--                true);
--        extractor.setListIgnored(listFiltered);
--
--        try {
--            Pair<Collection<CompilationUnitDeclaration>, INameEnvironment>
--                    pair = parseSources(sources, classpath, encoding, languageLevel);
--            Collection<CompilationUnitDeclaration> units = pair.getFirst();
--
--            boolean abort = false;
--            int errorCount = 0;
--            for (CompilationUnitDeclaration unit : units) {
--                // so maybe I don't need my map!!
--                IProblem[] problems = unit.compilationResult().getAllProblems();
--                if (problems != null) {
--                    for (IProblem problem : problems) {
--                        if (problem.isError()) {
--                            errorCount++;
--                            String message = problem.getMessage();
--                            if (allowMissingTypes) {
--                                if (message.contains("cannot be resolved")) {
--                                    continue;
--                                }
--                            }
--
--                            System.out.println("Error: " +
--                                    new String(problem.getOriginatingFileName()) + ":" +
--                                    problem.getSourceLineNumber() + ": " + message);
--                            abort = !allowErrors;
--                        }
--                    }
--                }
--            }
--            if (errorCount > 0) {
--                System.err.println("Found " + errorCount + " errors");
--            }
--            if (abort) {
--                abort("Not extracting annotations (compilation problems encountered)");
--            }
--
--            INameEnvironment environment = pair.getSecond();
--            extractor.extractFromProjectSource(units);
--
--            if (mergePaths != null) {
--                for (File jar : mergePaths) {
--                    extractor.mergeExisting(jar);
--                }
--            }
--
--            extractor.export(output, proguard);
--
--            // Remove typedefs?
--            //noinspection VariableNotUsedInsideIf
--            if (rmTypeDefs != null) {
--                extractor.removeTypedefClasses();
--            }
--
--            environment.cleanup();
--        } catch (IOException e) {
--            e.printStackTrace();
--        }
--    }
--
--    private static void abort(@NonNull String message) {
--        System.err.println(message);
--        System.exit(-1);
--    }
--
--    private static List<File> getFiles(String value) {
--        List<File> files = Lists.newArrayList();
--        Splitter splitter = Splitter.on(pathSeparatorChar).omitEmptyStrings().trimResults();
--        for (String path : splitter.split(value)) {
--            if (path.startsWith("@")) {
--                // Special syntax for providing files in a list
--                File sourcePath = new File(path.substring(1));
--                if (!sourcePath.exists()) {
--                    abort(sourcePath + " does not exist");
--                }
--                try {
--                    for (String line : Files.readLines(sourcePath, Charsets.UTF_8)) {
--                        line = line.trim();
--                        if (!line.isEmpty()) {
--                            File file = new File(line);
--                            if (!file.exists()) {
--                                System.err.println("Warning: Could not find file " + line +
--                                        " listed in " + sourcePath);
--                            }
--                            files.add(file);
--                        }
--                    }
--                    continue;
--                } catch (IOException e) {
--                    e.printStackTrace();
--                    System.exit(-1);
--                }
--            }
--            File file = new File(path);
--            if (!file.exists()) {
--                abort(file + " does not exist");
--            }
--            files.add(file);
--        }
--
--        return files;
--    }
--
--    private static List<String> getPaths(String value) {
--        List<File> files = getFiles(value);
--        List<String> paths = Lists.newArrayListWithExpectedSize(files.size());
--        for (File file : files) {
--            paths.add(file.getPath());
--        }
--        return paths;
--    }
--
--    private static void addJavaSources(List<File> list, File file) {
--        if (file.isDirectory()) {
--            File[] files = file.listFiles();
--            if (files != null) {
--                for (File child : files) {
--                    addJavaSources(list, child);
--                }
--            }
--        } else {
--            if (file.isFile() && file.getName().endsWith(DOT_JAVA)) {
--                list.add(file);
--            }
--        }
--    }
--
--    private static List<File> gatherJavaSources(List<File> sourcePath) {
--        List<File> sources = Lists.newArrayList();
--        for (File file : sourcePath) {
--            addJavaSources(sources, file);
--        }
--        return sources;
--    }
--
--    @NonNull
--    private static Pair<Collection<CompilationUnitDeclaration>,INameEnvironment> parseSources(
--            @NonNull List<File> sourcePaths,
--            @NonNull List<String> classpath,
--            @NonNull String encoding,
--            long languageLevel)
--            throws IOException {
--        List<ICompilationUnit> sourceUnits = Lists.newArrayListWithExpectedSize(100);
--
--        for (File source : gatherJavaSources(sourcePaths)) {
--            char[] contents = Util.getFileCharContent(source, encoding);
--            ICompilationUnit unit = new CompilationUnit(contents, source.getPath(), encoding);
--            sourceUnits.add(unit);
--        }
--
--        Map<ICompilationUnit, CompilationUnitDeclaration> outputMap = Maps.newHashMapWithExpectedSize(
--                sourceUnits.size());
--
--        CompilerOptions options = EcjParser.createCompilerOptions();
--        options.docCommentSupport = true; // So I can find @hide
--
--        // Note: We can *not* set options.ignoreMethodBodies=true because it disables
--        // type attribution!
--
--        options.sourceLevel = languageLevel;
--        options.complianceLevel = options.sourceLevel;
--        // We don't generate code, but just in case the parser consults this flag
--        // and makes sure that it's not greater than the source level:
--        options.targetJDK = options.sourceLevel;
--        options.originalComplianceLevel = options.sourceLevel;
--        options.originalSourceLevel = options.sourceLevel;
--        options.inlineJsrBytecode = true; // >= 1.5
--
--        INameEnvironment environment = EcjParser.parse(options, sourceUnits, classpath,
--                outputMap, null);
--        Collection<CompilationUnitDeclaration> parsedUnits = outputMap.values();
--        return Pair.of(parsedUnits, environment);
--    }
--}
-\ No newline at end of file
-diff --git a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/Extractor.java b/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/Extractor.java
-deleted file mode 100644
-index 8cc6042..0000000
---- a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/Extractor.java
-+++ /dev/null
-@@ -1,2484 +0,0 @@
--/*
-- * Copyright (C) 2014 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.build.gradle.tasks.annotations;
--
--import static com.android.SdkConstants.AMP_ENTITY;
--import static com.android.SdkConstants.APOS_ENTITY;
--import static com.android.SdkConstants.ATTR_NAME;
--import static com.android.SdkConstants.ATTR_VALUE;
--import static com.android.SdkConstants.DOT_CLASS;
--import static com.android.SdkConstants.DOT_JAR;
--import static com.android.SdkConstants.DOT_XML;
--import static com.android.SdkConstants.GT_ENTITY;
--import static com.android.SdkConstants.INT_DEF_ANNOTATION;
--import static com.android.SdkConstants.LT_ENTITY;
--import static com.android.SdkConstants.QUOT_ENTITY;
--import static com.android.SdkConstants.STRING_DEF_ANNOTATION;
--import static com.android.SdkConstants.SUPPORT_ANNOTATIONS_PREFIX;
--import static com.android.SdkConstants.TYPE_DEF_FLAG_ATTRIBUTE;
--import static com.android.SdkConstants.TYPE_DEF_VALUE_ATTRIBUTE;
--import static com.android.SdkConstants.VALUE_TRUE;
--import static com.android.tools.lint.checks.SupportAnnotationDetector.INT_RANGE_ANNOTATION;
--import static com.android.tools.lint.detector.api.LintUtils.assertionsEnabled;
--
--import com.android.annotations.NonNull;
--import com.android.annotations.Nullable;
--import com.android.utils.XmlUtils;
--import com.google.common.base.Charsets;
--import com.google.common.base.Splitter;
--import com.google.common.collect.Lists;
--import com.google.common.collect.Maps;
--import com.google.common.collect.Sets;
--import com.google.common.io.ByteStreams;
--import com.google.common.io.Closeables;
--import com.google.common.io.Files;
--import com.google.common.xml.XmlEscapers;
--
--import org.eclipse.jdt.internal.compiler.ASTVisitor;
--import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
--import org.eclipse.jdt.internal.compiler.ast.Annotation;
--import org.eclipse.jdt.internal.compiler.ast.Argument;
--import org.eclipse.jdt.internal.compiler.ast.ArrayInitializer;
--import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
--import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration;
--import org.eclipse.jdt.internal.compiler.ast.Expression;
--import org.eclipse.jdt.internal.compiler.ast.FalseLiteral;
--import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
--import org.eclipse.jdt.internal.compiler.ast.MemberValuePair;
--import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
--import org.eclipse.jdt.internal.compiler.ast.NameReference;
--import org.eclipse.jdt.internal.compiler.ast.NumberLiteral;
--import org.eclipse.jdt.internal.compiler.ast.SingleMemberAnnotation;
--import org.eclipse.jdt.internal.compiler.ast.StringLiteral;
--import org.eclipse.jdt.internal.compiler.ast.TrueLiteral;
--import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
--import org.eclipse.jdt.internal.compiler.impl.BooleanConstant;
--import org.eclipse.jdt.internal.compiler.impl.ByteConstant;
--import org.eclipse.jdt.internal.compiler.impl.CharConstant;
--import org.eclipse.jdt.internal.compiler.impl.Constant;
--import org.eclipse.jdt.internal.compiler.impl.DoubleConstant;
--import org.eclipse.jdt.internal.compiler.impl.FloatConstant;
--import org.eclipse.jdt.internal.compiler.impl.IntConstant;
--import org.eclipse.jdt.internal.compiler.impl.LongConstant;
--import org.eclipse.jdt.internal.compiler.impl.ReferenceContext;
--import org.eclipse.jdt.internal.compiler.impl.ShortConstant;
--import org.eclipse.jdt.internal.compiler.impl.StringConstant;
--import org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding;
--import org.eclipse.jdt.internal.compiler.lookup.Binding;
--import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
--import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
--import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope;
--import org.eclipse.jdt.internal.compiler.lookup.ElementValuePair;
--import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
--import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
--import org.eclipse.jdt.internal.compiler.lookup.MemberTypeBinding;
--import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
--import org.eclipse.jdt.internal.compiler.lookup.MethodScope;
--import org.eclipse.jdt.internal.compiler.lookup.Scope;
--import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
--import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
--import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
--import org.w3c.dom.Document;
--import org.w3c.dom.Element;
--import org.w3c.dom.Node;
--import org.w3c.dom.NodeList;
--import org.xml.sax.SAXException;
--import org.xml.sax.SAXParseException;
--
--import java.io.BufferedWriter;
--import java.io.File;
--import java.io.FileInputStream;
--import java.io.FileOutputStream;
--import java.io.FileWriter;
--import java.io.IOException;
--import java.io.PrintWriter;
--import java.io.StringWriter;
--import java.io.Writer;
--import java.lang.reflect.Field;
--import java.util.ArrayList;
--import java.util.Arrays;
--import java.util.Collection;
--import java.util.Collections;
--import java.util.Comparator;
--import java.util.List;
--import java.util.Map;
--import java.util.Set;
--import java.util.jar.JarEntry;
--import java.util.jar.JarInputStream;
--import java.util.jar.JarOutputStream;
--import java.util.regex.Matcher;
--import java.util.regex.Pattern;
--import java.util.zip.ZipEntry;
--
--/**
-- * Annotation extractor which looks for annotations in parsed compilation units and writes
-- * the annotations into a format suitable for use by IntelliJ and Android Studio etc;
-- * it's basically an XML file, organized by package, which lists the signatures for
-- * fields and methods in classes in the given package, and identifiers method parameters
-- * by index, and lists the annotations annotated on that element.
-- * <p>
-- * This is primarily intended for use in Android libraries such as the support library,
-- * where you want to use the resource int ({@code StringRes}, {@code DrawableRes}, and so on)
-- * annotations to indicate what types of id's are expected, or the {@code IntDef} or
-- * {@code StringDef} annotations to record which specific constants are allowed in int and
-- * String parameters.
-- * <p>
-- * However, the code is also used to extract SDK annotations from the platform, where
-- * the package names of the annotations differ slightly (and where the nullness annotations
-- * do not have class retention for example). Therefore, this code contains some extra
-- * support not needed when extracting annotations in an Android library, such as code
-- * to skip annotations for any method/field not mentioned in the API database, and code
-- * to rewrite the android.jar file to insert annotations in the generated bytecode.
-- * <p>
-- * TODO:
-- * - Warn if the {@code @IntDef} annotation is used on a non-int, and similarly if
-- *   {@code @StringDef} is used on a non-string
-- * - Ignore annotations defined on @hide elements
-- */
--public class Extractor {
--    /** Whether we should include type args like <T*gt; in external annotations signatures */
--    private static final boolean INCLUDE_TYPE_ARGS = false;
--
--    /** Whether to sort annotation attributes (otherwise their declaration order is used) */
--    private final boolean sortAnnotations;
--
--    /**
--     * Whether we should include class-retention annotations into the extracted file;
--     * we don't need {@code android.support.annotation.Nullable} to be in the extracted XML
--     * file since it has class retention and will appear in the compiled .jar version of
--     * the library
--     */
--    private final boolean includeClassRetentionAnnotations;
--
--    /**
--     * Whether we should skip nullable annotations in merged in annotations zip files
--     * (these are typically from infer nullity, which sometimes is a bit aggressive
--     * in assuming something should be marked as nullable; see for example issue #66999
--     * or all the manual removals of findViewById @Nullable return value annotations
--     */
--    private static final boolean INCLUDE_INFERRED_NULLABLE = false;
--
--    public static final String ANDROID_ANNOTATIONS_PREFIX = "android.annotation.";
--    public static final String ANDROID_NULLABLE = "android.annotation.Nullable";
--    public static final String SUPPORT_NULLABLE = "android.support.annotation.Nullable";
--    public static final String SUPPORT_KEEP = "android.support.annotation.Keep";
--    public static final String RESOURCE_TYPE_ANNOTATIONS_SUFFIX = "Res";
--    public static final String ANDROID_NOTNULL = "android.annotation.NonNull";
--    public static final String SUPPORT_NOTNULL = "android.support.annotation.NonNull";
--    public static final String ANDROID_INT_DEF = "android.annotation.IntDef";
--    public static final String ANDROID_INT_RANGE = "android.annotation.IntRange";
--    public static final String ANDROID_STRING_DEF = "android.annotation.StringDef";
--    public static final String REQUIRES_PERMISSION = "android.support.annotation.RequiresPermission";
--    public static final String ANDROID_REQUIRES_PERMISSION = "android.annotation.RequiresPermission";
--    public static final String IDEA_NULLABLE = "org.jetbrains.annotations.Nullable";
--    public static final String IDEA_NOTNULL = "org.jetbrains.annotations.NotNull";
--    public static final String IDEA_MAGIC = "org.intellij.lang.annotations.MagicConstant";
--    public static final String IDEA_CONTRACT = "org.jetbrains.annotations.Contract";
--    public static final String IDEA_NON_NLS = "org.jetbrains.annotations.NonNls";
--    public static final String ATTR_VAL = "val";
--
--    @NonNull
--    private final Map<String, List<AnnotationData>> types = Maps.newHashMap();
--
--    @NonNull
--    private final Set<String> irrelevantAnnotations = Sets.newHashSet();
--
--    private final File classDir;
--
--    @NonNull
--    private final Map<String, Map<String, List<Item>>> itemMap = Maps.newHashMap();
--
--    @Nullable
--    private final ApiDatabase apiFilter;
--
--    private final boolean displayInfo;
--
--    private final Map<String,Integer> stats = Maps.newHashMap();
--    private int filteredCount;
--    private int mergedCount;
--    private final Set<CompilationUnitDeclaration> processedFiles = Sets.newHashSetWithExpectedSize(100);
--    private final Set<String> ignoredAnnotations = Sets.newHashSet();
--    private boolean listIgnored;
--    private Map<String,List<Annotation>> typedefs;
--    private List<String> typedefClasses;
--    private Map<String,Boolean> sourceRetention;
--    private final List<Item> keepItems = Lists.newArrayList();
--
--    public Extractor(@Nullable ApiDatabase apiFilter, @Nullable File classDir, boolean displayInfo,
--            boolean includeClassRetentionAnnotations, boolean sortAnnotations) {
--        this.apiFilter = apiFilter;
--        this.listIgnored = apiFilter != null;
--        this.classDir = classDir;
--        this.displayInfo = displayInfo;
--        this.includeClassRetentionAnnotations = includeClassRetentionAnnotations;
--        this.sortAnnotations = sortAnnotations;
--    }
--
--    public void extractFromProjectSource(Collection<CompilationUnitDeclaration> units) {
--        TypedefCollector collector = new TypedefCollector(units, false /*requireHide*/,
--                true /*requireSourceRetention*/);
--        typedefs = collector.getTypedefs();
--        typedefClasses = collector.getNonPublicTypedefClasses();
--
--        for (CompilationUnitDeclaration unit : units) {
--            analyze(unit);
--        }
--    }
--
--    public void removeTypedefClasses() {
--        if (classDir != null && typedefClasses != null && !typedefClasses.isEmpty()) {
--            boolean quiet = false;
--            boolean verbose = false;
--            boolean dryRun = false;
--            //noinspection ConstantConditions
--            TypedefRemover remover = new TypedefRemover(this, quiet, verbose, dryRun);
--            remover.remove(classDir, typedefClasses);
--        }
--    }
--
--    public void export(@Nullable File annotationsZip, @Nullable File proguardCfg) {
--        if (proguardCfg != null) {
--            if (keepItems.isEmpty()) {
--                if (proguardCfg.exists()) {
--                    //noinspection ResultOfMethodCallIgnored
--                    proguardCfg.delete();
--                }
--            } else if (writeKeepRules(proguardCfg)) {
--                info("ProGuard keep rules written to " + proguardCfg);
--            }
--        }
--
--        if (annotationsZip != null) {
--            if (itemMap.isEmpty()) {
--                if (annotationsZip.exists()) {
--                    //noinspection ResultOfMethodCallIgnored
--                    annotationsZip.delete();
--                }
--            } else if (writeExternalAnnotations(annotationsZip)) {
--                writeStats();
--                info("Annotations written to " + annotationsZip);
--            }
--        }
--
--    }
--
--    public void writeStats() {
--        if (!displayInfo) {
--            return;
--        }
--
--        if (!stats.isEmpty()) {
--            List<String> annotations = Lists.newArrayList(stats.keySet());
--            Collections.sort(annotations, new Comparator<String>() {
--                @Override
--                public int compare(String s1, String s2) {
--                    int frequency1 = stats.get(s1);
--                    int frequency2 = stats.get(s2);
--                    int delta = frequency2 - frequency1;
--                    if (delta != 0) {
--                        return delta;
--                    }
--                    return s1.compareTo(s2);
--                }
--            });
--            Map<String,String> fqnToName = Maps.newHashMap();
--            int max = 0;
--            int count = 0;
--            for (String fqn : annotations) {
--                String name = fqn.substring(fqn.lastIndexOf('.') + 1);
--                fqnToName.put(fqn, name);
--                max = Math.max(max, name.length());
--                count += stats.get(fqn);
--            }
--
--            StringBuilder sb = new StringBuilder(200);
--            sb.append("Extracted ").append(count).append(" Annotations:");
--            for (String fqn : annotations) {
--                sb.append('\n');
--                String name = fqnToName.get(fqn);
--                for (int i = 0, n = max - name.length() + 1; i < n; i++) {
--                    sb.append(' ');
--                }
--                sb.append('@');
--                sb.append(name);
--                sb.append(':').append(' ');
--                sb.append(Integer.toString(stats.get(fqn)));
--            }
--            if (sb.length() > 0) {
--                info(sb.toString());
--            }
--        }
--
--        if (filteredCount > 0) {
--            info(filteredCount + " of these were filtered out (not in API database file)");
--        }
--        if (mergedCount > 0) {
--            info(mergedCount + " additional annotations were merged in");
--        }
--    }
--
--    @SuppressWarnings("UseOfSystemOutOrSystemErr")
--    void info(final String message) {
--        if (displayInfo) {
--            System.out.println(message);
--        }
--    }
--
--    @SuppressWarnings("UseOfSystemOutOrSystemErr")
--    static void error(String message) {
--        System.err.println("Error: " + message);
--    }
--
--    @SuppressWarnings("UseOfSystemOutOrSystemErr")
--    static void warning(String message) {
--        System.out.println("Warning: " + message);
--    }
--
--    private void analyze(CompilationUnitDeclaration unit) {
--        if (processedFiles.contains(unit)) {
--            // The code to process all roots seems to hit some of the same classes
--            // repeatedly... so filter these out manually
--            return;
--        }
--        processedFiles.add(unit);
--
--        AnnotationVisitor visitor = new AnnotationVisitor();
--        unit.traverse(visitor, unit.scope);
--    }
--
--    @Nullable
--    private static ClassScope findClassScope(Scope scope) {
--        while (scope != null) {
--            if (scope instanceof ClassScope) {
--                return (ClassScope)scope;
--            }
--            scope = scope.parent;
--        }
--
--        return null;
--    }
--
--    @Nullable
--    static String getFqn(@NonNull Annotation annotation) {
--        if (annotation.resolvedType != null) {
--            return new String(annotation.resolvedType.readableName());
--        }
--        return null;
--    }
--
--    @Nullable
--    private static String getFqn(@NonNull ClassScope scope) {
--        TypeDeclaration typeDeclaration = scope.referenceType();
--        if (typeDeclaration != null && typeDeclaration.binding != null) {
--            return new String(typeDeclaration.binding.readableName());
--        }
--        return null;
--    }
--
--    @Nullable
--    private static String getFqn(@NonNull MethodScope scope) {
--        ClassScope classScope = findClassScope(scope);
--        if (classScope != null) {
--            return getFqn(classScope);
--        }
--
--        return null;
--    }
--
--    @Nullable
--    private static String getFqn(@NonNull BlockScope scope) {
--        ClassScope classScope = findClassScope(scope);
--        if (classScope != null) {
--            return getFqn(classScope);
--        }
--
--        return null;
--    }
--
--    boolean hasSourceRetention(@NonNull String fqn, @Nullable Annotation annotation) {
--        if (sourceRetention == null) {
--            sourceRetention = Maps.newHashMapWithExpectedSize(20);
--            // The @IntDef and @String annotations have always had source retention,
--            // and always must (because we can't express fully qualified field references
--            // in a .class file.)
--            sourceRetention.put(INT_DEF_ANNOTATION, true);
--            sourceRetention.put(STRING_DEF_ANNOTATION, true);
--            // The @Nullable and @NonNull annotations have always had class retention
--            sourceRetention.put(SUPPORT_NOTNULL, false);
--            sourceRetention.put(SUPPORT_NULLABLE, false);
--
--            // TODO: Look at support library statistics and put the other most
--            // frequently referenced annotations in here statically
--
--            // The resource annotations vary: up until 22.0.1 they had source
--            // retention but then switched to class retention.
--        }
--
--        Boolean source = sourceRetention.get(fqn);
--
--        if (source != null) {
--            return source;
--        }
--
--        if (annotation == null || annotation.type == null
--                || annotation.type.resolvedType == null) {
--            // Assume it's class retention: that's what nearly all annotations
--            // currently are. (We do dynamic lookup of unknown ones to allow for
--            // this version of the Gradle plugin to be able to work on future
--            // versions of the support library with new annotations, where it's
--            // possible some annotations need to use source retention.
--            sourceRetention.put(fqn, false);
--            return false;
--        } else if (annotation.type.resolvedType.getAnnotations() != null) {
--            for (AnnotationBinding binding : annotation.type.resolvedType.getAnnotations()) {
--                if (hasSourceRetention(binding)) {
--                    sourceRetention.put(fqn, true);
--                    return true;
--                }
--            }
--        }
--
--        sourceRetention.put(fqn, false);
--        return false;
--    }
--
--    @SuppressWarnings("unused")
--    static boolean hasSourceRetention(@NonNull AnnotationBinding a) {
--        if (new String(a.getAnnotationType().readableName()).equals("java.lang.annotation.Retention")) {
--            ElementValuePair[] pairs = a.getElementValuePairs();
--            if (pairs == null || pairs.length != 1) {
--                warning("Expected exactly one parameter passed to @Retention");
--                return false;
--            }
--            ElementValuePair pair = pairs[0];
--            Object value = pair.getValue();
--            if (value instanceof FieldBinding) {
--                FieldBinding field = (FieldBinding) value;
--                if ("SOURCE".equals(new String(field.readableName()))) {
--                    return true;
--                }
--            }
--        }
--
--        return false;
--    }
--
--    @SuppressWarnings("unused")
--    static boolean hasSourceRetention(@NonNull Annotation[] annotations) {
--        for (Annotation annotation : annotations) {
--            String typeName = Extractor.getFqn(annotation);
--            if ("java.lang.annotation.Retention".equals(typeName)) {
--                MemberValuePair[] pairs = annotation.memberValuePairs();
--                if (pairs == null || pairs.length != 1) {
--                    warning("Expected exactly one parameter passed to @Retention");
--                    return false;
--                }
--                MemberValuePair pair = pairs[0];
--                Expression value = pair.value;
--                if (value instanceof NameReference) {
--                    NameReference reference = (NameReference) value;
--                    Binding binding = reference.binding;
--                    if (binding != null) {
--                        if (binding instanceof FieldBinding) {
--                            FieldBinding fb = (FieldBinding) binding;
--                            if ("SOURCE".equals(new String(fb.name)) &&
--                                    "java.lang.annotation.RetentionPolicy".equals(
--                                            new String(fb.declaringClass.readableName()))) {
--                                return true;
--                            }
--                        }
--                    }
--                }
--            }
--        }
--
--        return false;
--    }
--
--    private void addAnnotations(@Nullable Annotation[] annotations, @NonNull Item item) {
--        if (annotations != null) {
--            for (Annotation annotation : annotations) {
--                if (isRelevantAnnotation(annotation)) {
--                    String fqn = getFqn(annotation);
--                    if (SUPPORT_KEEP.equals(fqn)) {
--                        // Put keep rules in a different place; we don't want to write
--                        // these out into the external annotations database, they go
--                        // into a special proguard file
--                        keepItems.add(item);
--                    } else {
--                        addAnnotation(annotation, fqn, item.annotations);
--                    }
--                }
--            }
--        }
--    }
--
--    private void addAnnotation(@NonNull Annotation annotation, @Nullable String fqn,
--            @NonNull List<AnnotationData> list) {
--        if (fqn == null) {
--            return;
--        }
--
--        if (fqn.equals(ANDROID_NULLABLE) || fqn.equals(SUPPORT_NULLABLE)) {
--            recordStats(fqn);
--            list.add(new AnnotationData(SUPPORT_NULLABLE));
--            return;
--        }
--
--        if (fqn.equals(ANDROID_NOTNULL) || fqn.equals(SUPPORT_NOTNULL)) {
--            recordStats(fqn);
--            list.add(new AnnotationData(SUPPORT_NOTNULL));
--            return;
--        }
--
--        if (fqn.startsWith(SUPPORT_ANNOTATIONS_PREFIX)
--                && fqn.endsWith(RESOURCE_TYPE_ANNOTATIONS_SUFFIX)) {
--            recordStats(fqn);
--            list.add(new AnnotationData(fqn));
--            return;
--        }
--
--        if (fqn.startsWith(ANDROID_ANNOTATIONS_PREFIX)) {
--            // System annotations: translate to support library annotations
--            if (fqn.endsWith(RESOURCE_TYPE_ANNOTATIONS_SUFFIX)) {
--                // Translate e.g. android.annotation.DrawableRes to
--                //    android.support.annotation.DrawableRes
--                String resAnnotation = SUPPORT_ANNOTATIONS_PREFIX +
--                        fqn.substring(ANDROID_ANNOTATIONS_PREFIX.length());
--                if (!includeClassRetentionAnnotations
--                        && !hasSourceRetention(resAnnotation, null)) {
--                    return;
--                }
--                recordStats(resAnnotation);
--                list.add(new AnnotationData(resAnnotation));
--                return;
--            } else if (isRelevantFrameworkAnnotation(fqn)) {
--                // Translate other android.annotation annotations into corresponding
--                // support annotations
--                String supportAnnotation = SUPPORT_ANNOTATIONS_PREFIX +
--                        fqn.substring(ANDROID_ANNOTATIONS_PREFIX.length());
--                if (!includeClassRetentionAnnotations
--                        && !hasSourceRetention(supportAnnotation, null)) {
--                    return;
--                }
--                recordStats(supportAnnotation);
--                list.add(createData(supportAnnotation, annotation));
--            }
--        }
--
--        if (fqn.startsWith(SUPPORT_ANNOTATIONS_PREFIX)) {
--            recordStats(fqn);
--            list.add(createData(fqn, annotation));
--            return;
--        }
--
--        if (isMagicConstant(fqn)) {
--            List<AnnotationData> indirect = types.get(fqn);
--            if (indirect != null) {
--                list.addAll(indirect);
--            }
--        }
--    }
--
--    private void recordStats(String fqn) {
--        Integer count = stats.get(fqn);
--        if (count == null) {
--            count = 0;
--        }
--        stats.put(fqn, count + 1);
--    }
--
--    private boolean hasRelevantAnnotations(@Nullable Annotation[] annotations) {
--        if (annotations == null) {
--            return false;
--        }
--
--        for (Annotation annotation : annotations) {
--            if (isRelevantAnnotation(annotation)) {
--                return true;
--            }
--        }
--
--        return false;
--    }
--
--    private boolean isRelevantAnnotation(@NonNull Annotation annotation) {
--        String fqn = getFqn(annotation);
--        if (fqn == null || fqn.startsWith("java.lang.")) {
--            return false;
--        }
--        if (fqn.startsWith(SUPPORT_ANNOTATIONS_PREFIX)) {
--            if (fqn.equals(SUPPORT_KEEP)) {
--                return true; // even with class file retention we want to process these
--            }
--
--            //noinspection PointlessBooleanExpression,ConstantConditions,RedundantIfStatement
--            if (!includeClassRetentionAnnotations && !hasSourceRetention(fqn, annotation)) {
--                return false;
--            }
--
--            return true;
--        } else if (fqn.startsWith(ANDROID_ANNOTATIONS_PREFIX)) {
--            return isRelevantFrameworkAnnotation(fqn);
--        }
--        if (fqn.equals(ANDROID_NULLABLE) || fqn.equals(ANDROID_NOTNULL)
--                || isMagicConstant(fqn)) {
--            return true;
--        } else if (fqn.equals(IDEA_CONTRACT)) {
--            return true;
--        }
--
--        return false;
--    }
--
--    private static boolean isRelevantFrameworkAnnotation(@NonNull String fqn) {
--        return fqn.startsWith(ANDROID_ANNOTATIONS_PREFIX)
--                && !fqn.endsWith(".Widget")
--                && !fqn.endsWith(".TargetApi")
--                && !fqn.endsWith(".SystemApi")
--                && !fqn.endsWith(".SuppressLint")
--                && !fqn.endsWith(".SdkConstant");
--    }
--
--    boolean isMagicConstant(String typeName) {
--        if (irrelevantAnnotations.contains(typeName)
--                || typeName.startsWith("java.lang.")) { // @Override, @SuppressWarnings, etc.
--            return false;
--        }
--        if (types.containsKey(typeName) ||
--                typeName.equals(INT_DEF_ANNOTATION) ||
--                typeName.equals(STRING_DEF_ANNOTATION) ||
--                typeName.equals(INT_RANGE_ANNOTATION) ||
--                typeName.equals(ANDROID_INT_RANGE) ||
--                typeName.equals(ANDROID_INT_DEF) ||
--                typeName.equals(ANDROID_STRING_DEF)) {
--            return true;
--        }
--
--        List<Annotation> typeDefs = typedefs.get(typeName);
--        // We only support a single level of IntDef type annotations, not arbitrary nesting
--        if (typeDefs != null) {
--            boolean match = false;
--            for (Annotation typeDef : typeDefs) {
--                String fqn = getFqn(typeDef);
--                if (isNestedAnnotation(fqn)) {
--                    List<AnnotationData> list = types.get(typeName);
--                    if (list == null) {
--                        list = new ArrayList<AnnotationData>(2);
--                        types.put(typeName, list);
--                    }
--                    addAnnotation(typeDef, fqn, list);
--                    match = true;
--                }
--            }
--
--            return match;
--        }
--
--        irrelevantAnnotations.add(typeName);
--
--        return false;
--    }
--
--    static boolean isNestedAnnotation(@Nullable String fqn) {
--        return (fqn != null &&
--                (fqn.equals(INT_DEF_ANNOTATION) ||
--                        fqn.equals(STRING_DEF_ANNOTATION) ||
--                        fqn.equals(REQUIRES_PERMISSION) ||
--                        fqn.equals(ANDROID_REQUIRES_PERMISSION) ||
--                        fqn.equals(INT_RANGE_ANNOTATION) ||
--                        fqn.equals(ANDROID_INT_RANGE) ||
--                        fqn.equals(ANDROID_INT_DEF) ||
--                        fqn.equals(ANDROID_STRING_DEF)));
--    }
--
--    private boolean writeKeepRules(@NonNull File proguardCfg) {
--        if (!keepItems.isEmpty()) {
--            try {
--                Writer writer = new BufferedWriter(new FileWriter(proguardCfg));
--                try {
--                    Collections.sort(keepItems);
--                    for (Item item : keepItems) {
--                        writer.write(item.getKeepRule());
--                        writer.write('\n');
--                    }
--                } finally {
--                    writer.close();
--                }
--            } catch (IOException ioe) {
--                error(ioe.toString());
--                return true;
--            }
--
--            // Now that we've handled these items, remove them from the list
--            // such that we don't accidentally also emit them into the annotations.zip
--            // file, where they are not needed
--            for (Item item : keepItems) {
--                removeItem(item.getQualifiedClassName(), item);
--            }
--        } else if (proguardCfg.exists()) {
--            //noinspection ResultOfMethodCallIgnored
--            proguardCfg.delete();
--        }
--        return false;
--    }
--
--    private boolean writeExternalAnnotations(@NonNull File annotationsZip) {
--        try {
--            FileOutputStream fileOutputStream = new FileOutputStream(annotationsZip);
--            JarOutputStream zos = new JarOutputStream(fileOutputStream);
--
--            try {
--                // TODO: Extract to share with keep rules
--                List<String> sortedPackages = new ArrayList<String>(itemMap.keySet());
--                Collections.sort(sortedPackages);
--                for (String pkg : sortedPackages) {
--                    // Note: Using / rather than File.separator: jar lib requires it
--                    String name = pkg.replace('.', '/') + "/annotations.xml";
--
--                    JarEntry outEntry = new JarEntry(name);
--                    zos.putNextEntry(outEntry);
--
--                    StringWriter stringWriter = new StringWriter(1000);
--                    PrintWriter writer = new PrintWriter(stringWriter);
--                    try {
--                        writer.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
--                                "<root>");
--
--                        Map<String, List<Item>> classMap = itemMap.get(pkg);
--                        List<String> classes = new ArrayList<String>(classMap.keySet());
--                        Collections.sort(classes);
--                        for (String cls : classes) {
--                            List<Item> items = classMap.get(cls);
--                            Collections.sort(items);
--                            for (Item item : items) {
--                                item.write(writer);
--                            }
--                        }
--
--                        writer.println("</root>\n");
--                        writer.close();
--                        String xml = stringWriter.toString();
--
--                        // Validate
--                        if (assertionsEnabled()) {
--                            Document document = checkDocument(pkg, xml, false);
--                            if (document == null) {
--                                error("Could not parse XML document back in for entry " + name
--                                        + ": invalid XML?\n\"\"\"\n" + xml + "\n\"\"\"\n");
--                                return false;
--                            }
--                        }
--                        byte[] bytes = xml.getBytes(Charsets.UTF_8);
--                        zos.write(bytes);
--                        zos.closeEntry();
--                    } finally {
--                        writer.close();
--                    }
--                }
--            } finally {
--                zos.flush();
--                zos.close();
--            }
--        } catch (IOException ioe) {
--            error(ioe.toString());
--            return false;
--        }
--
--        return true;
--    }
--
--    private void addItem(@NonNull String fqn, @NonNull Item item) {
--        // Not part of the API?
--        if (apiFilter != null && item.isFiltered(apiFilter)) {
--            if (isListIgnored()) {
--                info("Skipping API because it is not part of the API file: " + item);
--            }
--
--            filteredCount++;
--            return;
--        }
--
--        String pkg = getPackage(fqn);
--        Map<String, List<Item>> classMap = itemMap.get(pkg);
--        if (classMap == null) {
--            classMap = Maps.newHashMapWithExpectedSize(100);
--            itemMap.put(pkg, classMap);
--        }
--        List<Item> items = classMap.get(fqn);
--        if (items == null) {
--            items = Lists.newArrayList();
--            classMap.put(fqn, items);
--        }
--
--        items.add(item);
--    }
--
--    private void removeItem(@NonNull String classFqn, @NonNull Item item) {
--        String pkg = getPackage(classFqn);
--        Map<String, List<Item>> classMap = itemMap.get(pkg);
--        if (classMap != null) {
--            List<Item> items = classMap.get(classFqn);
--            if (items != null) {
--                items.remove(item);
--                if (items.isEmpty()) {
--                    classMap.remove(classFqn);
--                    if (classMap.isEmpty()) {
--                        itemMap.remove(pkg);
--                    }
--                }
--            }
--        }
--    }
--
--    @Nullable
--    private Item findItem(@NonNull String fqn, @NonNull Item item) {
--        String pkg = getPackage(fqn);
--        Map<String, List<Item>> classMap = itemMap.get(pkg);
--        if (classMap == null) {
--            return null;
--        }
--        List<Item> items = classMap.get(fqn);
--        if (items == null) {
--            return null;
--        }
--        for (Item existing : items) {
--            if (existing.equals(item)) {
--                return existing;
--            }
--        }
--
--        return null;
--    }
--
--    @Nullable
--    private static Document checkDocument(@NonNull String pkg, @NonNull String xml,
--            boolean namespaceAware) {
--        try {
--            return XmlUtils.parseDocument(xml, namespaceAware);
--        } catch (SAXException sax) {
--            warning("Failed to parse document for package " + pkg + ": " + sax.toString());
--        } catch (Exception e) {
--            // pass
--            // This method is deliberately silent; will return null
--        }
--
--        return null;
--    }
--
--    public void mergeExisting(@NonNull File file) {
--        if (file.isDirectory()) {
--            File[] files = file.listFiles();
--            if (files != null) {
--                for (File child : files) {
--                    mergeExisting(child);
--                }
--            }
--        } else if (file.isFile()) {
--            if (file.getPath().endsWith(DOT_JAR)) {
--                mergeFromJar(file);
--            } else if (file.getPath().endsWith(DOT_XML)) {
--                try {
--                    String xml = Files.toString(file, Charsets.UTF_8);
--                    mergeAnnotationsXml(file.getPath(), xml);
--                } catch (IOException e) {
--                    error("Aborting: I/O problem during transform: " + e.toString());
--                }
--            }
--        }
--    }
--
--    private void mergeFromJar(@NonNull File jar) {
--        // Reads in an existing annotations jar and merges in entries found there
--        // with the annotations analyzed from source.
--        JarInputStream zis = null;
--        try {
--            @SuppressWarnings("IOResourceOpenedButNotSafelyClosed")
--            FileInputStream fis = new FileInputStream(jar);
--            zis = new JarInputStream(fis);
--            ZipEntry entry = zis.getNextEntry();
--            while (entry != null) {
--                if (entry.getName().endsWith(".xml")) {
--                    byte[] bytes = ByteStreams.toByteArray(zis);
--                    String xml = new String(bytes, Charsets.UTF_8);
--                    mergeAnnotationsXml(jar.getPath() + ": " + entry, xml);
--                }
--                entry = zis.getNextEntry();
--            }
--        } catch (IOException e) {
--            error("Aborting: I/O problem during transform: " + e.toString());
--        } finally {
--            //noinspection deprecation
--            try {
--                Closeables.close(zis, true /* swallowIOException */);
--            } catch (IOException e) {
--                // cannot happen
--            }
--        }
--    }
--
--    private void mergeAnnotationsXml(@NonNull String path, @NonNull String xml) {
--        try {
--            Document document = XmlUtils.parseDocument(xml, false);
--            mergeDocument(document);
--        } catch (Exception e) {
--            String message = "Failed to merge " + path + ": " + e.toString();
--            if (e instanceof SAXParseException) {
--                SAXParseException spe = (SAXParseException)e;
--                message = "Line " + spe.getLineNumber() + ":" + spe.getColumnNumber() + ": " + message;
--            }
--            error(message);
--            if (!(e instanceof IOException)) {
--                e.printStackTrace();
--            }
--        }
--    }
--
--    private void mergeDocument(@NonNull Document document) {
--        @SuppressWarnings("SpellCheckingInspection")
--        final Pattern XML_SIGNATURE = Pattern.compile(
--                // Class (FieldName | Type? Name(ArgList) Argnum?)
--                //"(\\S+) (\\S+|(.*)\\s+(\\S+)\\((.*)\\)( \\d+)?)");
--                "(\\S+) (\\S+|((.*)\\s+)?(\\S+)\\((.*)\\)( \\d+)?)");
--
--        Element root = document.getDocumentElement();
--        String rootTag = root.getTagName();
--        assert rootTag.equals("root") : rootTag;
--
--        for (Element item : getChildren(root)) {
--            String signature = item.getAttribute(ATTR_NAME);
--            if (signature == null || signature.equals("null")) {
--                continue; // malformed item
--            }
--
--            if (!hasRelevantAnnotations(item)) {
--                continue;
--            }
--
--            signature = unescapeXml(signature);
--            if (signature.equals("java.util.Arrays void sort(T[], java.util.Comparator<?) 0")) {
--                // Incorrect metadata (unbalanced <>'s)
--                // See IDEA-137385
--                signature = "java.util.Arrays void sort(T[], java.util.Comparator<?>) 0";
--            }
--
--            Matcher matcher = XML_SIGNATURE.matcher(signature);
--            if (matcher.matches()) {
--                String containingClass = matcher.group(1);
--                if (containingClass == null) {
--                    warning("Could not find class for " + signature);
--                }
--                String methodName = matcher.group(5);
--                if (methodName != null) {
--                    String type = matcher.group(4);
--                    boolean isConstructor = type == null;
--                    String parameters = matcher.group(6);
--                    mergeMethodOrParameter(item, matcher, containingClass, methodName, type,
--                            isConstructor, parameters);
--                } else {
--                    String fieldName = matcher.group(2);
--                    mergeField(item, containingClass, fieldName);
--                }
--            } else {
--                if (signature.indexOf(' ') != -1 || signature.indexOf('.') == -1) {
--                    warning("No merge match for signature " + signature);
--                } // else: probably just a class signature, e.g. for @NonNls
--            }
--        }
--    }
--
--    @NonNull
--    private static String unescapeXml(@NonNull String escaped) {
--        String workingString = escaped.replace(QUOT_ENTITY, "\"");
--        workingString = workingString.replace(LT_ENTITY, "<");
--        workingString = workingString.replace(GT_ENTITY, ">");
--        workingString = workingString.replace(APOS_ENTITY, "'");
--        workingString = workingString.replace(AMP_ENTITY, "&");
--
--        return workingString;
--    }
--
--    @NonNull
--    private static String escapeXml(@NonNull String unescaped) {
--        return XmlEscapers.xmlAttributeEscaper().escape(unescaped);
--    }
--
--    private void mergeField(Element item, String containingClass, String fieldName) {
--        if (apiFilter != null &&
--                !apiFilter.hasField(containingClass, fieldName)) {
--            if (isListIgnored()) {
--                info("Skipping imported element because it is not part of the API file: "
--                        + containingClass + "#" + fieldName);
--            }
--            filteredCount++;
--        } else {
--            FieldItem fieldItem = new FieldItem(containingClass, ClassKind.CLASS, fieldName, null);
--            Item existing = findItem(containingClass, fieldItem);
--            if (existing != null) {
--                mergedCount += mergeAnnotations(item, existing);
--            } else {
--                addItem(containingClass, fieldItem);
--                mergedCount += addAnnotations(item, fieldItem);
--            }
--        }
--    }
--
--    private void mergeMethodOrParameter(Element item, Matcher matcher, String containingClass,
--            String methodName, String type, boolean constructor, String parameters) {
--        parameters = fixParameterString(parameters);
--
--        if (apiFilter != null &&
--                !apiFilter.hasMethod(containingClass, methodName, parameters)) {
--            if (isListIgnored()) {
--                info("Skipping imported element because it is not part of the API file: "
--                        + containingClass + "#" + methodName + "(" + parameters + ")");
--            }
--            filteredCount++;
--            return;
--        }
--
--        String argNum = matcher.group(7);
--        if (argNum != null) {
--            argNum = argNum.trim();
--            ParameterItem parameterItem = new ParameterItem(containingClass, ClassKind.CLASS, type,
--                    methodName, parameters, constructor, argNum);
--            Item existing = findItem(containingClass, parameterItem);
--
--            if ("java.util.Calendar".equals(containingClass) && "set".equals(methodName)
--                    && Integer.parseInt(argNum) > 0) {
--                // Skip the metadata for Calendar.set(int, int, int+); see
--                // https://code.google.com/p/android/issues/detail?id=73982
--                return;
--            }
--
--            if (existing != null) {
--                mergedCount += mergeAnnotations(item, existing);
--            } else {
--                addItem(containingClass, parameterItem);
--                mergedCount += addAnnotations(item, parameterItem);
--            }
--        } else {
--            MethodItem methodItem = new MethodItem(containingClass, ClassKind.CLASS, type,
--                    methodName, parameters, constructor);
--            Item existing = findItem(containingClass, methodItem);
--            if (existing != null) {
--                mergedCount += mergeAnnotations(item, existing);
--            } else {
--                addItem(containingClass, methodItem);
--                mergedCount += addAnnotations(item, methodItem);
--            }
--        }
--    }
--
--    // The parameter declaration used in XML files should not have duplicated spaces,
--    // and there should be no space after commas (we can't however strip out all spaces,
--    // since for example the spaces around the "extends" keyword needs to be there in
--    // types like Map<String,? extends Number>
--    private static String fixParameterString(String parameters) {
--        return parameters.replace("  ", " ").replace(", ", ",");
--    }
--
--    private boolean hasRelevantAnnotations(Element item) {
--        for (Element annotationElement : getChildren(item)) {
--            if (isRelevantAnnotation(annotationElement)) {
--                return true;
--            }
--        }
--
--        return false;
--    }
--
--    private boolean isRelevantAnnotation(Element annotationElement) {
--        AnnotationData annotation = createAnnotation(annotationElement);
--        if (annotation == null) {
--            // Unsupported annotation in import
--            return false;
--        }
--        if (isNullable(annotation.name) || isNonNull(annotation.name)
--                || annotation.name.startsWith(ANDROID_ANNOTATIONS_PREFIX)
--                || annotation.name.startsWith(SUPPORT_ANNOTATIONS_PREFIX)) {
--            return true;
--        } else if (annotation.name.equals(IDEA_CONTRACT)) {
--            return true;
--        } else if (annotation.name.equals(IDEA_NON_NLS)) {
--            return false;
--        } else {
--            if (!ignoredAnnotations.contains(annotation.name)) {
--                ignoredAnnotations.add(annotation.name);
--                if (isListIgnored()) {
--                    info("(Ignoring merge annotation " + annotation.name + ")");
--                }
--            }
--        }
--
--        return false;
--    }
--
--    @NonNull
--    private static List<Element> getChildren(@NonNull Element element) {
--        NodeList itemList = element.getChildNodes();
--        int length = itemList.getLength();
--        List<Element> result = new ArrayList<Element>(Math.max(5, length / 2 + 1));
--        for (int i = 0; i < length; i++) {
--            Node node = itemList.item(i);
--            if (node.getNodeType() != Node.ELEMENT_NODE) {
--                continue;
--            }
--
--            result.add((Element) node);
--        }
--
--        return result;
--    }
--
--    private int addAnnotations(Element itemElement, Item item) {
--        int count = 0;
--        for (Element annotationElement : getChildren(itemElement)) {
--            if (!isRelevantAnnotation(annotationElement)) {
--                continue;
--            }
--            AnnotationData annotation = createAnnotation(annotationElement);
--            item.annotations.add(annotation);
--            count++;
--        }
--        return count;
--    }
--
--    private int mergeAnnotations(Element itemElement, Item item) {
--        int count = 0;
--        loop:
--        for (Element annotationElement : getChildren(itemElement)) {
--            if (!isRelevantAnnotation(annotationElement)) {
--                continue;
--            }
--            AnnotationData annotation = createAnnotation(annotationElement);
--            if (annotation == null) {
--                continue;
--            }
--            boolean haveNullable = false;
--            boolean haveNotNull = false;
--            for (AnnotationData existing : item.annotations) {
--                if (isNonNull(existing.name)) {
--                    haveNotNull = true;
--                }
--                if (isNullable(existing.name)) {
--                    haveNullable = true;
--                }
--                if (existing.equals(annotation)) {
--                    continue loop;
--                }
--            }
--
--            // Make sure we don't have a conflict between nullable and not nullable
--            if (isNonNull(annotation.name) && haveNullable ||
--                    isNullable(annotation.name) && haveNotNull) {
--                warning("Found both @Nullable and @NonNull after import for " + item);
--                continue;
--            }
--
--            item.annotations.add(annotation);
--            count++;
--        }
--
--        return count;
--    }
--
--    private static boolean isNonNull(String name) {
--        return name.equals(IDEA_NOTNULL)
--                || name.equals(ANDROID_NOTNULL)
--                || name.equals(SUPPORT_NOTNULL);
--    }
--
--    private static boolean isNullable(String name) {
--        return name.equals(IDEA_NULLABLE)
--                || name.equals(ANDROID_NULLABLE)
--                || name.equals(SUPPORT_NULLABLE);
--    }
--
--    private AnnotationData createAnnotation(Element annotationElement) {
--        String tagName = annotationElement.getTagName();
--        assert tagName.equals("annotation") : tagName;
--        String name = annotationElement.getAttribute(ATTR_NAME);
--        assert name != null && !name.isEmpty();
--        AnnotationData annotation;
--        if (IDEA_MAGIC.equals(name)) {
--            List<Element> children = getChildren(annotationElement);
--            assert children.size() == 1 : children.size();
--            Element valueElement = children.get(0);
--            String valName = valueElement.getAttribute(ATTR_NAME);
--            String value = valueElement.getAttribute(ATTR_VAL);
--            boolean flagsFromClass = valName.equals("flagsFromClass");
--            boolean flag = valName.equals("flags") || flagsFromClass;
--            if (valName.equals("valuesFromClass") || flagsFromClass) {
--                // Not supported
--                boolean found = false;
--                if (value.endsWith(DOT_CLASS)) {
--                    String clsName = value.substring(0, value.length() - DOT_CLASS.length());
--                    StringBuilder sb = new StringBuilder();
--                    sb.append('{');
--
--
--                    Field[] reflectionFields = null;
--                    try {
--                        Class<?> cls = Class.forName(clsName);
--                        reflectionFields = cls.getDeclaredFields();
--                    } catch (Exception ignore) {
--                        // Class not available: not a problem. We'll rely on API filter.
--                        // It's mainly used for sorting anyway.
--                    }
--                    if (apiFilter != null) {
--                        // Search in API database
--                        Set<String> fields = apiFilter.getDeclaredIntFields(clsName);
--                        if ("java.util.zip.ZipEntry".equals(clsName)) {
--                            // The metadata says valuesFromClass ZipEntry, and unfortunately
--                            // that class implements ZipConstants and therefore imports a large
--                            // number of irrelevant constants that aren't valid here. Instead,
--                            // only allow these two:
--                            fields = Sets.newHashSet("STORED", "DEFLATED");
--                        }
--
--                        if (fields != null) {
--                            List<String> sorted = Lists.newArrayList(fields);
--                            Collections.sort(sorted);
--                            if (reflectionFields != null) {
--                                final Map<String,Integer> rank = Maps.newHashMap();
--                                for (int i = 0, n = sorted.size(); i < n; i++) {
--                                    rank.put(sorted.get(i), reflectionFields.length + i);
--
--                                }
--                                for (int i = 0, n = reflectionFields.length; i < n; i++) {
--                                    rank.put(reflectionFields[i].getName(), i);
--                                }
--                                Collections.sort(sorted, new Comparator<String>() {
--                                    @Override
--                                    public int compare(String o1, String o2) {
--                                        int rank1 = rank.get(o1);
--                                        int rank2 = rank.get(o2);
--                                        int delta = rank1 - rank2;
--                                        if (delta != 0) {
--                                            return delta;
--                                        }
--                                        return o1.compareTo(o2);
--                                    }
--                                });
--                            }
--                            boolean first = true;
--                            for (String field : sorted) {
--                                if (first) {
--                                    first = false;
--                                } else {
--                                    sb.append(',').append(' ');
--                                }
--                                sb.append(clsName).append('.').append(field);
--                            }
--                            found = true;
--                        }
--                    }
--                    // Attempt to sort in reflection order
--                    if (!found && reflectionFields != null && (apiFilter == null || apiFilter.hasClass(clsName))) {
--                        // Attempt with reflection
--                        boolean first = true;
--                        for (Field field : reflectionFields) {
--                            if (field.getType() == Integer.TYPE ||
--                                    field.getType() == int.class) {
--                                if (first) {
--                                    first = false;
--                                } else {
--                                    sb.append(',').append(' ');
--                                }
--                                sb.append(clsName).append('.').append(field.getName());
--                            }
--                        }
--                    }
--                    sb.append('}');
--                    value = sb.toString();
--                    if (sb.length() > 2) { // 2: { }
--                        found = true;
--                    }
--                }
--
--                if (!found) {
--                    return null;
--                }
--            }
--
--            //noinspection VariableNotUsedInsideIf
--            if (apiFilter != null) {
--                value = removeFiltered(value);
--                while (value.contains(", ,")) {
--                    value = value.replace(", ,",",");
--                }
--                if (value.startsWith(", ")) {
--                    value = value.substring(2);
--                }
--            }
--
--            annotation = new AnnotationData(
--                    valName.equals("stringValues") ? STRING_DEF_ANNOTATION : INT_DEF_ANNOTATION,
--                    new String[] {
--                            TYPE_DEF_VALUE_ATTRIBUTE, value,
--                            flag ? TYPE_DEF_FLAG_ATTRIBUTE : null, flag ? VALUE_TRUE : null });
--        } else if (STRING_DEF_ANNOTATION.equals(name) || ANDROID_STRING_DEF.equals(name) ||
--                INT_DEF_ANNOTATION.equals(name) || ANDROID_INT_DEF.equals(name)) {
--            List<Element> children = getChildren(annotationElement);
--            Element valueElement = children.get(0);
--            String valName = valueElement.getAttribute(ATTR_NAME);
--            assert TYPE_DEF_VALUE_ATTRIBUTE.equals(valName);
--            String value = valueElement.getAttribute(ATTR_VAL);
--            boolean flag = false;
--            if (children.size() == 2) {
--                valueElement = children.get(1);
--                assert TYPE_DEF_FLAG_ATTRIBUTE.equals(valueElement.getAttribute(ATTR_NAME));
--                flag = VALUE_TRUE.equals(valueElement.getAttribute(ATTR_VAL));
--            }
--            boolean intDef = INT_DEF_ANNOTATION.equals(name) || ANDROID_INT_DEF.equals(name);
--            annotation = new AnnotationData(
--                    intDef ? INT_DEF_ANNOTATION : STRING_DEF_ANNOTATION,
--                    new String[] { TYPE_DEF_VALUE_ATTRIBUTE, value,
--                    flag ? TYPE_DEF_FLAG_ATTRIBUTE : null, flag ? VALUE_TRUE : null});
--        } else if (IDEA_CONTRACT.equals(name)) {
--            List<Element> children = getChildren(annotationElement);
--            assert children.size() == 1 : children.size();
--            Element valueElement = children.get(0);
--            String value = valueElement.getAttribute(ATTR_VAL);
--            annotation = new AnnotationData(name, new String[] { TYPE_DEF_VALUE_ATTRIBUTE, value });
--        } else if (isNonNull(name)) {
--            annotation = new AnnotationData(SUPPORT_NOTNULL);
--        } else if (isNullable(name)) {
--            //noinspection PointlessBooleanExpression,ConstantConditions
--            if (!INCLUDE_INFERRED_NULLABLE && IDEA_NULLABLE.equals(name)) {
--                return null;
--            }
--            annotation = new AnnotationData(SUPPORT_NULLABLE);
--        } else {
--            List<Element> children = getChildren(annotationElement);
--            if (children.isEmpty()) {
--                return new AnnotationData(name);
--            }
--            List<String> attributeStrings = Lists.newArrayList();
--            for (Element valueElement : children) {
--                attributeStrings.add(valueElement.getAttribute(ATTR_NAME));
--                attributeStrings.add(valueElement.getAttribute(ATTR_VAL));
--            }
--            annotation = new AnnotationData(name, attributeStrings.toArray(
--                    new String[attributeStrings.size()]));
--        }
--        return annotation;
--    }
--
--    private String removeFiltered(String value) {
--        assert apiFilter != null;
--        if (value.startsWith("{")) {
--            value = value.substring(1);
--        }
--        if (value.endsWith("}")) {
--            value = value.substring(0, value.length() - 1);
--        }
--        value = value.trim();
--        StringBuilder sb = new StringBuilder(value.length());
--        sb.append('{');
--        for (String fqn : Splitter.on(',').omitEmptyStrings().trimResults().split(value)) {
--            fqn = unescapeXml(fqn);
--            if (fqn.startsWith("\"")) {
--                continue;
--            }
--            int index = fqn.lastIndexOf('.');
--            String cls = fqn.substring(0, index);
--            String field = fqn.substring(index + 1);
--            if (apiFilter.hasField(cls, field)) {
--                if (sb.length() > 1) { // 0: '{'
--                    sb.append(", ");
--                }
--                sb.append(fqn);
--            } else if (isListIgnored()) {
--                info("Skipping constant from typedef because it is not part of the SDK: " + fqn);
--            }
--        }
--        sb.append('}');
--        return escapeXml(sb.toString());
--    }
--
--
--    private static String getPackage(String fqn) {
--        // Extract package from the given fqn. Attempts to handle inner classes;
--        // e.g.  "foo.bar.Foo.Bar will return "foo.bar".
--        int index = 0;
--        int last = 0;
--        while (true) {
--            index = fqn.indexOf('.', index);
--            if (index == -1) {
--                break;
--            }
--            last = index;
--            if (index < fqn.length() - 1) {
--                char next = fqn.charAt(index + 1);
--                if (Character.isUpperCase(next)) {
--                    break;
--                }
--            }
--            index++;
--        }
--
--        return fqn.substring(0, last);
--    }
--
--    @SuppressWarnings("UnusedDeclaration")
--    public void setListIgnored(boolean listIgnored) {
--        this.listIgnored = listIgnored;
--    }
--
--    public boolean isListIgnored() {
--        return listIgnored;
--    }
--
--    public AnnotationData createData(@NonNull String name, @NonNull Annotation annotation) {
--        MemberValuePair[] pairs = annotation.memberValuePairs();
--        if (pairs == null || pairs.length == 0) {
--            return new AnnotationData(name);
--        }
--        return new AnnotationData(name, pairs);
--    }
--
--    private class AnnotationData {
--        @NonNull
--        public final String name;
--
--        @Nullable
--        public String[] attributeStrings;
--
--        @Nullable
--        public MemberValuePair[] attributes;
--
--        private AnnotationData(@NonNull String name) {
--            this.name = name;
--        }
--
--        private AnnotationData(@NonNull String name, @Nullable MemberValuePair[] pairs) {
--            this(name);
--            attributes = pairs;
--            assert attributes == null || attributes.length > 0;
--        }
--
--        private AnnotationData(@NonNull String name, @Nullable String[] attributeStrings) {
--            this(name);
--            this.attributeStrings = attributeStrings;
--            assert attributeStrings != null && attributeStrings.length > 0;
--        }
--
--        void write(PrintWriter writer) {
--            writer.print("    <annotation name=\"");
--            writer.print(name);
--
--            if (attributes != null) {
--                writer.print("\">");
--                writer.println();
--                //noinspection PointlessBooleanExpression,ConstantConditions
--                if (attributes.length > 1 && sortAnnotations) {
--                    // Ensure that the value attribute is written first
--                    Arrays.sort(attributes, new Comparator<MemberValuePair>() {
--                        private String getName(MemberValuePair pair) {
--                            if (pair.name == null) {
--                                return ATTR_VALUE;
--                            } else {
--                                return new String(pair.name);
--                            }
--                        }
--
--                        private int rank(MemberValuePair pair) {
--                            return ATTR_VALUE.equals(getName(pair)) ? -1 : 0;
--                        }
--
--                        @Override
--                        public int compare(MemberValuePair o1, MemberValuePair o2) {
--                            int r1 = rank(o1);
--                            int r2 = rank(o2);
--                            int delta = r1 - r2;
--                            if (delta != 0) {
--                                return delta;
--                            }
--                            return getName(o1).compareTo(getName(o2));
--                        }
--                    });
--                }
--
--                MemberValuePair[] attributes = this.attributes;
--
--                if (attributes.length == 1
--                        && name.startsWith(REQUIRES_PERMISSION)
--                        && name.length() > REQUIRES_PERMISSION.length()
--                        && attributes[0].value instanceof SingleMemberAnnotation) {
--                    // The external annotations format does not allow for nested/complex annotations.
--                    // However, these special annotations (@RequiresPermission.Read,
--                    // @RequiresPermission.Write, etc) are known to only be simple containers with a
--                    // single permission child, so instead we "inline" the content:
--                    //  @Read(@RequiresPermission(allOf={P1,P2},conditional=true)
--                    //     =>
--                    //      @RequiresPermission.Read(allOf({P1,P2},conditional=true)
--                    // That's setting attributes that don't actually exist on the container permission,
--                    // but we'll counteract that on the read-annotations side.
--                    SingleMemberAnnotation annotation = (SingleMemberAnnotation)attributes[0].value;
--                    attributes = annotation.memberValuePairs();
--                }
--
--                for (MemberValuePair pair : attributes) {
--                    writer.print("      <val name=\"");
--                    if (pair.name != null) {
--                        writer.print(pair.name);
--                    } else {
--                        writer.print(ATTR_VALUE); // default name
--                    }
--                    writer.print("\" val=\"");
--                    writer.print(escapeXml(attributeString(pair.value)));
--                    writer.println("\" />");
--                }
--                writer.println("    </annotation>");
--
--            } else if (attributeStrings != null) {
--                writer.print("\">");
--                writer.println();
--                for (int i = 0; i < attributeStrings.length; i += 2) {
--                    String name = attributeStrings[i];
--                    String value = attributeStrings[i + 1];
--                    if (name == null) {
--                        continue;
--                    }
--                    writer.print("      <val name=\"");
--                    writer.print(name);
--                    writer.print("\" val=\"");
--                    writer.print(escapeXml(value));
--                    writer.println("\" />");
--                }
--                writer.println("    </annotation>");
--            } else {
--                writer.println("\" />");
--            }
--        }
--
--        @Override
--        public boolean equals(Object o) {
--            if (this == o) {
--                return true;
--            }
--            if (o == null || getClass() != o.getClass()) {
--                return false;
--            }
--
--            AnnotationData that = (AnnotationData) o;
--
--            return name.equals(that.name);
--        }
--
--        @Override
--        public int hashCode() {
--            return name.hashCode();
--        }
--
--        private String attributeString(@NonNull Expression value) {
--            StringBuilder sb = new StringBuilder();
--            appendExpression(sb, value);
--            return sb.toString();
--        }
--
--        private boolean appendExpression(@NonNull StringBuilder sb,
--                @NonNull Expression expression) {
--            if (expression instanceof ArrayInitializer) {
--                sb.append('{');
--                ArrayInitializer initializer = (ArrayInitializer) expression;
--                boolean first = true;
--                int initialLength = sb.length();
--                for (Expression e : initializer.expressions) {
--                    int length = sb.length();
--                    if (first) {
--                        first = false;
--                    } else {
--                        sb.append(", ");
--                    }
--                    boolean appended = appendExpression(sb, e);
--                    if (!appended) {
--                        // trunk off comma if it bailed for some reason (e.g. constant
--                        // filtered out by API etc)
--                        sb.setLength(length);
--                        if (length == initialLength) {
--                            first = true;
--                        }
--                    }
--                }
--                sb.append('}');
--                return true;
--            } else if (expression instanceof NameReference) {
--                NameReference reference = (NameReference) expression;
--                if (reference.binding != null) {
--                    if (reference.binding instanceof FieldBinding) {
--                        FieldBinding fb = (FieldBinding)reference.binding;
--                        Constant constant = fb.constant();
--                        if (constant != null && constant != Constant.NotAConstant &&
--                                !(name.equals(INT_DEF_ANNOTATION)) &&
--                                !(name.equals(STRING_DEF_ANNOTATION))) {
--                            if (constant instanceof StringConstant) {
--                                sb.append('"').append(constant.stringValue()).append('"');
--                                return true;
--                            } else if (constant instanceof IntConstant) {
--                                sb.append(Integer.toString(constant.intValue()));
--                                return true;
--                            } else if (constant instanceof BooleanConstant) {
--                                sb.append(Boolean.toString(constant.booleanValue()));
--                                return true;
--                            } else if (constant instanceof LongConstant) {
--                                sb.append(Long.toString(constant.longValue()));
--                                return true;
--                            } else if (constant instanceof DoubleConstant) {
--                                sb.append(Double.toString(constant.doubleValue()));
--                                return true;
--                            } else if (constant instanceof CharConstant) {
--                                sb.append('\'').append(Character.toString(constant.charValue())).append('\'');
--                                return true;
--                            } else if (constant instanceof FloatConstant) {
--                                sb.append(Float.toString(constant.floatValue()));
--                                return true;
--                            } else if (constant instanceof ShortConstant) {
--                                sb.append(Short.toString(constant.shortValue()));
--                                return true;
--                            } else if (constant instanceof ByteConstant) {
--                                sb.append(Byte.toString(constant.byteValue()));
--                                return true;
--                            }
--                        }
--                        if (fb.declaringClass != null) {
--                            if (apiFilter != null &&
--                                    !apiFilter.hasField(
--                                            new String(fb.declaringClass.readableName()),
--                                            new String(fb.name))) {
--                                if (isListIgnored()) {
--                                    info("Filtering out typedef constant "
--                                            + new String(fb.declaringClass.readableName()) + "."
--                                            + new String(fb.name) + "");
--                                }
--                                return false;
--                            }
--                            sb.append(fb.declaringClass.readableName());
--                            sb.append('.');
--                            sb.append(fb.name);
--                        } else {
--                            sb.append(reference.binding.readableName());
--                        }
--                    } else {
--                        sb.append(reference.binding.readableName());
--                    }
--                    return true;
--                } else {
--                    warning("No binding for reference " + reference);
--                }
--                return false;
--            } else if (expression instanceof StringLiteral) {
--                StringLiteral s = (StringLiteral) expression;
--                sb.append('"');
--                sb.append(s.source());
--                sb.append('"');
--                return true;
--            } else if (expression instanceof NumberLiteral) {
--                NumberLiteral number = (NumberLiteral) expression;
--                sb.append(number.source());
--                return true;
--            } else if (expression instanceof TrueLiteral) {
--                sb.append(true);
--                return true;
--            } else if (expression instanceof FalseLiteral) {
--                sb.append(false);
--                return true;
--            } else if (expression instanceof org.eclipse.jdt.internal.compiler.ast.NullLiteral) {
--                sb.append("null");
--                return true;
--            } else {
--                // BinaryExpression etc can happen if you put "3 + 4" in as an integer!
--                if (expression.constant != null) {
--                    if (expression.constant.typeID() == TypeIds.T_int) {
--                        sb.append(expression.constant.intValue());
--                        return true;
--                    } else if (expression.constant.typeID() == TypeIds.T_JavaLangString) {
--                        sb.append('"');
--                        sb.append(expression.constant.stringValue());
--                        sb.append('"');
--                        return true;
--                    } else {
--                        warning("Unexpected type for constant " + expression.constant.toString());
--                    }
--                } else {
--                    warning("Unexpected annotation expression of type " + expression.getClass() + " and is "
--                            + expression);
--                }
--            }
--
--            return false;
--        }
--    }
--
--    public enum ClassKind {
--        CLASS,
--        INTERFACE,
--        ENUM,
--        ANNOTATION;
--
--        @NonNull
--        public static ClassKind forType(@Nullable TypeDeclaration declaration) {
--            if (declaration == null) {
--                return CLASS;
--            }
--            switch (TypeDeclaration.kind(declaration.modifiers)) {
--                case TypeDeclaration.INTERFACE_DECL:
--                    return INTERFACE;
--                case TypeDeclaration.ANNOTATION_TYPE_DECL:
--                    return ANNOTATION;
--                case TypeDeclaration.ENUM_DECL:
--                    return ENUM;
--                default:
--                    return CLASS;
--            }
--        }
--
--        public String getKeepType() {
--            // See http://proguard.sourceforge.net/manual/usage.html#classspecification
--            switch (this) {
--                case INTERFACE:
--                    return "interface";
--                case ENUM:
--                    return "enum";
--
--                case ANNOTATION:
--                case CLASS:
--                default:
--                    return "class";
--            }
--        }
--    }
--
--    /**
--     * An item in the XML file: this corresponds to a method, a field, or a method parameter, and
--     * has an associated set of annotations
--     */
--    private abstract static class Item implements Comparable<Item> {
--        @NonNull public final String containingClass;
--        @NonNull public final ClassKind classKind;
--
--        public Item(@NonNull String containingClass, @NonNull ClassKind classKind) {
--            this.containingClass = containingClass;
--            this.classKind = classKind;
--        }
--
--        public final List<AnnotationData> annotations = Lists.newArrayList();
--
--        void write(PrintWriter writer) {
--            if (annotations.isEmpty()) {
--                return;
--            }
--            writer.print("  <item name=\"");
--            writer.print(getSignature());
--            writer.println("\">");
--
--            for (AnnotationData annotation : annotations) {
--                annotation.write(writer);
--            }
--            writer.print("  </item>");
--            writer.println();
--        }
--
--        abstract boolean isFiltered(@NonNull ApiDatabase database);
--
--        @NonNull
--        abstract String getSignature();
--
--        @Override
--        public int compareTo(@SuppressWarnings("NullableProblems") @NonNull Item item) {
--            String signature1 = getSignature();
--            String signature2 = item.getSignature();
--
--            // IntelliJ's sorting order is not on the escaped HTML but the original
--            // signatures, which means android.os.AsyncTask<Params,Progress,Result>
--            // should appear *after* android.os.AsyncTask.Status, which when the <'s are
--            // escaped it does not
--            signature1 = signature1.replace('&', '.');
--            signature2 = signature2.replace('&', '.');
--
--            return signature1.compareTo(signature2);
--        }
--
--        @NonNull
--        public abstract String getKeepRule();
--
--        @NonNull
--        public abstract String getQualifiedClassName();
--    }
--
--    private static class ClassItem extends Item {
--        private ClassItem(@NonNull String containingClass, @NonNull ClassKind classKind) {
--            super(containingClass, classKind);
--        }
--
--        @NonNull
--        static ClassItem create(@NonNull String classFqn, @NonNull ClassKind kind) {
--            classFqn = ApiDatabase.getRawClass(classFqn);
--            return new ClassItem(classFqn, kind);
--        }
--
--        @Override
--        boolean isFiltered(@NonNull ApiDatabase database) {
--            return !database.hasClass(containingClass);
--        }
--
--        @NonNull
--        @Override
--        String getSignature() {
--            return escapeXml(containingClass);
--        }
--
--        @NonNull
--        @Override
--        public String getKeepRule() {
--            // See http://proguard.sourceforge.net/manual/usage.html#classspecification
--            return "-keep " + classKind.getKeepType() + " " + containingClass + "\n";
--        }
--
--        @NonNull
--        @Override
--        public String getQualifiedClassName() {
--            return containingClass;
--        }
--
--        @Override
--        public String toString() {
--            return "Class " + containingClass;
--        }
--
--        @Override
--        public boolean equals(Object o) {
--            if (this == o) {
--                return true;
--            }
--            if (o == null || getClass() != o.getClass()) {
--                return false;
--            }
--
--            ClassItem that = (ClassItem) o;
--
--            return containingClass.equals(that.containingClass);
--        }
--
--        @Override
--        public int hashCode() {
--            return containingClass.hashCode();
--        }
--    }
--
--    private static class FieldItem extends Item {
--
--        @NonNull
--        public final String fieldName;
--
--        @Nullable
--        public final String fieldType;
--
--        private FieldItem(@NonNull String containingClass, @NonNull ClassKind classKind,
--                @NonNull String fieldName, @Nullable String fieldType) {
--            super(containingClass, classKind);
--            this.fieldName = fieldName;
--            this.fieldType = fieldType;
--        }
--
--        @Nullable
--        static FieldItem create(String classFqn, @NonNull ClassKind classKind, FieldBinding field) {
--            String name = new String(field.name);
--            String type = getFieldType(field);
--            return classFqn != null ? new FieldItem(classFqn, classKind, name, type) : null;
--        }
--
--        @Nullable
--        private static String getFieldType(FieldBinding binding) {
--            if (binding.type != null) {
--                return new String(binding.type.readableName());
--            }
--
--            return null;
--        }
--
--        @Override
--        boolean isFiltered(@NonNull ApiDatabase database) {
--            return !database.hasField(containingClass, fieldName);
--        }
--
--        @NonNull
--        @Override
--        String getSignature() {
--            return escapeXml(containingClass) + ' ' + fieldName;
--        }
--
--        @NonNull
--        @Override
--        public String getKeepRule() {
--            if (fieldType == null) {
--                return ""; // imported item; these can't have keep rules
--            }
--            // See http://proguard.sourceforge.net/manual/usage.html#classspecification
--            return "-keep " + classKind.getKeepType() + " " + containingClass +
--                    " {\n    " + fieldType + " " + fieldName + "\n}\n";
--        }
--
--        @NonNull
--        @Override
--        public String getQualifiedClassName() {
--            return containingClass;
--        }
--
--        @Override
--        public String toString() {
--            return "Field " + containingClass + "#" + fieldName;
--        }
--
--        @Override
--        public boolean equals(Object o) {
--            if (this == o) {
--                return true;
--            }
--            if (o == null || getClass() != o.getClass()) {
--                return false;
--            }
--
--            FieldItem that = (FieldItem) o;
--
--            return containingClass.equals(that.containingClass) &&
--                    fieldName.equals(that.fieldName);
--        }
--
--        @Override
--        public int hashCode() {
--            int result = fieldName.hashCode();
--            result = 31 * result + containingClass.hashCode();
--            return result;
--        }
--    }
--
--    private static class MethodItem extends Item {
--
--        @NonNull
--        public final String methodName;
--
--        @NonNull
--        public final String parameterList;
--
--        @Nullable
--        public final String returnType;
--
--        public final boolean isConstructor;
--
--        private MethodItem(
--                @NonNull String containingClass,
--                @NonNull ClassKind classKind,
--                @Nullable String returnType,
--                @NonNull String methodName,
--                @NonNull String parameterList,
--                boolean isConstructor) {
--            super(containingClass, classKind);
--            this.returnType = returnType;
--            this.methodName = methodName;
--            this.parameterList = parameterList;
--            this.isConstructor = isConstructor;
--        }
--
--        @NonNull
--        public String getName() {
--            return methodName;
--        }
--
--        @Nullable
--        static MethodItem create(@Nullable String classFqn,
--                @NonNull ClassKind classKind,
--                @NonNull AbstractMethodDeclaration declaration,
--                @Nullable MethodBinding binding) {
--            if (classFqn == null || binding == null) {
--                return null;
--            }
--            String returnType = getReturnType(binding);
--            String methodName = getMethodName(binding);
--            Argument[] arguments = declaration.arguments;
--            boolean isVarargs = arguments != null && arguments.length > 0 &&
--                    arguments[arguments.length - 1].isVarArgs();
--            String parameterList = getParameterList(binding, isVarargs);
--            if (returnType == null || methodName == null) {
--                return null;
--            }
--            //noinspection PointlessBooleanExpression,ConstantConditions
--            if (!INCLUDE_TYPE_ARGS) {
--                classFqn = ApiDatabase.getRawClass(classFqn);
--                methodName = ApiDatabase.getRawMethod(methodName);
--            }
--            return new MethodItem(classFqn, classKind, returnType,
--                    methodName, parameterList,
--                    binding.isConstructor());
--        }
--
--        @NonNull
--        @Override
--        String getSignature() {
--            StringBuilder sb = new StringBuilder(100);
--            sb.append(escapeXml(containingClass));
--            sb.append(' ');
--
--            if (isConstructor) {
--                sb.append(escapeXml(methodName));
--            } else {
--                assert returnType != null;
--                sb.append(escapeXml(returnType));
--                sb.append(' ');
--                sb.append(escapeXml(methodName));
--            }
--
--            sb.append('(');
--
--            // The signature must match *exactly* the formatting used by IDEA,
--            // since it looks up external annotations in a map by this key.
--            // Therefore, it is vital that the parameter list uses exactly one
--            // space after each comma between parameters, and *no* spaces between
--            // generics variables, e.g. foo(Map<A,B>, int)
--
--            // Insert spaces between commas, but not in generics signatures
--            int balance = 0;
--            for (int i = 0, n = parameterList.length(); i < n; i++) {
--                char c = parameterList.charAt(i);
--                if (c == '<') {
--                    balance++;
--                    sb.append("<");
--                } else if (c == '>') {
--                    balance--;
--                    sb.append(">");
--                } else if (c == ',') {
--                    sb.append(',');
--                    if (balance == 0) {
--                        sb.append(' ');
--                    }
--                } else {
--                    sb.append(c);
--                }
--            }
--            sb.append(')');
--            return sb.toString();
--        }
--
--        @Override
--        boolean isFiltered(@NonNull ApiDatabase database) {
--            return !database.hasMethod(containingClass, methodName, parameterList);
--        }
--
--        @Override
--        public boolean equals(Object o) {
--            if (this == o) {
--                return true;
--            }
--            if (o == null || getClass() != o.getClass()) {
--                return false;
--            }
--
--            MethodItem that = (MethodItem) o;
--
--            return isConstructor == that.isConstructor && containingClass
--                    .equals(that.containingClass) && methodName.equals(that.methodName)
--                    && parameterList.equals(that.parameterList);
--
--        }
--
--        @Override
--        public int hashCode() {
--            int result = methodName.hashCode();
--            result = 31 * result + containingClass.hashCode();
--            result = 31 * result + parameterList.hashCode();
--            result = 31 * result + (returnType != null ? returnType.hashCode() : 0);
--            result = 31 * result + (isConstructor ? 1 : 0);
--            return result;
--        }
--
--        @Override
--        public String toString() {
--            return "Method " + containingClass + "#" + methodName;
--        }
--
--        @NonNull
--        @Override
--        public String getKeepRule() {
--            // See http://proguard.sourceforge.net/manual/usage.html#classspecification
--            StringBuilder sb = new StringBuilder();
--            sb.append("-keep ");
--            sb.append(classKind.getKeepType());
--            sb.append(" ");
--            sb.append(containingClass);
--            sb.append(" {\n");
--            sb.append("    ");
--            if (isConstructor) {
--                sb.append("<init>");
--            } else {
--                sb.append(returnType);
--                sb.append(" ");
--                sb.append(methodName);
--            }
--            sb.append("(");
--            sb.append(parameterList); // TODO: Strip generics?
--            sb.append(")\n");
--            sb.append("}\n");
--
--            return sb.toString();
--        }
--
--        @NonNull
--        @Override
--        public String getQualifiedClassName() {
--            return containingClass;
--        }
--    }
--
--    @Nullable
--    private static String getReturnType(MethodBinding binding) {
--        if (binding.returnType != null) {
--            return new String(binding.returnType.readableName());
--        } else if (binding.declaringClass != null) {
--            assert binding.isConstructor();
--            return new String(binding.declaringClass.readableName());
--        }
--
--        return null;
--    }
--
--    @Nullable
--    private static String getMethodName(@NonNull MethodBinding binding) {
--        if (binding.isConstructor()) {
--            if (binding.declaringClass != null) {
--                String classFqn = new String(binding.declaringClass.readableName());
--                return classFqn.substring(classFqn.lastIndexOf('.') + 1);
--            }
--
--        }
--        if (binding.selector != null) {
--            return new String(binding.selector);
--        }
--
--        assert binding.isConstructor();
--
--        return null;
--    }
--
--    @NonNull
--    private static String getParameterList(@NonNull MethodBinding binding, boolean isVarargs) {
--        // Create compact type signature (no spaces around commas or generics arguments)
--        StringBuilder sb = new StringBuilder();
--        TypeBinding[] typeParameters = binding.parameters;
--        if (typeParameters != null) {
--            for (int i = 0, n = typeParameters.length; i < n; i++) {
--                TypeBinding parameter = typeParameters[i];
--                if (i > 0) {
--                    sb.append(',');
--                }
--                String str = fixParameterString(new String(parameter.readableName()));
--                if (isVarargs && i == n - 1 && str.endsWith("[]")) {
--                    str = str.substring(0, str.length() - 2) + "...";
--                }
--                sb.append(str);
--            }
--        }
--        return sb.toString();
--    }
--
--    private static class ParameterItem extends MethodItem {
--        @NonNull
--        public final String argIndex;
--
--        private ParameterItem(
--                @NonNull String containingClass,
--                @NonNull ClassKind classKind,
--                @Nullable String returnType,
--                @NonNull String methodName,
--                @NonNull String parameterList,
--                boolean isConstructor,
--                @NonNull String argIndex) {
--            super(containingClass, classKind, returnType, methodName, parameterList, isConstructor);
--            this.argIndex = argIndex;
--        }
--
--        @Nullable
--        static ParameterItem create(
--                AbstractMethodDeclaration methodDeclaration,
--                Argument argument,
--                String classFqn,
--                ClassKind classKind,
--                MethodBinding methodBinding,
--                LocalVariableBinding parameterBinding) {
--            if (classFqn == null || methodBinding == null || parameterBinding == null) {
--                return null;
--            }
--
--            String methodName = getMethodName(methodBinding);
--            Argument[] arguments = methodDeclaration.arguments;
--            boolean isVarargs = arguments != null && arguments.length > 0 &&
--                    arguments[arguments.length - 1].isVarArgs();
--            String parameterList = getParameterList(methodBinding, isVarargs);
--            String returnType = getReturnType(methodBinding);
--            if (methodName == null || returnType == null) {
--                return null;
--            }
--
--            int index = 0;
--            boolean found = false;
--            if (methodDeclaration.arguments != null) {
--                for (Argument a : methodDeclaration.arguments) {
--                    if (a == argument) {
--                        found = true;
--                        break;
--                    }
--                    index++;
--                }
--            }
--            if (!found) {
--                return null;
--            }
--            String argNum = Integer.toString(index);
--
--            //noinspection PointlessBooleanExpression,ConstantConditions
--            if (!INCLUDE_TYPE_ARGS) {
--                classFqn = ApiDatabase.getRawClass(classFqn);
--                methodName = ApiDatabase.getRawMethod(methodName);
--            }
--            return new ParameterItem(classFqn, classKind, returnType, methodName, parameterList,
--                    methodBinding.isConstructor(), argNum);
--        }
--
--
--        @NonNull
--        @Override
--        String getSignature() {
--            return super.getSignature() + ' ' + argIndex;
--        }
--
--        @Override
--        public boolean equals(Object o) {
--            if (this == o) {
--                return true;
--            }
--            if (o == null || getClass() != o.getClass()) {
--                return false;
--            }
--            if (!super.equals(o)) {
--                return false;
--            }
--
--            ParameterItem that = (ParameterItem) o;
--
--            return argIndex.equals(that.argIndex);
--
--        }
--
--        @Override
--        public int hashCode() {
--            int result = super.hashCode();
--            result = 31 * result + argIndex.hashCode();
--            return result;
--        }
--
--        @Override
--        public String toString() {
--            return "Parameter #" + argIndex + " in " + super.toString();
--        }
--
--        @NonNull
--        @Override
--        public String getKeepRule() {
--            return "";
--        }
--    }
--
--    private class AnnotationVisitor extends ASTVisitor {
--        @Override
--        public boolean visit(Argument argument, BlockScope scope) {
--            Annotation[] annotations = argument.annotations;
--            if (hasRelevantAnnotations(annotations)) {
--                ReferenceContext referenceContext = scope.referenceContext();
--                if (referenceContext instanceof AbstractMethodDeclaration) {
--                    MethodBinding binding = ((AbstractMethodDeclaration) referenceContext).binding;
--                    ClassScope classScope = findClassScope(scope);
--                    if (classScope == null) {
--                        return false;
--                    }
--                    String fqn = getFqn(classScope);
--                    ClassKind kind = ClassKind.forType(classScope.referenceContext);
--                    Item item = ParameterItem.create(
--                            (AbstractMethodDeclaration) referenceContext, argument, fqn, kind,
--                            binding, argument.binding);
--                    if (item != null) {
--                        addItem(fqn, item);
--                        addAnnotations(annotations, item);
--                    }
--                }
--            }
--            return false;
--        }
--
--        @Override
--        public boolean visit(ConstructorDeclaration constructorDeclaration, ClassScope scope) {
--            Annotation[] annotations = constructorDeclaration.annotations;
--            if (hasRelevantAnnotations(annotations)) {
--                MethodBinding constructorBinding = constructorDeclaration.binding;
--                if (constructorBinding == null) {
--                    return false;
--                }
--
--                String fqn = getFqn(scope);
--                ClassKind kind = ClassKind.forType(scope.referenceContext);
--                Item item = MethodItem.create(fqn, kind, constructorDeclaration, constructorBinding);
--                if (item != null) {
--                    addItem(fqn, item);
--                    addAnnotations(annotations, item);
--                }
--            }
--
--            Argument[] arguments = constructorDeclaration.arguments;
--            if (arguments != null) {
--                for (Argument argument : arguments) {
--                    argument.traverse(this, constructorDeclaration.scope);
--                }
--            }
--            return false;
--        }
--
--        @Override
--        public boolean visit(FieldDeclaration fieldDeclaration, MethodScope scope) {
--            Annotation[] annotations = fieldDeclaration.annotations;
--            if (hasRelevantAnnotations(annotations)) {
--                FieldBinding fieldBinding = fieldDeclaration.binding;
--                if (fieldBinding == null) {
--                    return false;
--                }
--
--                String fqn = getFqn(scope);
--                ClassKind kind = scope.referenceContext instanceof TypeDeclaration ?
--                        ClassKind.forType((TypeDeclaration)scope.referenceContext) :
--                        ClassKind.CLASS;
--                Item item = FieldItem.create(fqn, kind, fieldBinding);
--                if (item != null && fqn != null) {
--                    addItem(fqn, item);
--                    addAnnotations(annotations, item);
--                }
--            }
--            return false;
--        }
--
--        @Override
--        public boolean visit(MethodDeclaration methodDeclaration, ClassScope scope) {
--            Annotation[] annotations = methodDeclaration.annotations;
--            if (hasRelevantAnnotations(annotations)) {
--                MethodBinding methodBinding = methodDeclaration.binding;
--                if (methodBinding == null) {
--                    return false;
--                }
--                String fqn = getFqn(scope);
--                ClassKind kind = ClassKind.forType(scope.referenceContext);
--                MethodItem item = MethodItem.create(fqn, kind, methodDeclaration,
--                        methodDeclaration.binding);
--                if (item != null) {
--                    addItem(fqn, item);
--
--                    // Deliberately skip findViewById()'s return nullability
--                    // for now; it's true that findViewById can return null,
--                    // but that means all code which does findViewById(R.id.foo).something()
--                    // will be flagged as potentially throwing an NPE, and many developers
--                    // will do this when they *know* that the id exists (in which case
--                    // the method won't return null.)
--                    boolean skipReturnAnnotations = false;
--                    if ("findViewById".equals(item.getName())) {
--                        skipReturnAnnotations = true;
--                        if (item.annotations.isEmpty()) {
--                            // No other annotations so far: just remove it
--                            removeItem(fqn, item);
--                        }
--                    }
--
--                    if (!skipReturnAnnotations) {
--                        addAnnotations(annotations, item);
--                    }
--                }
--            }
--
--            Argument[] arguments = methodDeclaration.arguments;
--            if (arguments != null) {
--                for (Argument argument : arguments) {
--                    argument.traverse(this, methodDeclaration.scope);
--                }
--            }
--            return false;
--        }
--
--        @Override
--        public boolean visit(TypeDeclaration localTypeDeclaration, BlockScope scope) {
--            Annotation[] annotations = localTypeDeclaration.annotations;
--            if (hasRelevantAnnotations(annotations)) {
--                SourceTypeBinding binding = localTypeDeclaration.binding;
--                if (binding == null) {
--                    return true;
--                }
--
--                String fqn = getFqn(scope);
--                if (fqn == null) {
--                    fqn = new String(localTypeDeclaration.binding.readableName());
--                }
--                Item item = ClassItem.create(fqn, ClassKind.forType(localTypeDeclaration));
--                addItem(fqn, item);
--                addAnnotations(annotations, item);
--
--            }
--            return true;
--        }
--
--        @Override
--        public boolean visit(TypeDeclaration memberTypeDeclaration, ClassScope scope) {
--            Annotation[] annotations = memberTypeDeclaration.annotations;
--            if (hasRelevantAnnotations(annotations)) {
--                SourceTypeBinding binding = memberTypeDeclaration.binding;
--                if (!(binding instanceof MemberTypeBinding)) {
--                    return true;
--                }
--                if (binding.isAnnotationType() || binding.isAnonymousType()) {
--                    return false;
--                }
--
--                String fqn = new String(memberTypeDeclaration.binding.readableName());
--                Item item = ClassItem.create(fqn, ClassKind.forType(memberTypeDeclaration));
--                addItem(fqn, item);
--                addAnnotations(annotations, item);
--            }
--            return true;
--        }
--
--        @Override
--        public boolean visit(TypeDeclaration typeDeclaration, CompilationUnitScope scope) {
--            Annotation[] annotations = typeDeclaration.annotations;
--            if (hasRelevantAnnotations(annotations)) {
--                SourceTypeBinding binding = typeDeclaration.binding;
--                if (binding == null) {
--                    return true;
--                }
--                String fqn = new String(typeDeclaration.binding.readableName());
--                Item item = ClassItem.create(fqn, ClassKind.forType(typeDeclaration));
--                addItem(fqn, item);
--                addAnnotations(annotations, item);
--
--            }
--            return true;
--        }
--    }
--}
-diff --git a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/TypedefCollector.java b/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/TypedefCollector.java
-deleted file mode 100644
-index 5924f4c..0000000
---- a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/TypedefCollector.java
-+++ /dev/null
-@@ -1,154 +0,0 @@
--/*
-- * Copyright (C) 2014 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.build.gradle.tasks.annotations;
--
--import com.android.annotations.NonNull;
--import com.google.common.collect.Lists;
--import com.google.common.collect.Maps;
--
--import org.eclipse.jdt.internal.compiler.ASTVisitor;
--import org.eclipse.jdt.internal.compiler.ast.Annotation;
--import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
--import org.eclipse.jdt.internal.compiler.ast.Javadoc;
--import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
--import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
--import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
--import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope;
--import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
--
--import java.io.File;
--import java.util.ArrayList;
--import java.util.Collection;
--import java.util.List;
--import java.util.Map;
--
--/** Gathers information about typedefs (@IntDef and @StringDef */
--public class TypedefCollector extends ASTVisitor {
--    private Map<String,List<Annotation>> mMap = Maps.newHashMap();
--
--    private final boolean mRequireHide;
--    private final boolean mRequireSourceRetention;
--    private CompilationUnitDeclaration mCurrentUnit;
--    private List<String> mTypedefClasses = Lists.newArrayList();
--
--    public TypedefCollector(
--            @NonNull Collection<CompilationUnitDeclaration> units,
--            boolean requireHide,
--            boolean requireSourceRetention) {
--        mRequireHide = requireHide;
--        mRequireSourceRetention = requireSourceRetention;
--
--        for (CompilationUnitDeclaration unit : units) {
--            mCurrentUnit = unit;
--            unit.traverse(this, unit.scope);
--            mCurrentUnit = null;
--        }
--    }
--
--    public List<String> getNonPublicTypedefClasses() {
--        return mTypedefClasses;
--    }
--
--    public Map<String,List<Annotation>> getTypedefs() {
--        return mMap;
--    }
--
--    @Override
--    public boolean visit(TypeDeclaration memberTypeDeclaration, ClassScope scope) {
--        return recordTypedefs(memberTypeDeclaration);
--
--    }
--
--    @Override
--    public boolean visit(TypeDeclaration typeDeclaration, CompilationUnitScope scope) {
--        return recordTypedefs(typeDeclaration);
--    }
--
--    private boolean recordTypedefs(TypeDeclaration declaration) {
--        SourceTypeBinding binding = declaration.binding;
--        if (binding == null) {
--            return false;
--        }
--        Annotation[] annotations = declaration.annotations;
--        if (annotations != null) {
--            if (declaration.binding.isAnnotationType()) {
--                for (Annotation annotation : annotations) {
--                    String typeName = Extractor.getFqn(annotation);
--                    if (typeName == null) {
--                        continue;
--                    }
--
--                    if (Extractor.isNestedAnnotation(typeName)) {
--                        String fqn = new String(binding.readableName());
--
--                        List<Annotation> list = mMap.get(fqn);
--                        if (list == null) {
--                            list = new ArrayList<Annotation>(2);
--                            mMap.put(fqn, list);
--                        }
--                        list.add(annotation);
--
--                        if (mRequireHide) {
--                            Javadoc javadoc = declaration.javadoc;
--                            if (javadoc != null) {
--                                StringBuffer stringBuffer = new StringBuffer(200);
--                                javadoc.print(0, stringBuffer);
--                                String documentation = stringBuffer.toString();
--                                if (!documentation.contains("@hide")) {
--                                    Extractor.warning(getFileName()
--                                            + ": The typedef annotation " + fqn
--                                            + " should specify @hide in a doc comment");
--                                }
--                            }
--                        }
--                        if (mRequireSourceRetention
--                                && !Extractor.hasSourceRetention(annotations)) {
--                            Extractor.warning(getFileName()
--                                    + ": The typedef annotation " + fqn
--                                    + " should have @Retention(RetentionPolicy.SOURCE)");
--                        }
--                        if (declaration.binding != null
--                                && (declaration.modifiers & ClassFileConstants.AccPublic) == 0) {
--                            StringBuilder sb = new StringBuilder(100);
--                            for (char c : declaration.binding.qualifiedPackageName()) {
--                                if (c == '.') {
--                                    sb.append('/');
--                                } else {
--                                    sb.append(c);
--                                }
--                            }
--                            sb.append(File.separatorChar);
--                            for (char c : declaration.binding.qualifiedSourceName()) {
--                                if (c == '.') {
--                                    sb.append('$');
--                                } else {
--                                    sb.append(c);
--                                }
--                            }
--                            mTypedefClasses.add(sb.toString());
--                        }
--                    }
--                }
--            }
--        }
--        return true;
--    }
--
--    private String getFileName() {
--        return new String(mCurrentUnit.getFileName());
--    }
--}
-diff --git a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/TypedefRemover.java b/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/TypedefRemover.java
-deleted file mode 100644
-index 758f125..0000000
---- a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/TypedefRemover.java
-+++ /dev/null
-@@ -1,164 +0,0 @@
--/*
-- * Copyright (C) 2015 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.build.gradle.tasks.annotations;
--
--import static com.android.SdkConstants.DOT_CLASS;
--import static org.objectweb.asm.Opcodes.ASM5;
--
--import com.android.annotations.NonNull;
--import com.google.common.collect.Lists;
--import com.google.common.collect.Sets;
--import com.google.common.io.Files;
--
--import org.objectweb.asm.ClassReader;
--import org.objectweb.asm.ClassVisitor;
--import org.objectweb.asm.ClassWriter;
--
--import java.io.File;
--import java.io.IOException;
--import java.util.List;
--import java.util.Set;
--
--/**
-- * Finds and deletes typedef annotation classes (and also warns if their
-- * retention is wrong, such that usages of the annotation embeds data
-- * into the .class file.)
-- * <p>
-- * (Based on the similar class in {@code development/tools/rmtypedefs/})
-- */
-- at SuppressWarnings("SpellCheckingInspection")
--public class TypedefRemover {
--    private final Extractor mExtractor;
--    private final boolean mQuiet;
--    private final boolean mVerbose;
--    private final boolean mDryRun;
--
--    public TypedefRemover(
--            @NonNull Extractor extractor,
--            boolean quiet,
--            boolean verbose,
--            boolean dryRun) {
--        mExtractor = extractor;
--        mQuiet = quiet;
--        mVerbose = verbose;
--        mDryRun = dryRun;
--    }
--
--    private Set<String> mAnnotationNames = Sets.newHashSet();
--    private List<File> mAnnotationClassFiles = Lists.newArrayList();
--    private Set<File> mAnnotationOuterClassFiles = Sets.newHashSet();
--
--    public void remove(@NonNull File classDir, @NonNull List<String> owners) {
--        if (!mQuiet) {
--            mExtractor.info("Deleting @IntDef and @StringDef annotation class files");
--        }
--
--        // Record typedef annotation names and files
--        for (String owner : owners) {
--            File file = new File(classDir, owner.replace('/', File.separatorChar) + DOT_CLASS);
--            addTypeDef(owner, file);
--        }
--
--        // Rewrite the .class files for any classes that *contain* typedefs as innerclasses
--        rewriteOuterClasses();
--
--        // Removes the actual .class files for the typedef annotations
--        deleteAnnotationClasses();
--    }
--
--    /**
--     * Records the given class name (internal name) and class file path as corresponding to a
--     * typedef annotation
--     * */
--    private void addTypeDef(String name, File file) {
--        mAnnotationClassFiles.add(file);
--        mAnnotationNames.add(name);
--
--        String fileName = file.getName();
--        int index = fileName.lastIndexOf('$');
--        if (index != -1) {
--            File parentFile = file.getParentFile();
--            assert parentFile != null : file;
--            File container = new File(parentFile, fileName.substring(0, index) + ".class");
--            if (container.exists()) {
--                mAnnotationOuterClassFiles.add(container);
--            } else {
--                Extractor.error("Warning: Could not find outer class " + container
--                        + " for typedef " + file);
--            }
--        }
--    }
--
--    /**
--     * Rewrites the outer classes containing the typedefs such that they no longer refer to
--     * the (now removed) typedef annotation inner classes
--     */
--    private void rewriteOuterClasses() {
--        for (File file : mAnnotationOuterClassFiles) {
--            byte[] bytes;
--            try {
--                bytes = Files.toByteArray(file);
--            } catch (IOException e) {
--                Extractor.error("Could not read " + file + ": " + e.getLocalizedMessage());
--                continue;
--            }
--
--            ClassWriter classWriter = new ClassWriter(ASM5);
--            ClassVisitor classVisitor = new ClassVisitor(ASM5, classWriter) {
--                @Override
--                public void visitInnerClass(String name, String outerName, String innerName,
--                        int access) {
--                    if (!mAnnotationNames.contains(name)) {
--                        super.visitInnerClass(name, outerName, innerName, access);
--                    }
--                }
--            };
--            ClassReader reader = new ClassReader(bytes);
--            reader.accept(classVisitor, 0);
--            byte[] rewritten = classWriter.toByteArray();
--            try {
--                Files.write(rewritten, file);
--            } catch (IOException e) {
--                Extractor.error("Could not write " + file + ": " + e.getLocalizedMessage());
--                //noinspection UnnecessaryContinue
--                continue;
--            }
--        }
--    }
--
--    /**
--     * Performs the actual deletion (or display, if in dry-run mode) of the typedef annotation
--     * files
--     */
--    private void deleteAnnotationClasses() {
--        for (File mFile : mAnnotationClassFiles) {
--            if (mVerbose) {
--                if (mDryRun) {
--                    mExtractor.info("Would delete " + mFile);
--                } else {
--                    mExtractor.info("Deleting " + mFile);
--                }
--            }
--            if (!mDryRun) {
--                boolean deleted = mFile.delete();
--                if (!deleted) {
--                    Extractor.warning("Could not delete " + mFile);
--                }
--            }
--        }
--    }
--}
-\ No newline at end of file
-diff --git a/base/build-system/gradle/src/main/groovy/com/android/build/gradle/BaseExtension.java b/base/build-system/gradle/src/main/groovy/com/android/build/gradle/BaseExtension.java
-index b4f4bb0..b91a3dd 100644
---- a/base/build-system/gradle/src/main/groovy/com/android/build/gradle/BaseExtension.java
-+++ b/base/build-system/gradle/src/main/groovy/com/android/build/gradle/BaseExtension.java
-@@ -34,7 +34,6 @@ import com.android.build.gradle.internal.dsl.BuildType;
- import com.android.build.gradle.internal.dsl.CoreBuildType;
- import com.android.build.gradle.internal.dsl.CoreProductFlavor;
- import com.android.build.gradle.internal.dsl.DexOptions;
--import com.android.build.gradle.internal.dsl.LintOptions;
- import com.android.build.gradle.internal.dsl.PackagingOptions;
- import com.android.build.gradle.internal.dsl.PreprocessingOptions;
- import com.android.build.gradle.internal.dsl.ProductFlavor;
-@@ -92,9 +91,6 @@ public abstract class BaseExtension implements AndroidConfig {
-     /** Options for aapt, tool for packaging resources. */
-     final AaptOptions aaptOptions;
- 
--    /** Lint options. */
--    final LintOptions lintOptions;
--
-     /** Dex options. */
-     final DexOptions dexOptions;
- 
-@@ -188,7 +184,6 @@ public abstract class BaseExtension implements AndroidConfig {
- 
-         aaptOptions = instantiator.newInstance(AaptOptions.class);
-         dexOptions = instantiator.newInstance(DexOptions.class);
--        lintOptions = instantiator.newInstance(LintOptions.class);
-         testOptions = instantiator.newInstance(TestOptions.class);
-         compileOptions = instantiator.newInstance(CompileOptions.class);
-         packagingOptions = instantiator.newInstance(PackagingOptions.class);
-@@ -405,14 +400,6 @@ public abstract class BaseExtension implements AndroidConfig {
-         action.execute(dexOptions);
-     }
- 
--    /**
--     * Configure lint options.
--     */
--    public void lintOptions(Action<LintOptions> action) {
--        checkWritability();
--        action.execute(lintOptions);
--    }
--
-     /** Configures the test options. */
-     public void testOptions(Action<TestOptions> action) {
-         checkWritability();
-@@ -757,12 +744,6 @@ public abstract class BaseExtension implements AndroidConfig {
- 
-     /** {@inheritDoc} */
-     @Override
--    public LintOptions getLintOptions() {
--        return lintOptions;
--    }
--
--    /** {@inheritDoc} */
--    @Override
-     public PackagingOptions getPackagingOptions() {
-         return packagingOptions;
-     }
diff --git a/debian/patches/series b/debian/patches/series
index 63b7123..5a9f2c3 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -4,4 +4,3 @@ gradle-experimental.patch
 SdkTestCase.patch
 project-test-lib.patch
 trove3.patch
-disable-lint.patch

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/android-platform-tools-base.git



More information about the pkg-java-commits mailing list