// Copyright 2016 The Bazel Authors. All rights reserved. // // 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.google.devtools.build.android; import com.google.common.base.Joiner; import com.google.common.base.Optional; import com.google.common.base.Predicate; import com.google.common.base.Splitter; import com.google.common.collect.ComparisonChain; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSortedSet; import com.google.common.collect.Iterables; import com.google.common.collect.Ordering; import java.util.ArrayList; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.TreeSet; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * A parsed set of configuration filters for a split flag or an output filename. * *
The natural ordering of this class sorts by number of configurations, then by highest required * API version, if any, then by other specifiers (case-insensitive), with ties broken by the * filename or split flag originally used to create the instance (case-sensitive). * *
This has the following useful property: When created with {@link fromFilenameSuffix}, this will be the original filename from aapt;
* when created with {@link fromSplitFlag}, this will be the filename to rename to.
*/
private final String filename;
/**
* A set of resource configurations which will be included in this split, sorted so that the
* configs with the highest API versions come first.
*
* It's okay for this to collapse duplicates, because aapt forbids duplicate resource
* configurations across all splits in the same invocation anyway.
*/
private final ImmutableSortedSet This means that there must be a one-to-one mapping from each configuration in this filter to
* a configuration in the {@code other} filter such that the non-API-version specifiers of the two
* configurations match and the API version specifier of the {@code other} filter's configuration
* is greater than or equal to the API version specifier of this filter's configuration.
*
* Order of whole configurations doesn't matter, as aapt will reorder the configurations
* according to complicated internal logic (yes, logic even more complicated than this!).
*
* Care is needed with API version specifiers because aapt may add or change minimum API
* version specifiers to configurations according to whether they had specifiers which are only
* supported in certain versions of Android. It will only ever increase the minimum version or
* leave it the same.
*
* The other (non-wildcard) specifiers should be case-insensitive identical, including order;
* aapt will not allow parts of a single configuration to be parsed out of order.
*
* @see ResourceConfiguration#matchesConfigurationFromFilename(ResourceConfiguration)
*/
boolean matchesFilterFromFilename(SplitConfigurationFilter filenameFilter) {
if (filenameFilter.configs.size() != this.configs.size()) {
return false;
}
List The natural ordering of this class sorts by required API version, if any, then by other
* specifiers.
*
* This has the following useful property: Matches an 'any' part and the dash following it, or for an 'any' part which is the last
* specifier, the dash preceding it. In the former case, it must be a full part - that is,
* preceded by the beginning of the string or a dash, which will not be consumed.
*/
private static final Pattern WILDCARD_SPECIFIER = Pattern.compile("(?<=^|-)any(?:-|$)|-any$");
/**
* Pattern to match the API version and capture the version number.
*
* It must always be the last specifier in a config, although it may also be the first if
* there are no other specifiers.
*/
private static final Pattern API_VERSION = Pattern.compile("(?:-|^)v(\\d+)$");
/** Parses a resource configuration into a form that can be compared to other configurations. */
static ResourceConfiguration fromString(String text) {
// Case is ignored for resource configurations (aapt lowercases internally),
// and wildcards can be dropped.
String cleanSpecifiers =
WILDCARD_SPECIFIER.matcher(text.toLowerCase(Locale.ENGLISH)).replaceAll("");
Matcher apiVersionMatcher = API_VERSION.matcher(cleanSpecifiers);
if (apiVersionMatcher.find()) {
return new ResourceConfiguration(
cleanSpecifiers.substring(0, apiVersionMatcher.start()),
Integer.parseInt(apiVersionMatcher.group(1)));
} else {
return new ResourceConfiguration(cleanSpecifiers, 0);
}
}
/** The specifiers for this resource configuration, besides API version, in lowercase. */
private final String specifiers;
/** The API version, or 0 to indicate that no API version was present in the original config. */
private final int apiVersion;
private ResourceConfiguration(String specifiers, int apiVersion) {
this.specifiers = specifiers;
this.apiVersion = apiVersion;
}
/**
* Checks that the {@code other} configuration could be a filename generated from this one.
*
* @see SplitConfigurationFilter#matchesFilterFromFilename(SplitConfigurationFilter)
*/
boolean matchesConfigurationFromFilename(ResourceConfiguration other) {
return Objects.equals(other.specifiers, this.specifiers)
&& other.apiVersion >= this.apiVersion;
}
static final class MatchesConfigurationFromFilename
implements Predicate
* Given two sets of {@link SplitConfigurationFilter}s, one from the input split flags, and one from
* aapt's outputs... Each member of the output set can be matched to the greatest member of the
* input set for which {@code input.matchesFilterFromFilename(output)} is true.
*/
final class SplitConfigurationFilter implements Comparable
* Given two sets of {@link ResourceConfiguration}s, one from an input split flag, and one from
* aapt's output... Each member of the output set can be matched to the greatest member of the
* input set for which {@code input.matchesConfigurationFromFilename(output)} is true.
*/
static final class ResourceConfiguration implements Comparable