aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/tools/android/java/com/google/devtools
diff options
context:
space:
mode:
authorGravatar corysmith <corysmith@google.com>2017-05-15 16:56:01 +0200
committerGravatar Dmitry Lomov <dslomov@google.com>2017-05-15 19:51:22 +0200
commitda6bbce1ee317f3b4d7ca245058049ab2a032848 (patch)
treec32d28b144454f8ad1ed6c96ab7cebe2f89bb836 /src/tools/android/java/com/google/devtools
parent296cd4913d40b756e29fbe0aa6055addf228da6d (diff)
Further Refactoring/Yak Shaving
* Extract the FieldInitializer with placeholder ids from the AndroidResourceClassWriter * Extract a resource sink interface from the AndroidResourceClassWriter (a little renaming, the change isn't actually that big.) RELNOTES: None PiperOrigin-RevId: 156053478
Diffstat (limited to 'src/tools/android/java/com/google/devtools')
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/AndroidResourceClassWriter.java454
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/AndroidResourceSymbolSink.java28
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/DataResource.java10
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/DataResourceXml.java6
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/DataValueFile.java5
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/ParsedAndroidData.java5
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/PlaceholderIdFieldInitializerBuilder.java442
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/XmlResourceValue.java11
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/resources/ResourceSymbols.java2
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/xml/ArrayXmlResourceValue.java9
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/xml/AttrXmlResourceValue.java43
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/xml/IdXmlResourceValue.java7
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/xml/PluralXmlResourceValue.java7
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/xml/PublicXmlResourceValue.java7
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/xml/ResourcesAttribute.java5
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/xml/SimpleXmlResourceValue.java7
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/xml/StyleXmlResourceValue.java7
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/xml/StyleableXmlResourceValue.java7
18 files changed, 553 insertions, 509 deletions
diff --git a/src/tools/android/java/com/google/devtools/build/android/AndroidResourceClassWriter.java b/src/tools/android/java/com/google/devtools/build/android/AndroidResourceClassWriter.java
index 1f0360c85b..2fcbe8e93c 100644
--- a/src/tools/android/java/com/google/devtools/build/android/AndroidResourceClassWriter.java
+++ b/src/tools/android/java/com/google/devtools/build/android/AndroidResourceClassWriter.java
@@ -19,77 +19,49 @@ import com.android.SdkConstants;
import com.android.resources.ResourceType;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Ordering;
import com.google.devtools.build.android.AndroidFrameworkAttrIdProvider.AttrLookupException;
import com.google.devtools.build.android.resources.FieldInitializer;
-import com.google.devtools.build.android.resources.IntArrayFieldInitializer;
-import com.google.devtools.build.android.resources.IntFieldInitializer;
import com.google.devtools.build.android.resources.RClassGenerator;
import java.io.BufferedWriter;
import java.io.Flushable;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
-import java.util.Collection;
-import java.util.EnumMap;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
-import java.util.Set;
-import java.util.SortedMap;
-import java.util.TreeMap;
-import java.util.TreeSet;
-import java.util.logging.Logger;
/**
* Generates the R class for an android_library with made up field initializers for the ids. The
* real ids will be assigned when we build the android_binary.
*
- * Collects the R class fields from the merged resource maps, and then writes out the resource class
- * files.
+ * <p>Collects the R class fields from the merged resource maps, and then writes out the resource
+ * class files.
*/
-public class AndroidResourceClassWriter implements Flushable {
+public class AndroidResourceClassWriter implements Flushable, AndroidResourceSymbolSink {
/** Create a new class writer. */
public static AndroidResourceClassWriter createWith(
Path androidJar, Path out, String javaPackage) {
- return new AndroidResourceClassWriter(
- new AndroidFrameworkAttrIdJar(androidJar), out, javaPackage);
+ return of(new AndroidFrameworkAttrIdJar(androidJar), out, javaPackage);
}
@VisibleForTesting
public static AndroidResourceClassWriter of(
AndroidFrameworkAttrIdProvider androidIdProvider, Path outputBasePath, String packageName) {
- return new AndroidResourceClassWriter(androidIdProvider, outputBasePath, packageName);
+ return new AndroidResourceClassWriter(
+ new PlaceholderIdFieldInitializerBuilder(androidIdProvider), outputBasePath, packageName);
}
-
- private static final Logger logger =
- Logger.getLogger(AndroidResourceClassWriter.class.getName());
- private static final int APP_PACKAGE_MASK = 0x7f000000;
- private static final int ATTR_TYPE_ID = 1;
- private final AndroidFrameworkAttrIdProvider androidIdProvider;
+
private final Path outputBasePath;
private final String packageName;
private boolean includeClassFile = true;
private boolean includeJavaFile = true;
- private final Map<ResourceType, Set<String>> innerClasses = new EnumMap<>(ResourceType.class);
- private final Map<String, Map<String, Boolean>> styleableAttrs = new HashMap<>();
- private final Map<ResourceType, SortedMap<String, Optional<Integer>>> publicIds =
- new EnumMap<>(ResourceType.class);
-
- private static final String NORMALIZED_ANDROID_PREFIX = "android_";
+ private final PlaceholderIdFieldInitializerBuilder generator;
private AndroidResourceClassWriter(
- AndroidFrameworkAttrIdProvider androidIdProvider, Path outputBasePath, String packageName) {
- this.androidIdProvider = androidIdProvider;
+ PlaceholderIdFieldInitializerBuilder generator, Path outputBasePath, String packageName) {
+ this.generator = generator;
this.outputBasePath = outputBasePath;
this.packageName = packageName;
}
@@ -102,346 +74,36 @@ public class AndroidResourceClassWriter implements Flushable {
this.includeJavaFile = include;
}
- public void writeSimpleResource(ResourceType type, String name) {
- Set<String> fields = innerClasses.get(type);
- if (fields == null) {
- fields = new HashSet<>();
- innerClasses.put(type, fields);
- }
- fields.add(normalizeName(name));
+ @Override
+ public void acceptSimpleResource(ResourceType type, String name) {
+ generator.addSimpleResource(type, name);
}
- public void writePublicValue(ResourceType type, String name, Optional<Integer> value) {
- SortedMap<String, Optional<Integer>> publicMappings = publicIds.get(type);
- if (publicMappings == null) {
- publicMappings = new TreeMap<>();
- publicIds.put(type, publicMappings);
- }
- Optional<Integer> oldValue = publicMappings.put(name, value);
- // AAPT should issue an error, but do a bit of sanity checking here just in case.
- if (oldValue != null && !oldValue.equals(value)) {
- // Enforce a consistent ordering on the warning message.
- Integer lower = oldValue.orNull();
- Integer higher = value.orNull();
- if (Ordering.natural().compare(oldValue.orNull(), value.orNull()) > 0) {
- lower = higher;
- higher = oldValue.orNull();
- }
- logger.warning(
- String.format(
- "resource %s/%s has conflicting public identifiers (0x%x vs 0x%x)",
- type, name, lower, higher));
- }
+ @Override
+ public void acceptPublicResource(ResourceType type, String name, Optional<Integer> value) {
+ generator.addPublicResource(type, name, value);
}
- public void writeStyleableResource(FullyQualifiedName key,
- Map<FullyQualifiedName, Boolean> attrs) {
- ResourceType type = ResourceType.STYLEABLE;
- // The configuration can play a role in sorting, but that isn't modeled yet.
- String normalizedStyleableName = normalizeName(key.name());
- writeSimpleResource(type, normalizedStyleableName);
- // We should have merged styleables, so there should only be one definition per configuration.
- // However, we don't combine across configurations, so there can be a pre-existing definition.
- Map<String, Boolean> normalizedAttrs = styleableAttrs.get(normalizedStyleableName);
- if (normalizedAttrs == null) {
- // We need to maintain the original order of the attrs.
- normalizedAttrs = new LinkedHashMap<>();
- styleableAttrs.put(normalizedStyleableName, normalizedAttrs);
- }
- for (Map.Entry<FullyQualifiedName, Boolean> attrEntry : attrs.entrySet()) {
- String normalizedAttrName = normalizeAttrName(attrEntry.getKey().name());
- normalizedAttrs.put(normalizedAttrName, attrEntry.getValue());
- }
+ @Override
+ public void acceptStyleableResource(
+ FullyQualifiedName key, Map<FullyQualifiedName, Boolean> attrs) {
+ generator.addStyleableResource(key, attrs);
}
@Override
public void flush() throws IOException {
- Map<ResourceType, List<FieldInitializer>> initializers = new EnumMap<>(ResourceType.class);
try {
- fillInitializers(initializers);
- } catch (AttrLookupException e) {
- throw new IOException(e);
- }
-
- if (includeClassFile) {
- writeAsClass(initializers);
- }
- if (includeJavaFile) {
- writeAsJava(initializers);
- }
- }
-
- /**
- * Determine the TT portion of the resource ID (PPTTEEEE) that aapt would have assigned. This not
- * at all alphabetical. It depends on the order in which the types are processed, and whether or
- * not previous types are present (compact). See the code in aapt Resource.cpp:buildResources().
- * There are several seemingly arbitrary and different processing orders in the function, but the
- * ordering is determined specifically by the portion at: <a
- * href="https://android.googlesource.com/platform/frameworks/base.git/+/marshmallow-release/tools/aapt/Resource.cpp#1254">
- * Resource.cpp:buildResources() </a>
- *
- * <p>where it does:
- *
- * <pre>
- * if (drawables != NULL) { ... }
- * if (mipmaps != NULL) { ... }
- * if (layouts != NULL) { ... }
- * </pre>
- *
- * Numbering starts at 1 instead of 0, and ResourceType.ATTR comes before the rest.
- * ResourceType.STYLEABLE doesn't actually need a resource ID, so that is skipped. We encode the
- * ordering in the following list.
- */
- private static final ImmutableList<ResourceType> AAPT_TYPE_ORDERING =
- ImmutableList.of(
- ResourceType.DRAWABLE,
- ResourceType.MIPMAP,
- ResourceType.LAYOUT,
- ResourceType.ANIM,
- ResourceType.ANIMATOR,
- ResourceType.TRANSITION,
- ResourceType.INTERPOLATOR,
- ResourceType.XML,
- ResourceType.RAW,
- // Begin VALUES portion
- // Technically, aapt just assigns according to declaration order in the source value.xml files
- // so it isn't really deterministic. However, the Gradle merger sorts the values.xml file
- // before invoking aapt, so assume that is also done.
- ResourceType.ARRAY,
- ResourceType.BOOL,
- ResourceType.COLOR,
- ResourceType.DIMEN,
- ResourceType.FRACTION,
- ResourceType.ID,
- ResourceType.INTEGER,
- ResourceType.PLURALS,
- ResourceType.STRING,
- ResourceType.STYLE,
- // End VALUES portion
- // Technically, file-based COLOR resources come next. If we care about complete equivalence
- // we should separate the file-based resources from value-based resources so that we can
- // number them the same way.
- ResourceType.MENU);
-
- private Map<ResourceType, Integer> chooseTypeIds() {
- // Go through public entries. Those may have forced certain type assignments, so take those
- // into account first.
- Map<ResourceType, Integer> allocatedTypeIds = assignTypeIdsForPublic();
- Set<Integer> reservedTypeSlots = ImmutableSet.copyOf(allocatedTypeIds.values());
- // ATTR always takes up slot #1, even if it isn't present.
- allocatedTypeIds.put(ResourceType.ATTR, ATTR_TYPE_ID);
- // The rest are packed after that.
- int nextTypeId = nextFreeId(ATTR_TYPE_ID + 1, reservedTypeSlots);
- for (ResourceType t : AAPT_TYPE_ORDERING) {
- if (innerClasses.containsKey(t) && !allocatedTypeIds.containsKey(t)) {
- allocatedTypeIds.put(t, nextTypeId);
- nextTypeId = nextFreeId(nextTypeId + 1, reservedTypeSlots);
- }
- }
- // Sanity check that everything has been assigned, except STYLEABLE. There shouldn't be
- // anything of type PUBLIC either (since that isn't a real resource).
- // We will need to update the list if there is a new resource type.
- for (ResourceType t : innerClasses.keySet()) {
- Preconditions.checkState(
- t == ResourceType.STYLEABLE || allocatedTypeIds.containsKey(t),
- "Resource type %s is not allocated a type ID",
- t);
- }
- return allocatedTypeIds;
- }
-
- private Map<ResourceType, Integer> assignTypeIdsForPublic() {
- Map<ResourceType, Integer> allocatedTypeIds = new EnumMap<>(ResourceType.class);
- if (publicIds.isEmpty()) {
- return allocatedTypeIds;
- }
- // Keep track of the reverse mapping from Int -> Type for validation.
- Map<Integer, ResourceType> assignedIds = new HashMap<>();
- for (Map.Entry<ResourceType, SortedMap<String, Optional<Integer>>> publicTypeEntry :
- publicIds.entrySet()) {
- ResourceType currentType = publicTypeEntry.getKey();
- Integer reservedTypeSlot = null;
- String previousResource = null;
- for (Map.Entry<String, Optional<Integer>> publicEntry :
- publicTypeEntry.getValue().entrySet()) {
- Optional<Integer> reservedId = publicEntry.getValue();
- if (!reservedId.isPresent()) {
- continue;
- }
- Integer typePortion = extractTypeId(reservedId.get());
- if (reservedTypeSlot == null) {
- reservedTypeSlot = typePortion;
- previousResource = publicEntry.getKey();
- } else {
- if (!reservedTypeSlot.equals(typePortion)) {
- logger.warning(
- String.format(
- "%s has conflicting type codes for its public identifiers (%s=%s vs %s=%s)",
- currentType.getName(),
- previousResource,
- reservedTypeSlot,
- publicEntry.getKey(),
- typePortion));
- }
- }
- }
- if (currentType == ResourceType.ATTR
- && reservedTypeSlot != null
- && !reservedTypeSlot.equals(ATTR_TYPE_ID)) {
- logger.warning(
- String.format(
- "Cannot force ATTR to have type code other than 0x%02x (got 0x%02x from %s)",
- ATTR_TYPE_ID, reservedTypeSlot, previousResource));
- }
- allocatedTypeIds.put(currentType, reservedTypeSlot);
- ResourceType alreadyAssigned = assignedIds.put(reservedTypeSlot, currentType);
- if (alreadyAssigned != null) {
- logger.warning(
- String.format(
- "Multiple type names declared for public type identifier 0x%x (%s vs %s)",
- reservedTypeSlot, alreadyAssigned, currentType));
- }
- }
- return allocatedTypeIds;
- }
-
- private Map<String, Integer> assignAttrIds(int attrTypeId) {
- // Attrs are special, since they can be defined within a declare-styleable. Those are sorted
- // after top-level definitions.
- if (!innerClasses.containsKey(ResourceType.ATTR)) {
- return ImmutableMap.of();
- }
- Map<String, Integer> attrToId = Maps.newHashMapWithExpectedSize(
- innerClasses.get(ResourceType.ATTR).size());
- // After assigning public IDs, we count up monotonically, so we don't need to track additional
- // assignedIds to avoid collisions (use an ImmutableSet to ensure we don't add more).
- Set<Integer> assignedIds = ImmutableSet.of();
- if (publicIds.containsKey(ResourceType.ATTR)) {
- assignedIds = assignPublicIds(attrToId, publicIds.get(ResourceType.ATTR), attrTypeId);
- }
- Set<String> inlineAttrs = new HashSet<>();
- Set<String> styleablesWithInlineAttrs = new TreeSet<>();
- for (Map.Entry<String, Map<String, Boolean>> styleableAttrEntry
- : styleableAttrs.entrySet()) {
- Map<String, Boolean> attrs = styleableAttrEntry.getValue();
- for (Map.Entry<String, Boolean> attrEntry : attrs.entrySet()) {
- if (attrEntry.getValue()) {
- inlineAttrs.add(attrEntry.getKey());
- styleablesWithInlineAttrs.add(styleableAttrEntry.getKey());
- }
- }
- }
- int nextId = nextFreeId(getInitialIdForTypeId(attrTypeId), assignedIds);
- // Technically, aapt assigns based on declaration order, but the merge should have sorted
- // the non-inline attributes, so assigning by sorted order is the same.
- ImmutableList<String> sortedAttrs = Ordering.natural()
- .immutableSortedCopy(innerClasses.get(ResourceType.ATTR));
- for (String attr : sortedAttrs) {
- if (!inlineAttrs.contains(attr) && !attrToId.containsKey(attr)) {
- attrToId.put(attr, nextId);
- nextId = nextFreeId(nextId + 1, assignedIds);
- }
- }
- for (String styleable : styleablesWithInlineAttrs) {
- Map<String, Boolean> attrs = styleableAttrs.get(styleable);
- for (Map.Entry<String, Boolean> attrEntry : attrs.entrySet()) {
- if (attrEntry.getValue() && !attrToId.containsKey(attrEntry.getKey())) {
- attrToId.put(attrEntry.getKey(), nextId);
- nextId = nextFreeId(nextId + 1, assignedIds);
- }
- }
- }
- return attrToId;
- }
-
- private void fillInitializers(Map<ResourceType, List<FieldInitializer>> initializers)
- throws AttrLookupException {
- Map<ResourceType, Integer> typeIdMap = chooseTypeIds();
- Map<String, Integer> attrAssignments = assignAttrIds(typeIdMap.get(ResourceType.ATTR));
- for (Map.Entry<ResourceType, Set<String>> fieldEntries : innerClasses.entrySet()) {
- ResourceType type = fieldEntries.getKey();
- ImmutableList<String> sortedFields = Ordering
- .natural()
- .immutableSortedCopy(fieldEntries.getValue());
- List<FieldInitializer> fields;
- if (type == ResourceType.STYLEABLE) {
- fields = getStyleableInitializers(attrAssignments, sortedFields);
- } else if (type == ResourceType.ATTR) {
- fields = getAttrInitializers(attrAssignments, sortedFields);
- } else {
- int typeId = typeIdMap.get(type);
- fields = getResourceInitializers(type, typeId, sortedFields);
+ Map<ResourceType, List<FieldInitializer>> initializers = generator.build();
+ if (includeClassFile) {
+ writeAsClass(initializers);
}
- // The maximum number of Java fields is 2^16.
- // See the JVM reference "4.11. Limitations of the Java Virtual Machine."
- Preconditions.checkArgument(fields.size() < (1 << 16));
- initializers.put(type, fields);
- }
- }
-
- private List<FieldInitializer> getStyleableInitializers(
- Map<String, Integer> attrAssignments,
- Collection<String> styleableFields)
- throws AttrLookupException {
- ImmutableList.Builder<FieldInitializer> initList = ImmutableList.builder();
- for (String field : styleableFields) {
- Set<String> attrs = styleableAttrs.get(field).keySet();
- ImmutableMap.Builder<String, Integer> arrayInitValues = ImmutableMap.builder();
- for (String attr : attrs) {
- Integer attrId = attrAssignments.get(attr);
- if (attrId == null) {
- // It should be a framework resource, otherwise we don't know about the resource.
- if (!attr.startsWith(NORMALIZED_ANDROID_PREFIX)) {
- throw new AttrLookupException("App attribute not found: " + attr);
- }
- String attrWithoutPrefix = attr.substring(NORMALIZED_ANDROID_PREFIX.length());
- attrId = androidIdProvider.getAttrId(attrWithoutPrefix);
- }
- arrayInitValues.put(attr, attrId);
- }
- // The styleable array should be sorted by ID value.
- // Make sure that if we have android: framework attributes, their IDs are listed first.
- ImmutableMap<String, Integer> arrayInitMap = arrayInitValues
- .orderEntriesByValue(Ordering.<Integer>natural())
- .build();
- initList.add(new IntArrayFieldInitializer(field, arrayInitMap.values()));
- int index = 0;
- for (String attr : arrayInitMap.keySet()) {
- initList.add(new IntFieldInitializer(field + "_" + attr, index));
- ++index;
+ if (includeJavaFile) {
+ writeAsJava(initializers);
}
+ } catch (AttrLookupException e) {
+ throw new IOException(e);
}
- return initList.build();
- }
-
- private List<FieldInitializer> getAttrInitializers(
- Map<String, Integer> attrAssignments, Collection<String> sortedFields) {
- ImmutableList.Builder<FieldInitializer> initList = ImmutableList.builder();
- for (String field : sortedFields) {
- int attrId = attrAssignments.get(field);
- initList.add(new IntFieldInitializer(field, attrId));
- }
- return initList.build();
- }
- private List<FieldInitializer> getResourceInitializers(
- ResourceType type, int typeId, Collection<String> sortedFields) {
- ImmutableList.Builder<FieldInitializer> initList = ImmutableList.builder();
- Map<String, Integer> publicNameToId = new HashMap<>();
- Set<Integer> assignedIds = ImmutableSet.of();
- if (publicIds.containsKey(type)) {
- assignedIds = assignPublicIds(publicNameToId, publicIds.get(type), typeId);
- }
- int resourceIds = nextFreeId(getInitialIdForTypeId(typeId), assignedIds);
- for (String field : sortedFields) {
- Integer fieldValue = publicNameToId.get(field);
- if (fieldValue == null) {
- fieldValue = resourceIds;
- resourceIds = nextFreeId(resourceIds + 1, assignedIds);
- }
- initList.add(new IntFieldInitializer(field, fieldValue));
- }
- return initList.build();
}
private void writeAsJava(Map<ResourceType, List<FieldInitializer>> initializers)
@@ -459,8 +121,7 @@ public class AndroidResourceClassWriter implements Flushable {
writer.write(" */\n");
writer.write(String.format("package %s;\n", packageName));
writer.write("public final class R {\n");
- for (Map.Entry<ResourceType, Set<String>> fieldEntries : innerClasses.entrySet()) {
- ResourceType type = fieldEntries.getKey();
+ for (ResourceType type : initializers.keySet()) {
writer.write(String.format(" public static final class %s {\n", type.getName()));
for (FieldInitializer field : initializers.get(type)) {
field.writeInitSource(writer);
@@ -477,63 +138,4 @@ public class AndroidResourceClassWriter implements Flushable {
new RClassGenerator(outputBasePath, initializers, false /* finalFields */);
rClassGenerator.write(packageName);
}
-
- private static String normalizeName(String resourceName) {
- return resourceName.replace('.', '_');
- }
-
- private static String normalizeAttrName(String attrName) {
- // In addition to ".", attributes can have ":", e.g., for "android:textColor".
- return normalizeName(attrName).replace(':', '_');
- }
-
- /**
- * Assign any public ids to the given idBuilder.
- *
- * @param nameToId where to store the final name -> id mappings
- * @param publicIds known public resources (can contain null values, if ID isn't reserved)
- * @param typeId the type slot for the current resource type.
- * @return the final set of assigned resource ids (includes those without apriori assignments).
- */
- private static Set<Integer> assignPublicIds(
- Map<String, Integer> nameToId,
- SortedMap<String, Optional<Integer>> publicIds,
- int typeId) {
- HashMap<Integer, String> assignedIds = new HashMap<>();
- int prevId = getInitialIdForTypeId(typeId);
- for (Map.Entry<String, Optional<Integer>> entry : publicIds.entrySet()) {
- Optional<Integer> id = entry.getValue();
- if (id.isPresent()) {
- prevId = id.get();
- } else {
- prevId = nextFreeId(prevId + 1, assignedIds.keySet());
- }
- String previousMapping = assignedIds.put(prevId, entry.getKey());
- if (previousMapping != null) {
- logger.warning(
- String.format(
- "Multiple entry names declared for public entry identifier 0x%x (%s and %s)",
- prevId, previousMapping, entry.getKey()));
- }
- nameToId.put(entry.getKey(), prevId);
- }
- return assignedIds.keySet();
- }
-
- private static int extractTypeId(int fullID) {
- return (fullID & 0x00FF0000) >> 16;
- }
-
- private static int getInitialIdForTypeId(int typeId) {
- return APP_PACKAGE_MASK | (typeId << 16);
- }
-
- private static int nextFreeId(int nextSlot, Set<Integer> reservedSlots) {
- // Linear search for the next free slot. This assumes that reserved <public> ids are rare.
- // Otherwise we should use a NavigableSet or some other smarter data-structure.
- while (reservedSlots.contains(nextSlot)) {
- ++nextSlot;
- }
- return nextSlot;
- }
}
diff --git a/src/tools/android/java/com/google/devtools/build/android/AndroidResourceSymbolSink.java b/src/tools/android/java/com/google/devtools/build/android/AndroidResourceSymbolSink.java
new file mode 100644
index 0000000000..cd7276c2fa
--- /dev/null
+++ b/src/tools/android/java/com/google/devtools/build/android/AndroidResourceSymbolSink.java
@@ -0,0 +1,28 @@
+// Copyright 2017 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.android.resources.ResourceType;
+import com.google.common.base.Optional;
+import java.util.Map;
+
+/** Defines a sink for collecting data about resource symbols. */
+public interface AndroidResourceSymbolSink {
+
+ void acceptSimpleResource(ResourceType type, String name);
+
+ void acceptPublicResource(ResourceType type, String name, Optional<Integer> value);
+
+ void acceptStyleableResource(FullyQualifiedName key, Map<FullyQualifiedName, Boolean> attrs);
+}
diff --git a/src/tools/android/java/com/google/devtools/build/android/DataResource.java b/src/tools/android/java/com/google/devtools/build/android/DataResource.java
index 578c4d34ad..1295f60192 100644
--- a/src/tools/android/java/com/google/devtools/build/android/DataResource.java
+++ b/src/tools/android/java/com/google/devtools/build/android/DataResource.java
@@ -20,7 +20,7 @@ import com.google.devtools.build.android.AndroidResourceMerger.MergingException;
*/
public interface DataResource extends DataValue {
/** Write as a resource using the supplied {@link AndroidDataWritingVisitor}. */
- void writeResource(FullyQualifiedName key, AndroidDataWritingVisitor mergedDataWriter)
+ void writeResource(FullyQualifiedName key, AndroidDataWritingVisitor writer)
throws MergingException;
/**
@@ -32,12 +32,8 @@ public interface DataResource extends DataValue {
*/
DataResource combineWith(DataResource resource);
- /**
- * Queue up writing the resource to the given {@link AndroidResourceClassWriter}.
- */
- void writeResourceToClass(
- FullyQualifiedName key,
- AndroidResourceClassWriter resourceClassWriter);
+ /** Queue up writing the resource to the given {@link AndroidResourceSymbolSink}. */
+ void writeResourceToClass(FullyQualifiedName key, AndroidResourceSymbolSink sink);
/** Overwrite another {@link DataResource}. */
DataResource overwrite(DataResource other);
diff --git a/src/tools/android/java/com/google/devtools/build/android/DataResourceXml.java b/src/tools/android/java/com/google/devtools/build/android/DataResourceXml.java
index d2100dc4eb..0dcc77ed3d 100644
--- a/src/tools/android/java/com/google/devtools/build/android/DataResourceXml.java
+++ b/src/tools/android/java/com/google/devtools/build/android/DataResourceXml.java
@@ -329,10 +329,8 @@ public class DataResourceXml implements DataResource {
}
@Override
- public void writeResourceToClass(
- FullyQualifiedName key,
- AndroidResourceClassWriter resourceClassWriter) {
- xml.writeResourceToClass(key, resourceClassWriter);
+ public void writeResourceToClass(FullyQualifiedName key, AndroidResourceSymbolSink sink) {
+ xml.writeResourceToClass(key, sink);
}
@Override
diff --git a/src/tools/android/java/com/google/devtools/build/android/DataValueFile.java b/src/tools/android/java/com/google/devtools/build/android/DataValueFile.java
index 11ace2bd6d..7740e977c0 100644
--- a/src/tools/android/java/com/google/devtools/build/android/DataValueFile.java
+++ b/src/tools/android/java/com/google/devtools/build/android/DataValueFile.java
@@ -116,9 +116,8 @@ public class DataValueFile implements DataResource, DataAsset {
}
@Override
- public void writeResourceToClass(FullyQualifiedName key,
- AndroidResourceClassWriter resourceClassWriter) {
- resourceClassWriter.writeSimpleResource(key.type(), key.name());
+ public void writeResourceToClass(FullyQualifiedName key, AndroidResourceSymbolSink sink) {
+ sink.acceptSimpleResource(key.type(), key.name());
}
@Override
diff --git a/src/tools/android/java/com/google/devtools/build/android/ParsedAndroidData.java b/src/tools/android/java/com/google/devtools/build/android/ParsedAndroidData.java
index 235e0c2fc5..36eeabe9bd 100644
--- a/src/tools/android/java/com/google/devtools/build/android/ParsedAndroidData.java
+++ b/src/tools/android/java/com/google/devtools/build/android/ParsedAndroidData.java
@@ -88,8 +88,7 @@ public class ParsedAndroidData {
private void checkForErrors() throws MergingException {
if (!errors.isEmpty()) {
MergingException mergingException =
- MergingException
- .withMessage(String.format("%s Parse Error(s)", errors.size()));
+ MergingException.withMessage(String.format("%s Parse Error(s)", errors.size()));
for (Exception e : errors) {
mergingException.addSuppressed(e);
}
@@ -500,7 +499,7 @@ public class ParsedAndroidData {
return overwritingResources.get(name);
}
- void writeResourcesTo(AndroidResourceClassWriter writer) {
+ void writeResourcesTo(AndroidResourceSymbolSink writer) {
for (Entry<DataKey, DataResource> resource : iterateDataResourceEntries()) {
resource.getValue().writeResourceToClass((FullyQualifiedName) resource.getKey(), writer);
}
diff --git a/src/tools/android/java/com/google/devtools/build/android/PlaceholderIdFieldInitializerBuilder.java b/src/tools/android/java/com/google/devtools/build/android/PlaceholderIdFieldInitializerBuilder.java
new file mode 100644
index 0000000000..7f8fff613f
--- /dev/null
+++ b/src/tools/android/java/com/google/devtools/build/android/PlaceholderIdFieldInitializerBuilder.java
@@ -0,0 +1,442 @@
+// Copyright 2017 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.android.resources.ResourceType;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Ordering;
+import com.google.devtools.build.android.AndroidFrameworkAttrIdProvider.AttrLookupException;
+import com.google.devtools.build.android.resources.FieldInitializer;
+import com.google.devtools.build.android.resources.IntArrayFieldInitializer;
+import com.google.devtools.build.android.resources.IntFieldInitializer;
+import java.util.Collection;
+import java.util.EnumMap;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.logging.Logger;
+
+/**
+ * Generates {@link FieldInitializer}s placeholder unique ids. The real ids will be assigned when
+ * building the android_binary.
+ */
+class PlaceholderIdFieldInitializerBuilder {
+ /**
+ * Determine the TT portion of the resource ID (PPTTEEEE) that aapt would have assigned. This not
+ * at all alphabetical. It depends on the order in which the types are processed, and whether or
+ * not previous types are present (compact). See the code in aapt Resource.cpp:buildResources().
+ * There are several seemingly arbitrary and different processing orders in the function, but the
+ * ordering is determined specifically by the portion at: <a
+ * href="https://android.googlesource.com/platform/frameworks/base.git/+/marshmallow-release/tools/aapt/Resource.cpp#1254">
+ * Resource.cpp:buildResources() </a>
+ *
+ * <p>where it does:
+ *
+ * <pre>
+ * if (drawables != NULL) { ... }
+ * if (mipmaps != NULL) { ... }
+ * if (layouts != NULL) { ... }
+ * </pre>
+ *
+ * Numbering starts at 1 instead of 0, and ResourceType.ATTR comes before the rest.
+ * ResourceType.STYLEABLE doesn't actually need a resource ID, so that is skipped. We encode the
+ * ordering in the following list.
+ */
+ private static final ImmutableList<ResourceType> AAPT_TYPE_ORDERING =
+ ImmutableList.of(
+ ResourceType.DRAWABLE,
+ ResourceType.MIPMAP,
+ ResourceType.LAYOUT,
+ ResourceType.ANIM,
+ ResourceType.ANIMATOR,
+ ResourceType.TRANSITION,
+ ResourceType.INTERPOLATOR,
+ ResourceType.XML,
+ ResourceType.RAW,
+ // Begin VALUES portion
+ // Technically, aapt just assigns according to declaration order in the source value.xml
+ // files so it isn't really deterministic. However, the Gradle merger sorts the values.xml
+ // file before invoking aapt, so assume that is also done.
+ ResourceType.ARRAY,
+ ResourceType.BOOL,
+ ResourceType.COLOR,
+ ResourceType.DIMEN,
+ ResourceType.FRACTION,
+ ResourceType.ID,
+ ResourceType.INTEGER,
+ ResourceType.PLURALS,
+ ResourceType.STRING,
+ ResourceType.STYLE,
+ // End VALUES portion
+ // Technically, file-based COLOR resources come next. If we care about complete
+ // equivalence we should separate the file-based resources from value-based resources so
+ // that we can number them the same way.
+ ResourceType.MENU);
+
+ private static final int APP_PACKAGE_MASK = 0x7f000000;
+ private static final int ATTR_TYPE_ID = 1;
+ private static final Logger logger =
+ Logger.getLogger(PlaceholderIdFieldInitializerBuilder.class.getName());
+ private static final String NORMALIZED_ANDROID_PREFIX = "android_";
+
+ /**
+ * Assign any public ids to the given idBuilder.
+ *
+ * @param nameToId where to store the final name -> id mappings
+ * @param publicIds known public resources (can contain null values, if ID isn't reserved)
+ * @param typeId the type slot for the current resource type.
+ * @return the final set of assigned resource ids (includes those without apriori assignments).
+ */
+ private static Set<Integer> assignPublicIds(
+ Map<String, Integer> nameToId, SortedMap<String, Optional<Integer>> publicIds, int typeId) {
+ HashMap<Integer, String> assignedIds = new HashMap<>();
+ int prevId = getInitialIdForTypeId(typeId);
+ for (Map.Entry<String, Optional<Integer>> entry : publicIds.entrySet()) {
+ Optional<Integer> id = entry.getValue();
+ if (id.isPresent()) {
+ prevId = id.get();
+ } else {
+ prevId = nextFreeId(prevId + 1, assignedIds.keySet());
+ }
+ String previousMapping = assignedIds.put(prevId, entry.getKey());
+ if (previousMapping != null) {
+ logger.warning(
+ String.format(
+ "Multiple entry names declared for public entry identifier 0x%x (%s and %s)",
+ prevId, previousMapping, entry.getKey()));
+ }
+ nameToId.put(entry.getKey(), prevId);
+ }
+ return assignedIds.keySet();
+ }
+
+ private static int extractTypeId(int fullID) {
+ return (fullID & 0x00FF0000) >> 16;
+ }
+
+ private static int getInitialIdForTypeId(int typeId) {
+ return APP_PACKAGE_MASK | (typeId << 16);
+ }
+
+ private static int nextFreeId(int nextSlot, Set<Integer> reservedSlots) {
+ // Linear search for the next free slot. This assumes that reserved <public> ids are rare.
+ // Otherwise we should use a NavigableSet or some other smarter data-structure.
+ while (reservedSlots.contains(nextSlot)) {
+ ++nextSlot;
+ }
+ return nextSlot;
+ }
+
+ private static String normalizeAttrName(String attrName) {
+ // In addition to ".", attributes can have ":", e.g., for "android:textColor".
+ return normalizeName(attrName).replace(':', '_');
+ }
+
+ private static String normalizeName(String resourceName) {
+ return resourceName.replace('.', '_');
+ }
+
+ private final AndroidFrameworkAttrIdProvider androidIdProvider;
+
+ private final Map<ResourceType, Set<String>> innerClasses = new EnumMap<>(ResourceType.class);
+
+ private final Map<ResourceType, SortedMap<String, Optional<Integer>>> publicIds =
+ new EnumMap<>(ResourceType.class);
+
+ private final Map<String, Map<String, Boolean>> styleableAttrs = new HashMap<>();
+
+ public PlaceholderIdFieldInitializerBuilder(AndroidFrameworkAttrIdProvider androidIdProvider) {
+ this.androidIdProvider = androidIdProvider;
+ }
+
+ public void addPublicResource(ResourceType type, String name, Optional<Integer> value) {
+ SortedMap<String, Optional<Integer>> publicMappings = publicIds.get(type);
+ if (publicMappings == null) {
+ publicMappings = new TreeMap<>();
+ publicIds.put(type, publicMappings);
+ }
+ Optional<Integer> oldValue = publicMappings.put(name, value);
+ // AAPT should issue an error, but do a bit of sanity checking here just in case.
+ if (oldValue != null && !oldValue.equals(value)) {
+ // Enforce a consistent ordering on the warning message.
+ Integer lower = oldValue.orNull();
+ Integer higher = value.orNull();
+ if (Ordering.natural().compare(oldValue.orNull(), value.orNull()) > 0) {
+ lower = higher;
+ higher = oldValue.orNull();
+ }
+ logger.warning(
+ String.format(
+ "resource %s/%s has conflicting public identifiers (0x%x vs 0x%x)",
+ type, name, lower, higher));
+ }
+ }
+
+ public void addSimpleResource(ResourceType type, String name) {
+ Set<String> fields = innerClasses.get(type);
+ if (fields == null) {
+ fields = new HashSet<>();
+ innerClasses.put(type, fields);
+ }
+ fields.add(normalizeName(name));
+ }
+
+ public void addStyleableResource(FullyQualifiedName key, Map<FullyQualifiedName, Boolean> attrs) {
+ ResourceType type = ResourceType.STYLEABLE;
+ // The configuration can play a role in sorting, but that isn't modeled yet.
+ String normalizedStyleableName = normalizeName(key.name());
+ addSimpleResource(type, normalizedStyleableName);
+ // We should have merged styleables, so there should only be one definition per configuration.
+ // However, we don't combine across configurations, so there can be a pre-existing definition.
+ Map<String, Boolean> normalizedAttrs = styleableAttrs.get(normalizedStyleableName);
+ if (normalizedAttrs == null) {
+ // We need to maintain the original order of the attrs.
+ normalizedAttrs = new LinkedHashMap<>();
+ styleableAttrs.put(normalizedStyleableName, normalizedAttrs);
+ }
+ for (Map.Entry<FullyQualifiedName, Boolean> attrEntry : attrs.entrySet()) {
+ String normalizedAttrName = normalizeAttrName(attrEntry.getKey().name());
+ normalizedAttrs.put(normalizedAttrName, attrEntry.getValue());
+ }
+ }
+
+ private Map<String, Integer> assignAttrIds(int attrTypeId) {
+ // Attrs are special, since they can be defined within a declare-styleable. Those are sorted
+ // after top-level definitions.
+ if (!innerClasses.containsKey(ResourceType.ATTR)) {
+ return ImmutableMap.of();
+ }
+ Map<String, Integer> attrToId =
+ Maps.newHashMapWithExpectedSize(innerClasses.get(ResourceType.ATTR).size());
+ // After assigning public IDs, we count up monotonically, so we don't need to track additional
+ // assignedIds to avoid collisions (use an ImmutableSet to ensure we don't add more).
+ Set<Integer> assignedIds = ImmutableSet.of();
+ if (publicIds.containsKey(ResourceType.ATTR)) {
+ assignedIds = assignPublicIds(attrToId, publicIds.get(ResourceType.ATTR), attrTypeId);
+ }
+ Set<String> inlineAttrs = new HashSet<>();
+ Set<String> styleablesWithInlineAttrs = new TreeSet<>();
+ for (Map.Entry<String, Map<String, Boolean>> styleableAttrEntry : styleableAttrs.entrySet()) {
+ Map<String, Boolean> attrs = styleableAttrEntry.getValue();
+ for (Map.Entry<String, Boolean> attrEntry : attrs.entrySet()) {
+ if (attrEntry.getValue()) {
+ inlineAttrs.add(attrEntry.getKey());
+ styleablesWithInlineAttrs.add(styleableAttrEntry.getKey());
+ }
+ }
+ }
+ int nextId = nextFreeId(getInitialIdForTypeId(attrTypeId), assignedIds);
+ // Technically, aapt assigns based on declaration order, but the merge should have sorted
+ // the non-inline attributes, so assigning by sorted order is the same.
+ ImmutableList<String> sortedAttrs =
+ Ordering.natural().immutableSortedCopy(innerClasses.get(ResourceType.ATTR));
+ for (String attr : sortedAttrs) {
+ if (!inlineAttrs.contains(attr) && !attrToId.containsKey(attr)) {
+ attrToId.put(attr, nextId);
+ nextId = nextFreeId(nextId + 1, assignedIds);
+ }
+ }
+ for (String styleable : styleablesWithInlineAttrs) {
+ Map<String, Boolean> attrs = styleableAttrs.get(styleable);
+ for (Map.Entry<String, Boolean> attrEntry : attrs.entrySet()) {
+ if (attrEntry.getValue() && !attrToId.containsKey(attrEntry.getKey())) {
+ attrToId.put(attrEntry.getKey(), nextId);
+ nextId = nextFreeId(nextId + 1, assignedIds);
+ }
+ }
+ }
+ return attrToId;
+ }
+
+ private Map<ResourceType, Integer> assignTypeIdsForPublic() {
+ Map<ResourceType, Integer> allocatedTypeIds = new EnumMap<>(ResourceType.class);
+ if (publicIds.isEmpty()) {
+ return allocatedTypeIds;
+ }
+ // Keep track of the reverse mapping from Int -> Type for validation.
+ Map<Integer, ResourceType> assignedIds = new HashMap<>();
+ for (Map.Entry<ResourceType, SortedMap<String, Optional<Integer>>> publicTypeEntry :
+ publicIds.entrySet()) {
+ ResourceType currentType = publicTypeEntry.getKey();
+ Integer reservedTypeSlot = null;
+ String previousResource = null;
+ for (Map.Entry<String, Optional<Integer>> publicEntry :
+ publicTypeEntry.getValue().entrySet()) {
+ Optional<Integer> reservedId = publicEntry.getValue();
+ if (!reservedId.isPresent()) {
+ continue;
+ }
+ Integer typePortion = extractTypeId(reservedId.get());
+ if (reservedTypeSlot == null) {
+ reservedTypeSlot = typePortion;
+ previousResource = publicEntry.getKey();
+ } else {
+ if (!reservedTypeSlot.equals(typePortion)) {
+ logger.warning(
+ String.format(
+ "%s has conflicting type codes for its public identifiers (%s=%s vs %s=%s)",
+ currentType.getName(),
+ previousResource,
+ reservedTypeSlot,
+ publicEntry.getKey(),
+ typePortion));
+ }
+ }
+ }
+ if (currentType == ResourceType.ATTR
+ && reservedTypeSlot != null
+ && !reservedTypeSlot.equals(ATTR_TYPE_ID)) {
+ logger.warning(
+ String.format(
+ "Cannot force ATTR to have type code other than 0x%02x (got 0x%02x from %s)",
+ ATTR_TYPE_ID, reservedTypeSlot, previousResource));
+ }
+ allocatedTypeIds.put(currentType, reservedTypeSlot);
+ ResourceType alreadyAssigned = assignedIds.put(reservedTypeSlot, currentType);
+ if (alreadyAssigned != null) {
+ logger.warning(
+ String.format(
+ "Multiple type names declared for public type identifier 0x%x (%s vs %s)",
+ reservedTypeSlot, alreadyAssigned, currentType));
+ }
+ }
+ return allocatedTypeIds;
+ }
+
+ public Map<ResourceType, List<FieldInitializer>> build() throws AttrLookupException {
+ Map<ResourceType, List<FieldInitializer>> initializers = new EnumMap<>(ResourceType.class);
+ Map<ResourceType, Integer> typeIdMap = chooseTypeIds();
+ Map<String, Integer> attrAssignments = assignAttrIds(typeIdMap.get(ResourceType.ATTR));
+ for (Map.Entry<ResourceType, Set<String>> fieldEntries : innerClasses.entrySet()) {
+ ResourceType type = fieldEntries.getKey();
+ ImmutableList<String> sortedFields =
+ Ordering.natural().immutableSortedCopy(fieldEntries.getValue());
+ List<FieldInitializer> fields;
+ if (type == ResourceType.STYLEABLE) {
+ fields = getStyleableInitializers(attrAssignments, sortedFields);
+ } else if (type == ResourceType.ATTR) {
+ fields = getAttrInitializers(attrAssignments, sortedFields);
+ } else {
+ int typeId = typeIdMap.get(type);
+ fields = getResourceInitializers(type, typeId, sortedFields);
+ }
+ // The maximum number of Java fields is 2^16.
+ // See the JVM reference "4.11. Limitations of the Java Virtual Machine."
+ Preconditions.checkArgument(fields.size() < (1 << 16));
+ initializers.put(type, fields);
+ }
+ return initializers;
+ }
+
+ private Map<ResourceType, Integer> chooseTypeIds() {
+ // Go through public entries. Those may have forced certain type assignments, so take those
+ // into account first.
+ Map<ResourceType, Integer> allocatedTypeIds = assignTypeIdsForPublic();
+ Set<Integer> reservedTypeSlots = ImmutableSet.copyOf(allocatedTypeIds.values());
+ // ATTR always takes up slot #1, even if it isn't present.
+ allocatedTypeIds.put(ResourceType.ATTR, ATTR_TYPE_ID);
+ // The rest are packed after that.
+ int nextTypeId = nextFreeId(ATTR_TYPE_ID + 1, reservedTypeSlots);
+ for (ResourceType t : AAPT_TYPE_ORDERING) {
+ if (innerClasses.containsKey(t) && !allocatedTypeIds.containsKey(t)) {
+ allocatedTypeIds.put(t, nextTypeId);
+ nextTypeId = nextFreeId(nextTypeId + 1, reservedTypeSlots);
+ }
+ }
+ // Sanity check that everything has been assigned, except STYLEABLE. There shouldn't be
+ // anything of type PUBLIC either (since that isn't a real resource).
+ // We will need to update the list if there is a new resource type.
+ for (ResourceType t : innerClasses.keySet()) {
+ Preconditions.checkState(
+ t == ResourceType.STYLEABLE || allocatedTypeIds.containsKey(t),
+ "Resource type %s is not allocated a type ID",
+ t);
+ }
+ return allocatedTypeIds;
+ }
+
+ private List<FieldInitializer> getAttrInitializers(
+ Map<String, Integer> attrAssignments, Collection<String> sortedFields) {
+ ImmutableList.Builder<FieldInitializer> initList = ImmutableList.builder();
+ for (String field : sortedFields) {
+ int attrId = attrAssignments.get(field);
+ initList.add(new IntFieldInitializer(field, attrId));
+ }
+ return initList.build();
+ }
+
+ private List<FieldInitializer> getResourceInitializers(
+ ResourceType type, int typeId, Collection<String> sortedFields) {
+ ImmutableList.Builder<FieldInitializer> initList = ImmutableList.builder();
+ Map<String, Integer> publicNameToId = new HashMap<>();
+ Set<Integer> assignedIds = ImmutableSet.of();
+ if (publicIds.containsKey(type)) {
+ assignedIds = assignPublicIds(publicNameToId, publicIds.get(type), typeId);
+ }
+ int resourceIds = nextFreeId(getInitialIdForTypeId(typeId), assignedIds);
+ for (String field : sortedFields) {
+ Integer fieldValue = publicNameToId.get(field);
+ if (fieldValue == null) {
+ fieldValue = resourceIds;
+ resourceIds = nextFreeId(resourceIds + 1, assignedIds);
+ }
+ initList.add(new IntFieldInitializer(field, fieldValue));
+ }
+ return initList.build();
+ }
+
+ private List<FieldInitializer> getStyleableInitializers(
+ Map<String, Integer> attrAssignments, Collection<String> styleableFields)
+ throws AttrLookupException {
+ ImmutableList.Builder<FieldInitializer> initList = ImmutableList.builder();
+ for (String field : styleableFields) {
+ Set<String> attrs = styleableAttrs.get(field).keySet();
+ ImmutableMap.Builder<String, Integer> arrayInitValues = ImmutableMap.builder();
+ for (String attr : attrs) {
+ Integer attrId = attrAssignments.get(attr);
+ if (attrId == null) {
+ // It should be a framework resource, otherwise we don't know about the resource.
+ if (!attr.startsWith(NORMALIZED_ANDROID_PREFIX)) {
+ throw new AttrLookupException("App attribute not found: " + attr);
+ }
+ String attrWithoutPrefix = attr.substring(NORMALIZED_ANDROID_PREFIX.length());
+ attrId = androidIdProvider.getAttrId(attrWithoutPrefix);
+ }
+ arrayInitValues.put(attr, attrId);
+ }
+ // The styleable array should be sorted by ID value.
+ // Make sure that if we have android: framework attributes, their IDs are listed first.
+ ImmutableMap<String, Integer> arrayInitMap =
+ arrayInitValues.orderEntriesByValue(Ordering.<Integer>natural()).build();
+ initList.add(new IntArrayFieldInitializer(field, arrayInitMap.values()));
+ int index = 0;
+ for (String attr : arrayInitMap.keySet()) {
+ initList.add(new IntFieldInitializer(field + "_" + attr, index));
+ ++index;
+ }
+ }
+ return initList.build();
+ }
+}
diff --git a/src/tools/android/java/com/google/devtools/build/android/XmlResourceValue.java b/src/tools/android/java/com/google/devtools/build/android/XmlResourceValue.java
index bcbf99acca..0c0edd92a5 100644
--- a/src/tools/android/java/com/google/devtools/build/android/XmlResourceValue.java
+++ b/src/tools/android/java/com/google/devtools/build/android/XmlResourceValue.java
@@ -43,16 +43,13 @@ public interface XmlResourceValue {
XmlResourceValue combineWith(XmlResourceValue value);
/**
- * Queue up writing the resource to the given {@link AndroidResourceClassWriter}.
- * Each resource can generate one or more (in the case of styleable) fields and inner classes
- * in the R class.
+ * Queue up writing the resource to the given {@link AndroidResourceClassWriter}. Each resource
+ * can generate one or more (in the case of styleable) fields and inner classes in the R class.
*
* @param key The FullyQualifiedName of the resource
- * @param resourceClassWriter the R java class writer
+ * @param sink the symbol sink for producing source and classes
*/
- void writeResourceToClass(
- FullyQualifiedName key,
- AndroidResourceClassWriter resourceClassWriter);
+ void writeResourceToClass(FullyQualifiedName key, AndroidResourceSymbolSink sink);
/** Returns a representation of the xml value as a string suitable for conflict messages. */
String asConflictStringWith(DataSource source);
diff --git a/src/tools/android/java/com/google/devtools/build/android/resources/ResourceSymbols.java b/src/tools/android/java/com/google/devtools/build/android/resources/ResourceSymbols.java
index 19776d9ef0..03122884a9 100644
--- a/src/tools/android/java/com/google/devtools/build/android/resources/ResourceSymbols.java
+++ b/src/tools/android/java/com/google/devtools/build/android/resources/ResourceSymbols.java
@@ -211,7 +211,7 @@ public class ResourceSymbols {
for (ResourceSymbols packageSymbol : packageSymbols) {
symbols.putAll(packageSymbol.asTable());
}
-
+
Path packageOut = sourceOut.resolve(packageName.replace('.', File.separatorChar));
Files.createDirectories(packageOut);
diff --git a/src/tools/android/java/com/google/devtools/build/android/xml/ArrayXmlResourceValue.java b/src/tools/android/java/com/google/devtools/build/android/xml/ArrayXmlResourceValue.java
index 721cd29715..e3f5571c7d 100644
--- a/src/tools/android/java/com/google/devtools/build/android/xml/ArrayXmlResourceValue.java
+++ b/src/tools/android/java/com/google/devtools/build/android/xml/ArrayXmlResourceValue.java
@@ -19,7 +19,7 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.devtools.build.android.AndroidDataWritingVisitor;
import com.google.devtools.build.android.AndroidDataWritingVisitor.ValuesResourceDefinition;
-import com.google.devtools.build.android.AndroidResourceClassWriter;
+import com.google.devtools.build.android.AndroidResourceSymbolSink;
import com.google.devtools.build.android.DataSource;
import com.google.devtools.build.android.FullyQualifiedName;
import com.google.devtools.build.android.XmlResourceValue;
@@ -66,7 +66,7 @@ public class ArrayXmlResourceValue implements XmlResourceValue {
ARRAY(TAG_ARRAY),
STRING_ARRAY(TAG_STRING_ARRAY);
- public QName tagName;
+ public final QName tagName;
ArrayType(QName tagName) {
this.tagName = tagName;
@@ -183,9 +183,8 @@ public class ArrayXmlResourceValue implements XmlResourceValue {
}
@Override
- public void writeResourceToClass(FullyQualifiedName key,
- AndroidResourceClassWriter resourceClassWriter) {
- resourceClassWriter.writeSimpleResource(key.type(), key.name());
+ public void writeResourceToClass(FullyQualifiedName key, AndroidResourceSymbolSink sink) {
+ sink.acceptSimpleResource(key.type(), key.name());
}
public static XmlResourceValue parseArray(
diff --git a/src/tools/android/java/com/google/devtools/build/android/xml/AttrXmlResourceValue.java b/src/tools/android/java/com/google/devtools/build/android/xml/AttrXmlResourceValue.java
index 252b4cbac6..6be244b115 100644
--- a/src/tools/android/java/com/google/devtools/build/android/xml/AttrXmlResourceValue.java
+++ b/src/tools/android/java/com/google/devtools/build/android/xml/AttrXmlResourceValue.java
@@ -28,7 +28,7 @@ import com.google.common.collect.Ordering;
import com.google.devtools.build.android.AndroidDataWritingVisitor;
import com.google.devtools.build.android.AndroidDataWritingVisitor.StartTag;
import com.google.devtools.build.android.AndroidDataWritingVisitor.ValuesResourceDefinition;
-import com.google.devtools.build.android.AndroidResourceClassWriter;
+import com.google.devtools.build.android.AndroidResourceSymbolSink;
import com.google.devtools.build.android.DataSource;
import com.google.devtools.build.android.FullyQualifiedName;
import com.google.devtools.build.android.XmlResourceValue;
@@ -330,13 +330,12 @@ public class AttrXmlResourceValue implements XmlResourceValue {
}
@Override
- public void writeResourceToClass(FullyQualifiedName key,
- AndroidResourceClassWriter resourceClassWriter) {
- resourceClassWriter.writeSimpleResource(key.type(), key.name());
+ public void writeResourceToClass(FullyQualifiedName key, AndroidResourceSymbolSink sink) {
+ sink.acceptSimpleResource(key.type(), key.name());
// Flags and enums generate ID fields.
if (formats.keySet().contains(FLAGS) || formats.keySet().contains(ENUM)) {
for (ResourceXmlAttrValue value : formats.values()) {
- value.writeToClass(resourceClassWriter);
+ value.writeToClass(sink);
}
}
}
@@ -372,7 +371,7 @@ public class AttrXmlResourceValue implements XmlResourceValue {
SerializeFormat.DataValueXml appendTo(SerializeFormat.DataValueXml.Builder builder);
- void writeToClass(AndroidResourceClassWriter writer);
+ void writeToClass(AndroidResourceSymbolSink writer);
}
// TODO(corysmith): The ResourceXmlAttrValue implementors, other than enum and flag, share a
@@ -443,9 +442,9 @@ public class AttrXmlResourceValue implements XmlResourceValue {
}
@Override
- public void writeToClass(AndroidResourceClassWriter writer) {
+ public void writeToClass(AndroidResourceSymbolSink writer) {
for (Map.Entry<String, String> entry : values.entrySet()) {
- writer.writeSimpleResource(ResourceType.ID, entry.getKey());
+ writer.acceptSimpleResource(ResourceType.ID, entry.getKey());
}
}
}
@@ -516,9 +515,9 @@ public class AttrXmlResourceValue implements XmlResourceValue {
}
@Override
- public void writeToClass(AndroidResourceClassWriter writer) {
+ public void writeToClass(AndroidResourceSymbolSink writer) {
for (Map.Entry<String, String> entry : values.entrySet()) {
- writer.writeSimpleResource(ResourceType.ID, entry.getKey());
+ writer.acceptSimpleResource(ResourceType.ID, entry.getKey());
}
}
}
@@ -549,8 +548,7 @@ public class AttrXmlResourceValue implements XmlResourceValue {
}
@Override
- public void writeToClass(AndroidResourceClassWriter writer) {
- }
+ public void writeToClass(AndroidResourceSymbolSink writer) {}
}
/** Represents an Android Color Attribute resource. */
@@ -578,8 +576,7 @@ public class AttrXmlResourceValue implements XmlResourceValue {
}
@Override
- public void writeToClass(AndroidResourceClassWriter writer) {
- }
+ public void writeToClass(AndroidResourceSymbolSink writer) {}
}
/** Represents an Android Boolean Attribute resource. */
@@ -607,8 +604,7 @@ public class AttrXmlResourceValue implements XmlResourceValue {
}
@Override
- public void writeToClass(AndroidResourceClassWriter writer) {
- }
+ public void writeToClass(AndroidResourceSymbolSink writer) {}
}
/** Represents an Android Float Attribute resource. */
@@ -636,8 +632,7 @@ public class AttrXmlResourceValue implements XmlResourceValue {
}
@Override
- public void writeToClass(AndroidResourceClassWriter writer) {
- }
+ public void writeToClass(AndroidResourceSymbolSink writer) {}
}
/** Represents an Android Dimension Attribute resource. */
@@ -666,8 +661,7 @@ public class AttrXmlResourceValue implements XmlResourceValue {
}
@Override
- public void writeToClass(AndroidResourceClassWriter writer) {
- }
+ public void writeToClass(AndroidResourceSymbolSink writer) {}
}
/** Represents an Android Integer Attribute resource. */
@@ -695,8 +689,7 @@ public class AttrXmlResourceValue implements XmlResourceValue {
}
@Override
- public void writeToClass(AndroidResourceClassWriter writer) {
- }
+ public void writeToClass(AndroidResourceSymbolSink writer) {}
}
/** Represents an Android String Attribute resource. */
@@ -724,8 +717,7 @@ public class AttrXmlResourceValue implements XmlResourceValue {
}
@Override
- public void writeToClass(AndroidResourceClassWriter writer) {
- }
+ public void writeToClass(AndroidResourceSymbolSink writer) {}
}
/** Represents an Android Fraction Attribute resource. */
@@ -753,8 +745,7 @@ public class AttrXmlResourceValue implements XmlResourceValue {
}
@Override
- public void writeToClass(AndroidResourceClassWriter writer) {
- }
+ public void writeToClass(AndroidResourceSymbolSink writer) {}
}
@Override
diff --git a/src/tools/android/java/com/google/devtools/build/android/xml/IdXmlResourceValue.java b/src/tools/android/java/com/google/devtools/build/android/xml/IdXmlResourceValue.java
index d0a4114c2f..c3fb2faefa 100644
--- a/src/tools/android/java/com/google/devtools/build/android/xml/IdXmlResourceValue.java
+++ b/src/tools/android/java/com/google/devtools/build/android/xml/IdXmlResourceValue.java
@@ -16,7 +16,7 @@ package com.google.devtools.build.android.xml;
import com.google.common.base.MoreObjects;
import com.google.devtools.build.android.AndroidDataWritingVisitor;
import com.google.devtools.build.android.AndroidDataWritingVisitor.StartTag;
-import com.google.devtools.build.android.AndroidResourceClassWriter;
+import com.google.devtools.build.android.AndroidResourceSymbolSink;
import com.google.devtools.build.android.DataSource;
import com.google.devtools.build.android.FullyQualifiedName;
import com.google.devtools.build.android.XmlResourceValue;
@@ -89,9 +89,8 @@ public class IdXmlResourceValue implements XmlResourceValue {
}
@Override
- public void writeResourceToClass(FullyQualifiedName key,
- AndroidResourceClassWriter resourceClassWriter) {
- resourceClassWriter.writeSimpleResource(key.type(), key.name());
+ public void writeResourceToClass(FullyQualifiedName key, AndroidResourceSymbolSink sink) {
+ sink.acceptSimpleResource(key.type(), key.name());
}
@Override
diff --git a/src/tools/android/java/com/google/devtools/build/android/xml/PluralXmlResourceValue.java b/src/tools/android/java/com/google/devtools/build/android/xml/PluralXmlResourceValue.java
index faf418a77f..1ff1578eab 100644
--- a/src/tools/android/java/com/google/devtools/build/android/xml/PluralXmlResourceValue.java
+++ b/src/tools/android/java/com/google/devtools/build/android/xml/PluralXmlResourceValue.java
@@ -17,7 +17,7 @@ import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableMap;
import com.google.devtools.build.android.AndroidDataWritingVisitor;
import com.google.devtools.build.android.AndroidDataWritingVisitor.ValuesResourceDefinition;
-import com.google.devtools.build.android.AndroidResourceClassWriter;
+import com.google.devtools.build.android.AndroidResourceSymbolSink;
import com.google.devtools.build.android.DataSource;
import com.google.devtools.build.android.FullyQualifiedName;
import com.google.devtools.build.android.XmlResourceValue;
@@ -98,9 +98,8 @@ public class PluralXmlResourceValue implements XmlResourceValue {
}
@Override
- public void writeResourceToClass(FullyQualifiedName key,
- AndroidResourceClassWriter resourceClassWriter) {
- resourceClassWriter.writeSimpleResource(key.type(), key.name());
+ public void writeResourceToClass(FullyQualifiedName key, AndroidResourceSymbolSink sink) {
+ sink.acceptSimpleResource(key.type(), key.name());
}
@Override
diff --git a/src/tools/android/java/com/google/devtools/build/android/xml/PublicXmlResourceValue.java b/src/tools/android/java/com/google/devtools/build/android/xml/PublicXmlResourceValue.java
index 9399c64647..b513cf4beb 100644
--- a/src/tools/android/java/com/google/devtools/build/android/xml/PublicXmlResourceValue.java
+++ b/src/tools/android/java/com/google/devtools/build/android/xml/PublicXmlResourceValue.java
@@ -21,7 +21,7 @@ import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.google.devtools.build.android.AndroidDataWritingVisitor;
-import com.google.devtools.build.android.AndroidResourceClassWriter;
+import com.google.devtools.build.android.AndroidResourceSymbolSink;
import com.google.devtools.build.android.DataSource;
import com.google.devtools.build.android.FullyQualifiedName;
import com.google.devtools.build.android.XmlResourceValue;
@@ -91,10 +91,9 @@ public class PublicXmlResourceValue implements XmlResourceValue {
}
@Override
- public void writeResourceToClass(
- FullyQualifiedName key, AndroidResourceClassWriter resourceClassWriter) {
+ public void writeResourceToClass(FullyQualifiedName key, AndroidResourceSymbolSink sink) {
for (Entry<ResourceType, Optional<Integer>> entry : typeToId.entrySet()) {
- resourceClassWriter.writePublicValue(entry.getKey(), key.name(), entry.getValue());
+ sink.acceptPublicResource(entry.getKey(), key.name(), entry.getValue());
}
}
diff --git a/src/tools/android/java/com/google/devtools/build/android/xml/ResourcesAttribute.java b/src/tools/android/java/com/google/devtools/build/android/xml/ResourcesAttribute.java
index 01424f8683..9202d599b5 100644
--- a/src/tools/android/java/com/google/devtools/build/android/xml/ResourcesAttribute.java
+++ b/src/tools/android/java/com/google/devtools/build/android/xml/ResourcesAttribute.java
@@ -17,7 +17,7 @@ import com.google.common.base.Joiner;
import com.google.common.base.MoreObjects;
import com.google.common.collect.Iterables;
import com.google.devtools.build.android.AndroidDataWritingVisitor;
-import com.google.devtools.build.android.AndroidResourceClassWriter;
+import com.google.devtools.build.android.AndroidResourceSymbolSink;
import com.google.devtools.build.android.DataSource;
import com.google.devtools.build.android.FullyQualifiedName;
import com.google.devtools.build.android.XmlResourceValue;
@@ -164,8 +164,7 @@ public class ResourcesAttribute implements XmlResourceValue {
}
@Override
- public void writeResourceToClass(
- FullyQualifiedName key, AndroidResourceClassWriter resourceClassWriter) {
+ public void writeResourceToClass(FullyQualifiedName key, AndroidResourceSymbolSink sink) {
// This is an xml attribute and does not have any java representation.
}
diff --git a/src/tools/android/java/com/google/devtools/build/android/xml/SimpleXmlResourceValue.java b/src/tools/android/java/com/google/devtools/build/android/xml/SimpleXmlResourceValue.java
index edcfe3fe4f..923c163ad3 100644
--- a/src/tools/android/java/com/google/devtools/build/android/xml/SimpleXmlResourceValue.java
+++ b/src/tools/android/java/com/google/devtools/build/android/xml/SimpleXmlResourceValue.java
@@ -18,7 +18,7 @@ import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableMap;
import com.google.devtools.build.android.AndroidDataWritingVisitor;
import com.google.devtools.build.android.AndroidDataWritingVisitor.StartTag;
-import com.google.devtools.build.android.AndroidResourceClassWriter;
+import com.google.devtools.build.android.AndroidResourceSymbolSink;
import com.google.devtools.build.android.DataSource;
import com.google.devtools.build.android.FullyQualifiedName;
import com.google.devtools.build.android.XmlResourceValue;
@@ -212,9 +212,8 @@ public class SimpleXmlResourceValue implements XmlResourceValue {
}
@Override
- public void writeResourceToClass(FullyQualifiedName key,
- AndroidResourceClassWriter resourceClassWriter) {
- resourceClassWriter.writeSimpleResource(key.type(), key.name());
+ public void writeResourceToClass(FullyQualifiedName key, AndroidResourceSymbolSink sink) {
+ sink.acceptSimpleResource(key.type(), key.name());
}
@Override
diff --git a/src/tools/android/java/com/google/devtools/build/android/xml/StyleXmlResourceValue.java b/src/tools/android/java/com/google/devtools/build/android/xml/StyleXmlResourceValue.java
index bfc02a4244..ccf52b6480 100644
--- a/src/tools/android/java/com/google/devtools/build/android/xml/StyleXmlResourceValue.java
+++ b/src/tools/android/java/com/google/devtools/build/android/xml/StyleXmlResourceValue.java
@@ -18,7 +18,7 @@ import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableMap;
import com.google.devtools.build.android.AndroidDataWritingVisitor;
import com.google.devtools.build.android.AndroidDataWritingVisitor.ValuesResourceDefinition;
-import com.google.devtools.build.android.AndroidResourceClassWriter;
+import com.google.devtools.build.android.AndroidResourceSymbolSink;
import com.google.devtools.build.android.DataSource;
import com.google.devtools.build.android.FullyQualifiedName;
import com.google.devtools.build.android.XmlResourceValue;
@@ -106,9 +106,8 @@ public class StyleXmlResourceValue implements XmlResourceValue {
}
@Override
- public void writeResourceToClass(FullyQualifiedName key,
- AndroidResourceClassWriter resourceClassWriter) {
- resourceClassWriter.writeSimpleResource(key.type(), key.name());
+ public void writeResourceToClass(FullyQualifiedName key, AndroidResourceSymbolSink sink) {
+ sink.acceptSimpleResource(key.type(), key.name());
}
@Override
diff --git a/src/tools/android/java/com/google/devtools/build/android/xml/StyleableXmlResourceValue.java b/src/tools/android/java/com/google/devtools/build/android/xml/StyleableXmlResourceValue.java
index 93478876a3..8e55e089d3 100644
--- a/src/tools/android/java/com/google/devtools/build/android/xml/StyleableXmlResourceValue.java
+++ b/src/tools/android/java/com/google/devtools/build/android/xml/StyleableXmlResourceValue.java
@@ -21,7 +21,7 @@ import com.google.common.collect.ImmutableMap.Builder;
import com.google.common.collect.Iterables;
import com.google.devtools.build.android.AndroidDataWritingVisitor;
import com.google.devtools.build.android.AndroidDataWritingVisitor.ValuesResourceDefinition;
-import com.google.devtools.build.android.AndroidResourceClassWriter;
+import com.google.devtools.build.android.AndroidResourceSymbolSink;
import com.google.devtools.build.android.DataSource;
import com.google.devtools.build.android.FullyQualifiedName;
import com.google.devtools.build.android.XmlResourceValue;
@@ -137,9 +137,8 @@ public class StyleableXmlResourceValue implements XmlResourceValue {
}
@Override
- public void writeResourceToClass(FullyQualifiedName key,
- AndroidResourceClassWriter resourceClassWriter) {
- resourceClassWriter.writeStyleableResource(key, attrs);
+ public void writeResourceToClass(FullyQualifiedName key, AndroidResourceSymbolSink sink) {
+ sink.acceptStyleableResource(key, attrs);
}
@Override