[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