diff options
author | 2017-05-15 16:56:01 +0200 | |
---|---|---|
committer | 2017-05-15 19:51:22 +0200 | |
commit | da6bbce1ee317f3b4d7ca245058049ab2a032848 (patch) | |
tree | c32d28b144454f8ad1ed6c96ab7cebe2f89bb836 /src/tools/android/java/com/google/devtools | |
parent | 296cd4913d40b756e29fbe0aa6055addf228da6d (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')
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 |