diff options
author | 2018-02-16 13:14:29 -0800 | |
---|---|---|
committer | 2018-02-16 13:18:21 -0800 | |
commit | f672a31b8b19baab95373e4f2f6d110aa8b8f0fb (patch) | |
tree | 58cef0309a67e62e3fe0ef024916d9d5c53bae8c /src/tools/android/java/com/google/devtools/build/android | |
parent | 950bcf79c47484832bb4c84fbb23f6b56800e0b3 (diff) |
Normalized the serialization proto to save space and allow greater versatility in storage.
RELNOTES: None
PiperOrigin-RevId: 186036607
Diffstat (limited to 'src/tools/android/java/com/google/devtools/build/android')
38 files changed, 682 insertions, 783 deletions
diff --git a/src/tools/android/java/com/google/devtools/build/android/Aapt2ResourcePackagingAction.java b/src/tools/android/java/com/google/devtools/build/android/Aapt2ResourcePackagingAction.java index 83b628cd9e..4ef23e76e2 100644 --- a/src/tools/android/java/com/google/devtools/build/android/Aapt2ResourcePackagingAction.java +++ b/src/tools/android/java/com/google/devtools/build/android/Aapt2ResourcePackagingAction.java @@ -125,83 +125,82 @@ public class Aapt2ResourcePackagingAction { profiler.recordEndOf("merging"); - - profiler.startTask("compile"); - final ResourceCompiler compiler = - ResourceCompiler.create( - executorService, - compiledResources, - aaptConfigOptions.aapt2, - aaptConfigOptions.buildToolsVersion); - - CompiledResources compiled = - options - .primaryData - .processDataBindings( - options.dataBindingInfoOut, options.packageForR, databindingResourcesRoot) - .compile(compiler, compiledResources) - .processManifest( - manifest -> - AndroidManifestProcessor.with(STD_LOGGER) - .processManifest( - options.applicationId, - options.versionCode, - options.versionName, - manifest, - processedManifest)) - .processManifest( - manifest -> - new DensitySpecificManifestProcessor(densities, densityManifest) - .process(manifest)); - profiler.recordEndOf("compile").startTask("link"); - // Write manifestOutput now before the dummy manifest is created. - if (options.manifestOutput != null) { - AndroidResourceOutputs.copyManifestToOutput(compiled, options.manifestOutput); - } - - List<CompiledResources> compiledResourceDeps = - // Last defined dependencies will overwrite previous one, so always place direct - // after transitive. - concat(options.transitiveData.stream(), options.directData.stream()) - .map(DependencyAndroidData::getCompiledSymbols) - .collect(toList()); - - List<Path> assetDirs = - concat(options.transitiveData.stream(), options.directData.stream()) - .flatMap(dep -> dep.assetDirs.stream()) - .collect(toList()); - assetDirs.addAll(options.primaryData.assetDirs); - - final PackagedResources packagedResources = - ResourceLinker.create(aaptConfigOptions.aapt2, linkedOut) - .profileUsing(profiler) - .customPackage(options.packageForR) - .outputAsProto(aaptConfigOptions.resourceTableAsProto) - .dependencies(ImmutableList.of(StaticLibrary.from(aaptConfigOptions.androidJar))) - .include(compiledResourceDeps) - .withAssets(assetDirs) - .buildVersion(aaptConfigOptions.buildToolsVersion) - .conditionalKeepRules(aaptConfigOptions.conditionalKeepRules == TriState.YES) - .filterToDensity(densities) - .includeOnlyConfigs(aaptConfigOptions.resourceConfigs) - .link(compiled) - .copyPackageTo(options.packagePath) - .copyProguardTo(options.proguardOutput) - .copyMainDexProguardTo(options.mainDexProguardOutput) - .createSourceJar(options.srcJarOutput) - .copyRTxtTo(options.rOutput); - profiler.recordEndOf("link"); - if (options.resourcesOutput != null) { - profiler.startTask("package"); - // The compiled resources and the merged resources should be the same. - // TODO(corysmith): Decompile or otherwise provide the exact resources in the apk. - ResourcesZip.fromApk( - mergedAndroidData.getResourceDir(), - packagedResources.getApk(), - packagedResources.getResourceIds()) - .writeTo(options.resourcesOutput, false /* compress */); - profiler.recordEndOf("package"); - } + profiler.startTask("compile"); + final ResourceCompiler compiler = + ResourceCompiler.create( + executorService, + compiledResources, + aaptConfigOptions.aapt2, + aaptConfigOptions.buildToolsVersion); + + CompiledResources compiled = + options + .primaryData + .processDataBindings( + options.dataBindingInfoOut, options.packageForR, databindingResourcesRoot) + .compile(compiler, compiledResources) + .processManifest( + manifest -> + AndroidManifestProcessor.with(STD_LOGGER) + .processManifest( + options.applicationId, + options.versionCode, + options.versionName, + manifest, + processedManifest)) + .processManifest( + manifest -> + new DensitySpecificManifestProcessor(densities, densityManifest) + .process(manifest)); + profiler.recordEndOf("compile").startTask("link"); + // Write manifestOutput now before the dummy manifest is created. + if (options.manifestOutput != null) { + AndroidResourceOutputs.copyManifestToOutput(compiled, options.manifestOutput); + } + + List<CompiledResources> compiledResourceDeps = + // Last defined dependencies will overwrite previous one, so always place direct + // after transitive. + concat(options.transitiveData.stream(), options.directData.stream()) + .map(DependencyAndroidData::getCompiledSymbols) + .collect(toList()); + + List<Path> assetDirs = + concat(options.transitiveData.stream(), options.directData.stream()) + .flatMap(dep -> dep.assetDirs.stream()) + .collect(toList()); + assetDirs.addAll(options.primaryData.assetDirs); + + final PackagedResources packagedResources = + ResourceLinker.create(aaptConfigOptions.aapt2, linkedOut) + .profileUsing(profiler) + .customPackage(options.packageForR) + .outputAsProto(aaptConfigOptions.resourceTableAsProto) + .dependencies(ImmutableList.of(StaticLibrary.from(aaptConfigOptions.androidJar))) + .include(compiledResourceDeps) + .withAssets(assetDirs) + .buildVersion(aaptConfigOptions.buildToolsVersion) + .conditionalKeepRules(aaptConfigOptions.conditionalKeepRules == TriState.YES) + .filterToDensity(densities) + .includeOnlyConfigs(aaptConfigOptions.resourceConfigs) + .link(compiled) + .copyPackageTo(options.packagePath) + .copyProguardTo(options.proguardOutput) + .copyMainDexProguardTo(options.mainDexProguardOutput) + .createSourceJar(options.srcJarOutput) + .copyRTxtTo(options.rOutput); + profiler.recordEndOf("link"); + if (options.resourcesOutput != null) { + profiler.startTask("package"); + // The compiled resources and the merged resources should be the same. + // TODO(corysmith): Decompile or otherwise provide the exact resources in the apk. + ResourcesZip.fromApk( + mergedAndroidData.getResourceDir(), + packagedResources.getApk(), + packagedResources.getResourceIds()) + .writeTo(options.resourcesOutput, false /* compress */); + profiler.recordEndOf("package"); } } } +} diff --git a/src/tools/android/java/com/google/devtools/build/android/AarGeneratorAction.java b/src/tools/android/java/com/google/devtools/build/android/AarGeneratorAction.java index 20e9e6c9d7..3f3c35c029 100644 --- a/src/tools/android/java/com/google/devtools/build/android/AarGeneratorAction.java +++ b/src/tools/android/java/com/google/devtools/build/android/AarGeneratorAction.java @@ -169,7 +169,7 @@ public class AarGeneratorAction { null, VariantType.LIBRARY, null, - /* filteredResources= */ ImmutableList.<String>of(), + /* filteredResources= */ ImmutableList.of(), options.throwOnResourceConflict); logger.fine(String.format("Merging finished at %dms", timer.elapsed(TimeUnit.MILLISECONDS))); diff --git a/src/tools/android/java/com/google/devtools/build/android/AndroidCompiledDataDeserializer.java b/src/tools/android/java/com/google/devtools/build/android/AndroidCompiledDataDeserializer.java index fb4d93dcc6..0e6209b2c9 100644 --- a/src/tools/android/java/com/google/devtools/build/android/AndroidCompiledDataDeserializer.java +++ b/src/tools/android/java/com/google/devtools/build/android/AndroidCompiledDataDeserializer.java @@ -28,9 +28,6 @@ import com.google.common.base.Stopwatch; import com.google.common.collect.ImmutableSet; import com.google.common.io.LittleEndianDataInputStream; import com.google.devtools.build.android.FullyQualifiedName.Factory; -import com.google.devtools.build.android.proto.SerializeFormat; -import com.google.devtools.build.android.proto.SerializeFormat.Header; -import com.google.devtools.build.android.xml.ResourcesAttribute.AttributeType; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; @@ -80,7 +77,8 @@ public class AndroidCompiledDataDeserializer implements AndroidDataDeserializer private void readResourceTable( LittleEndianDataInputStream resourceTableStream, KeyValueConsumers consumers, - Factory fqnFactory) throws IOException { + Factory fqnFactory) + throws IOException { long alignedSize = resourceTableStream.readLong(); Preconditions.checkArgument(alignedSize <= Integer.MAX_VALUE); @@ -113,34 +111,27 @@ public class AndroidCompiledDataDeserializer implements AndroidDataDeserializer new SimpleEntry<FullyQualifiedName, Boolean>(fqn, packageName.isEmpty())); List<ConfigValue> configValues = resource.getConfigValueList(); - if (configValues.isEmpty() - && resource.getVisibility().getLevel() == Level.PUBLIC) { + if (configValues.isEmpty() && resource.getVisibility().getLevel() == Level.PUBLIC) { int sourceIndex = resource.getVisibility().getSource().getPathIdx(); String source = sourcePool.get(sourceIndex); DataSource dataSource = DataSource.of(Paths.get(source)); - DataResourceXml dataResourceXml = DataResourceXml - .fromPublic(dataSource, resourceType, resource.getEntryId().getId()); + DataResourceXml dataResourceXml = + DataResourceXml.fromPublic(dataSource, resourceType, resource.getEntryId().getId()); consumers.combiningConsumer.accept(fqn, dataResourceXml); - } else if (packageName.isEmpty()) {// This means this resource is not in the android sdk + } else if (packageName.isEmpty()) { // This means this resource is not in the android sdk Preconditions.checkArgument(configValues.size() == 1); - int sourceIndex = - configValues.get(0) - .getValue() - .getSource() - .getPathIdx(); + int sourceIndex = configValues.get(0).getValue().getSource().getPathIdx(); String source = sourcePool.get(sourceIndex); DataSource dataSource = DataSource.of(Paths.get(source)); Value resourceValue = resource.getConfigValue(0).getValue(); DataResourceXml dataResourceXml = - DataResourceXml - .from(resourceValue, dataSource, resourceType, fullyQualifiedNames); + DataResourceXml.from(resourceValue, dataSource, resourceType, fullyQualifiedNames); - if (resourceType == ResourceType.ID - || resourceType == ResourceType.STYLEABLE) { + if (resourceType == ResourceType.ID || resourceType == ResourceType.STYLEABLE) { consumers.combiningConsumer.accept(fqn, dataResourceXml); } else { consumers.overwritingConsumer.accept(fqn, dataResourceXml); @@ -165,13 +156,14 @@ public class AndroidCompiledDataDeserializer implements AndroidDataDeserializer private void readCompiledFile( LittleEndianDataInputStream compiledFileStream, KeyValueConsumers consumers, - Factory fqnFactory) throws IOException { - //Skip aligned size. We don't need it here. + Factory fqnFactory) + throws IOException { + // Skip aligned size. We don't need it here. Preconditions.checkArgument(compiledFileStream.skipBytes(8) == 8); int resFileHeaderSize = compiledFileStream.readInt(); - //Skip data payload size. We don't need it here. + // Skip data payload size. We don't need it here. Preconditions.checkArgument(compiledFileStream.skipBytes(8) == 8); byte[] file = new byte[resFileHeaderSize]; @@ -201,35 +193,10 @@ public class AndroidCompiledDataDeserializer implements AndroidDataDeserializer } private void readAttributesFile( - InputStream resourceFileStream, - FileSystem fileSystem, - KeyValueConsumers consumers) throws IOException { - - Header header = Header.parseDelimitedFrom(resourceFileStream); - List<DataKey> fullyQualifiedNames = new ArrayList<>(); - for (int i = 0; i < header.getEntryCount(); i++) { - SerializeFormat.DataKey protoKey = - SerializeFormat.DataKey.parseDelimitedFrom(resourceFileStream); - fullyQualifiedNames.add(FullyQualifiedName.fromProto(protoKey)); - } - - DataSourceTable sourceTable = DataSourceTable.read(resourceFileStream, fileSystem, header); - - for (DataKey fullyQualifiedName : fullyQualifiedNames) { - SerializeFormat.DataValue protoValue = - SerializeFormat.DataValue.parseDelimitedFrom(resourceFileStream); - DataSource source = sourceTable.sourceFromId(protoValue.getSourceId()); - DataResourceXml dataResourceXml = - (DataResourceXml) DataResourceXml.from(protoValue, source); - AttributeType attributeType = - AttributeType.valueOf(protoValue.getXmlValue().getValueType()); - - if (attributeType.isCombining()) { - consumers.combiningConsumer.accept(fullyQualifiedName, dataResourceXml); - } else { - consumers.overwritingConsumer.accept(fullyQualifiedName, dataResourceXml); - } - } + InputStream resourceFileStream, FileSystem fileSystem, KeyValueConsumers consumers) + throws IOException { + AndroidParsedDataDeserializer.deserializeEntries( + consumers, resourceFileStream, fileSystem, ImmutableSet.of()); } @Override diff --git a/src/tools/android/java/com/google/devtools/build/android/AndroidDataSerializer.java b/src/tools/android/java/com/google/devtools/build/android/AndroidDataSerializer.java index 2304f3cb67..9a1c3a07ac 100644 --- a/src/tools/android/java/com/google/devtools/build/android/AndroidDataSerializer.java +++ b/src/tools/android/java/com/google/devtools/build/android/AndroidDataSerializer.java @@ -13,17 +13,23 @@ // limitations under the License. package com.google.devtools.build.android; +import com.google.common.base.MoreObjects; +import com.google.common.base.Preconditions; import com.google.common.base.Stopwatch; +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; import com.google.devtools.build.android.proto.SerializeFormat; +import com.google.devtools.build.android.proto.SerializeFormat.DataValue.Builder; import com.google.devtools.build.android.proto.SerializeFormat.Header; +import com.google.devtools.build.android.xml.Namespaces; import java.io.BufferedOutputStream; -import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; -import java.util.Map; +import java.util.ArrayList; +import java.util.List; import java.util.Map.Entry; import java.util.NavigableMap; import java.util.Set; @@ -33,6 +39,128 @@ import java.util.logging.Logger; /** Serializes {@link DataKey},{@link DataValue} entries to a binary file. */ public class AndroidDataSerializer { + + /** A visitor to accumulate the necessary state to write a resource entry. */ + public interface SerializeEntryVisitor extends Writeable { + SerializeEntryVisitor setSource(DataSource dataSource); + + SerializeEntryVisitor setKey(DataKey key); + + SerializeEntryVisitor overwrite(Set<DataSource> dataSource); + + SerializeEntryVisitor setXml(XmlResourceValue value); + + SerializeEntryVisitor setNamespaces(Namespaces namespaces); + } + + private static class DataValueBuilder implements SerializeEntryVisitor { + + private final Builder builder; + private final WritablePool<DataKey> keys; + private final WritablePool<DataSource> sources; + private final WritablePool<XmlResourceValue> xml; + private final WritablePool<Namespaces> namespaces; + + public DataValueBuilder( + Builder builder, + WritablePool<DataKey> keys, + WritablePool<DataSource> sources, + WritablePool<XmlResourceValue> xml, + WritablePool<Namespaces> namespaces) { + this.builder = Preconditions.checkNotNull(builder); + this.keys = Preconditions.checkNotNull(keys); + this.sources = sources; + this.xml = xml; + this.namespaces = namespaces; + } + + static DataValueBuilder create( + WritablePool<DataKey> keys, + WritablePool<DataSource> sources, + WritablePool<XmlResourceValue> xml, + WritablePool<Namespaces> namespaces) { + return new DataValueBuilder( + SerializeFormat.DataValue.newBuilder(), keys, sources, xml, namespaces); + } + + @Override + public SerializeEntryVisitor setSource(DataSource dataSource) { + builder.setSourceId(sources.queue(dataSource)); + overwrite(dataSource.overrides()); + return this; + } + + @Override + public SerializeEntryVisitor setKey(DataKey key) { + builder.setKeyId(keys.queue(key)); + return this; + } + + @Override + public SerializeEntryVisitor overwrite(Set<DataSource> dataSource) { + for (DataSource source : dataSource) { + builder.addOverwrittenSourceId(sources.queue(source)); + } + return this; + } + + @Override + public SerializeEntryVisitor setXml(XmlResourceValue value) { + builder.setXmlId(xml.queue(value)); + return this; + } + + @Override + public SerializeEntryVisitor setNamespaces(Namespaces namespaces) { + builder.setNamespaceId(this.namespaces.queue(namespaces)); + return this; + } + + @Override + public void writeTo(OutputStream out) throws IOException { + builder.build().writeDelimitedTo(out); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("builder", builder.build()) + .add("keys", keys) + .add("sources", sources) + .add("xml", xml) + .add("namespaces", namespaces) + .toString(); + } + } + + private static class WritablePool<T extends Writeable> implements Writeable { + BiMap<T, Integer> lookup = HashBiMap.create(); + Integer lastIndex = 0; + + Integer queue(T value) { + if (!lookup.containsKey(value)) { + lookup.put(value, lastIndex++); + } + return lookup.get(value); + } + + @Override + public void writeTo(OutputStream out) throws IOException { + final BiMap<Integer, T> indexed = lookup.inverse(); + for (int idx = 0; idx < lastIndex; idx++) { + indexed.get(idx).writeTo(out); + } + } + + public int size() { + return lookup.size(); + } + + public static <T extends Writeable> WritablePool<T> create() { + return new WritablePool<>(); + } + } + private static final Logger logger = Logger.getLogger(AndroidDataSerializer.class.getName()); private final NavigableMap<DataKey, DataValue> entries = new TreeMap<>(); @@ -63,52 +191,59 @@ public class AndroidDataSerializer { if (out.getParent() != null) { Files.createDirectories(out.getParent()); } + WritablePool<DataKey> keys = WritablePool.create(); + WritablePool<DataSource> sources = WritablePool.create(); + WritablePool<XmlResourceValue> xml = WritablePool.create(); + WritablePool<Namespaces> namespaces = WritablePool.create(); + try (OutputStream outStream = new BufferedOutputStream( Files.newOutputStream(out, StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE))) { // Set the header for the deserialization process. - SerializeFormat.Header.Builder headerBuilder = - Header.newBuilder().setEntryCount(entries.size()); - // Create table of source paths to allow references in the serialization format via an index. - ByteArrayOutputStream sourceTableOutputStream = new ByteArrayOutputStream(2048); - DataSourceTable sourceTable = - DataSourceTable.createAndWrite(entries, sourceTableOutputStream, headerBuilder); + List<SerializeEntryVisitor> values = new ArrayList<>(entries.size()); + for (Entry<DataKey, DataValue> entry : entries.entrySet()) { + values.add( + entry + .getValue() + .serializeTo( + DataValueBuilder.create(keys, sources, xml, namespaces) + .setKey(entry.getKey()))); + } - headerBuilder.build().writeDelimitedTo(outStream); + Header.newBuilder() + .setKeyCount(keys.size()) + .setSourceCount(sources.size()) + .setValueCount(values.size()) + .setXmlCount(xml.size()) + .setNamespacesCount(namespaces.size()) + .build() + .writeDelimitedTo(outStream); - writeKeyValuesTo(entries, outStream, sourceTable, sourceTableOutputStream.toByteArray()); - } - logger.fine(String.format("Serialized merged in %sms", timer.elapsed(TimeUnit.MILLISECONDS))); - } + /* + Serialization order: + keys + sources + values + xml + namespaces - private void writeKeyValuesTo( - NavigableMap<DataKey, DataValue> map, - OutputStream outStream, - DataSourceTable sourceTable, - byte[] sourceTableBytes) - throws IOException { - Set<Entry<DataKey, DataValue>> entries = map.entrySet(); - int[] orderedValueSizes = new int[entries.size()]; - int valueSizeIndex = 0; - // Serialize all the values in sorted order to a intermediate buffer, so that the keys - // can be associated with a value size. - // TODO(corysmith): Tune the size of the byte array. - ByteArrayOutputStream valuesOutputStream = new ByteArrayOutputStream(2048); - for (Map.Entry<DataKey, DataValue> entry : entries) { - orderedValueSizes[valueSizeIndex++] = - entry.getValue().serializeTo(sourceTable, valuesOutputStream); - } - // Serialize all the keys in sorted order - valueSizeIndex = 0; - for (Map.Entry<DataKey, DataValue> entry : entries) { - entry.getKey().serializeTo(outStream, orderedValueSizes[valueSizeIndex++]); + This allows deserializing keys for R generation, as well as sources and entries for + lightweight merging. + */ + + keys.writeTo(outStream); + sources.writeTo(outStream); + for (SerializeEntryVisitor value : values) { + value.writeTo(outStream); + } + xml.writeTo(outStream); + namespaces.writeTo(outStream); + + outStream.flush(); } - // write the source table - outStream.write(sourceTableBytes); - // write the values to the output stream. - outStream.write(valuesOutputStream.toByteArray()); + logger.fine(String.format("Serialized merged in %sms", timer.elapsed(TimeUnit.MILLISECONDS))); } /** Queues the key and value for serialization as a entries entry. */ diff --git a/src/tools/android/java/com/google/devtools/build/android/AndroidDataWriter.java b/src/tools/android/java/com/google/devtools/build/android/AndroidDataWriter.java index 38d38f3aa0..a9770f76cd 100644 --- a/src/tools/android/java/com/google/devtools/build/android/AndroidDataWriter.java +++ b/src/tools/android/java/com/google/devtools/build/android/AndroidDataWriter.java @@ -654,7 +654,7 @@ public class AndroidDataWriter implements AndroidDataWritingVisitor { } /** Base interface for writing information to a {@link Writer}. */ - private static interface Segment { + private interface Segment { /** * Writes the segment contents to a Writer * diff --git a/src/tools/android/java/com/google/devtools/build/android/AndroidParsedDataDeserializer.java b/src/tools/android/java/com/google/devtools/build/android/AndroidParsedDataDeserializer.java index e704c06552..3ded0da24f 100644 --- a/src/tools/android/java/com/google/devtools/build/android/AndroidParsedDataDeserializer.java +++ b/src/tools/android/java/com/google/devtools/build/android/AndroidParsedDataDeserializer.java @@ -15,7 +15,6 @@ package com.google.devtools.build.android; import com.google.common.base.Stopwatch; import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Maps; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListeningExecutorService; import com.google.common.util.concurrent.MoreExecutors; @@ -24,6 +23,9 @@ import com.google.devtools.build.android.ParsedAndroidData.Builder; import com.google.devtools.build.android.ParsedAndroidData.KeyValueConsumer; import com.google.devtools.build.android.proto.SerializeFormat; import com.google.devtools.build.android.proto.SerializeFormat.Header; +import com.google.devtools.build.android.proto.SerializeFormat.ProtoSource; +import com.google.devtools.build.android.proto.SerializeFormat.XmlNamespaces; +import com.google.devtools.build.android.xml.Namespaces; import java.io.Closeable; import java.io.IOException; import java.io.InputStream; @@ -34,12 +36,11 @@ import java.nio.file.StandardOpenOption; import java.util.ArrayList; import java.util.Collection; import java.util.List; -import java.util.Map; -import java.util.Map.Entry; import java.util.concurrent.Callable; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.logging.Logger; +import java.util.stream.Collectors; /** Deserializes {@link DataKey}, {@link DataValue} entries from a binary file. */ public class AndroidParsedDataDeserializer implements AndroidDataDeserializer { @@ -87,7 +88,7 @@ public class AndroidParsedDataDeserializer implements AndroidDataDeserializer { } public static AndroidParsedDataDeserializer create() { - return new AndroidParsedDataDeserializer(ImmutableSet.<String>of()); + return new AndroidParsedDataDeserializer(ImmutableSet.of()); } private AndroidParsedDataDeserializer(ImmutableSet<String> filteredResources) { @@ -108,12 +109,8 @@ public class AndroidParsedDataDeserializer implements AndroidDataDeserializer { Stopwatch timer = Stopwatch.createStarted(); try (InputStream in = Files.newInputStream(inPath, StandardOpenOption.READ)) { FileSystem currentFileSystem = inPath.getFileSystem(); - Header header = Header.parseDelimitedFrom(in); - if (header == null) { - throw new DeserializationException("No Header found in " + inPath); - } - readEntriesSegment(consumers, in, currentFileSystem, header); - } catch (IOException e) { + deserializeEntries(consumers, in, currentFileSystem, filteredResources); + } catch (Throwable e) { throw new DeserializationException("Error deserializing " + inPath, e); } finally { logger.fine( @@ -121,59 +118,103 @@ public class AndroidParsedDataDeserializer implements AndroidDataDeserializer { } } - private void readEntriesSegment( - KeyValueConsumers consumers, InputStream in, FileSystem currentFileSystem, Header header) + public static void deserializeEntries( + KeyValueConsumers consumers, + InputStream in, + FileSystem currentFileSystem, + ImmutableSet<String> filteredResources) throws IOException { - int numberOfEntries = header.getEntryCount(); - Map<DataKey, KeyValueConsumer<DataKey, ? extends DataValue>> keys = - Maps.newLinkedHashMapWithExpectedSize(numberOfEntries); - for (int i = 0; i < numberOfEntries; i++) { - SerializeFormat.DataKey protoKey = SerializeFormat.DataKey.parseDelimitedFrom(in); - if (protoKey.hasResourceType()) { - FullyQualifiedName resourceName = FullyQualifiedName.fromProto(protoKey); - keys.put( - resourceName, - resourceName.isOverwritable() - ? consumers.overwritingConsumer - : consumers.combiningConsumer); - } else { - keys.put(RelativeAssetPath.fromProto(protoKey, currentFileSystem), consumers.assetConsumer); - } + + /* + Serialization order: + header + keys + sources + values + xml + namespaces + */ + + Header header = Header.parseDelimitedFrom(in); + if (header == null) { + throw new DeserializationException("Invalid Format: no header"); + } + + DataKey[] keys = new DataKey[header.getKeyCount()]; + for (int i = 0; i < header.getKeyCount(); i++) { + final SerializeFormat.DataKey proto = SerializeFormat.DataKey.parseDelimitedFrom(in); + keys[i] = + proto.hasResourceType() + ? FullyQualifiedName.fromProto(proto) + : RelativeAssetPath.fromProto(proto, currentFileSystem); + } + + DataSource[] sources = new DataSource[header.getSourceCount()]; + for (int i = 0; i < header.getSourceCount(); i++) { + sources[i] = DataSource.from(ProtoSource.parseDelimitedFrom(in), currentFileSystem); } - // Read back the sources table. - DataSourceTable sourceTable = DataSourceTable.read(in, currentFileSystem, header); - - // TODO(corysmith): Make this a lazy read of the values. - for (Entry<DataKey, KeyValueConsumer<DataKey, ?>> entry : keys.entrySet()) { - SerializeFormat.DataValue protoValue = SerializeFormat.DataValue.parseDelimitedFrom(in); - DataSource source = sourceTable.sourceFromId(protoValue.getSourceId()); - // Compose the `shortPath` manually to ensure it uses a forward slash. - // Using Path.subpath would return a backslash-using path on Windows. - String shortPath = - source.getPath().getParent().getFileName() + "/" + source.getPath().getFileName(); - if (filteredResources.contains(shortPath) && !Files.exists(source.getPath())) { - // Skip files that were filtered out during analysis. - // TODO(asteinb): Properly filter out these files from android_library symbol files during - // analysis instead, and remove this list. + List<SerializeFormat.DataValue> values = new ArrayList<>(header.getValueCount()); + for (int i = 0; i < header.getValueCount(); i++) { + final SerializeFormat.DataValue protoValue = SerializeFormat.DataValue.parseDelimitedFrom(in); + if (sources[protoValue.getSourceId()].shouldFilter(filteredResources)) { continue; } - if (protoValue.hasXmlValue()) { - // TODO(corysmith): Figure out why the generics are wrong. - // If I use Map<DataKey, KeyValueConsumer<DataKey, ? extends DataValue>>, I can put - // consumers into the map, but I can't call accept. - // If I use Map<DataKey, KeyValueConsumer<DataKey, ? super DataValue>>, I can consume - // but I can't put. - // Same for below. - @SuppressWarnings("unchecked") - KeyValueConsumer<DataKey, DataValue> value = - (KeyValueConsumer<DataKey, DataValue>) entry.getValue(); - value.accept(entry.getKey(), DataResourceXml.from(protoValue, source)); - } else { - @SuppressWarnings("unchecked") - KeyValueConsumer<DataKey, DataValue> value = - (KeyValueConsumer<DataKey, DataValue>) entry.getValue(); - value.accept(entry.getKey(), DataValueFile.of(source)); + values.add(protoValue); + } + + XmlResourceValue[] xml = new XmlResourceValue[header.getXmlCount()]; + for (int i = 0; i < header.getXmlCount(); i++) { + xml[i] = + XmlResourceValues.valueFromProto(SerializeFormat.DataValueXml.parseDelimitedFrom(in)); + } + + Namespaces[] namespaces = new Namespaces[header.getNamespacesCount()]; + for (int i = 0; i < header.getNamespacesCount(); i++) { + final XmlNamespaces proto = XmlNamespaces.parseDelimitedFrom(in); + namespaces[i] = Namespaces.fromProto(proto); + } + + for (SerializeFormat.DataValue protoValue : values) { + final DataSource dataSource = + sources[protoValue.getSourceId()].overwrite( + protoValue + .getOverwrittenSourceIdList() + .stream() + .map(sourceId -> sources[sourceId]) + .collect(Collectors.toList()) + .toArray(new DataSource[0])); + + final DataKey dataKey = keys[protoValue.getKeyId()]; + + switch (dataKey.getKeyType()) { + case ASSET_PATH: + consumers.assetConsumer.accept(dataKey, DataValueFile.of(dataSource)); + break; + case FULL_QUALIFIED_NAME: + final FullyQualifiedName fullyQualifiedName = (FullyQualifiedName) dataKey; + + KeyValueConsumer<DataKey, DataResource> consumer = + fullyQualifiedName.isOverwritable() + ? consumers.overwritingConsumer + : consumers.combiningConsumer; + + if (!protoValue.hasXmlId()) { + consumer.accept(dataKey, DataValueFile.of(dataSource)); + } else { + consumer.accept( + fullyQualifiedName, + protoValue.hasNamespaceId() + ? DataResourceXml.createWithNamespaces( + dataSource, + xml[protoValue.getXmlId()], + namespaces[protoValue.getNamespaceId()]) + : DataResourceXml.createWithNoNamespace( + dataSource, xml[protoValue.getXmlId()])); + } + break; + default: + throw new DeserializationException("Unknown key type " + dataKey); } } } diff --git a/src/tools/android/java/com/google/devtools/build/android/AndroidResourceParsingAction.java b/src/tools/android/java/com/google/devtools/build/android/AndroidResourceParsingAction.java index e2a98e4f13..ce5cb77147 100644 --- a/src/tools/android/java/com/google/devtools/build/android/AndroidResourceParsingAction.java +++ b/src/tools/android/java/com/google/devtools/build/android/AndroidResourceParsingAction.java @@ -83,7 +83,7 @@ public class AndroidResourceParsingAction { logger.fine(String.format("Walked XML tree at %dms", timer.elapsed(TimeUnit.MILLISECONDS))); UnwrittenMergedAndroidData unwrittenData = UnwrittenMergedAndroidData.of( - null, parsedPrimary, ParsedAndroidData.from(ImmutableList.<DependencyAndroidData>of())); + null, parsedPrimary, ParsedAndroidData.from(ImmutableList.of())); AndroidDataSerializer serializer = AndroidDataSerializer.create(); unwrittenData.serializeTo(serializer); serializer.flushTo(options.output); diff --git a/src/tools/android/java/com/google/devtools/build/android/AndroidResourceValidatorAction.java b/src/tools/android/java/com/google/devtools/build/android/AndroidResourceValidatorAction.java index 23f1671a07..970c7bc512 100644 --- a/src/tools/android/java/com/google/devtools/build/android/AndroidResourceValidatorAction.java +++ b/src/tools/android/java/com/google/devtools/build/android/AndroidResourceValidatorAction.java @@ -162,7 +162,7 @@ public class AndroidResourceValidatorAction { options.packageForR, new FlagAaptOptions(aaptConfigOptions), aaptConfigOptions.resourceConfigs, - ImmutableList.<String>of(), + ImmutableList.of(), dummyManifest, resources, assets, diff --git a/src/tools/android/java/com/google/devtools/build/android/Converters.java b/src/tools/android/java/com/google/devtools/build/android/Converters.java index e58dd2d29b..75c5ead21e 100644 --- a/src/tools/android/java/com/google/devtools/build/android/Converters.java +++ b/src/tools/android/java/com/google/devtools/build/android/Converters.java @@ -218,7 +218,7 @@ public final class Converters { @Override public List<DependencySymbolFileProvider> convert(String input) throws OptionsParsingException { if (input.isEmpty()) { - return ImmutableList.<DependencySymbolFileProvider>of(); + return ImmutableList.of(); } try { ImmutableList.Builder<DependencySymbolFileProvider> builder = ImmutableList.builder(); @@ -473,7 +473,7 @@ public final class Converters { @Override public List<StaticLibrary> convert(String input) throws OptionsParsingException { - final Builder<StaticLibrary> builder = ImmutableList.<StaticLibrary>builder(); + final Builder<StaticLibrary> builder = ImmutableList.builder(); for (String path : SPLITTER.splitToList(input)) { builder.add(libraryConverter.convert(path)); } diff --git a/src/tools/android/java/com/google/devtools/build/android/DataKey.java b/src/tools/android/java/com/google/devtools/build/android/DataKey.java index 02f193c0f7..f8a76a5768 100644 --- a/src/tools/android/java/com/google/devtools/build/android/DataKey.java +++ b/src/tools/android/java/com/google/devtools/build/android/DataKey.java @@ -13,9 +13,6 @@ // limitations under the License. package com.google.devtools.build.android; -import java.io.IOException; -import java.io.OutputStream; - /** * A general interface for resource and asset keys. * @@ -26,16 +23,7 @@ import java.io.OutputStream; * * <p>For Assets, it is the asset path from the assets directory. */ -public interface DataKey extends Comparable<DataKey> { - - /** - * Writes the Key and the value size to a stream. - * - * @param output The destination stream to serialize the key. - * @param valueSize The size, in bytes, of the serialized output for this key. The value size can - * be used for calculating offsets of the value in the stream. - */ - void serializeTo(OutputStream output, int valueSize) throws IOException; +public interface DataKey extends Comparable<DataKey>, Writeable { /** Returns a human readable string representation of the key. */ String toPrettyString(); 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 2b39c449e6..eade76072a 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 @@ -20,13 +20,11 @@ import static com.android.resources.ResourceType.PUBLIC; import com.android.aapt.Resources.Value; import com.android.resources.ResourceType; import com.google.common.base.MoreObjects; -import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableMap; +import com.google.devtools.build.android.AndroidDataSerializer.SerializeEntryVisitor; import com.google.devtools.build.android.FullyQualifiedName.Factory; import com.google.devtools.build.android.FullyQualifiedName.VirtualType; import com.google.devtools.build.android.ParsedAndroidData.KeyValueConsumer; -import com.google.devtools.build.android.proto.SerializeFormat; -import com.google.devtools.build.android.proto.SerializeFormat.DataValueXml; import com.google.devtools.build.android.xml.ArrayXmlResourceValue; import com.google.devtools.build.android.xml.AttrXmlResourceValue; import com.google.devtools.build.android.xml.IdXmlResourceValue; @@ -40,7 +38,6 @@ import com.google.devtools.build.android.xml.StyleableXmlResourceValue; import com.google.protobuf.InvalidProtocolBufferException; import java.io.BufferedInputStream; import java.io.IOException; -import java.io.OutputStream; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; @@ -161,15 +158,6 @@ public class DataResourceXml implements DataResource { } } - @SuppressWarnings("deprecation") - // TODO(corysmith): Update proto to use get<>Map - public static DataValue from(SerializeFormat.DataValue protoValue, DataSource source) - throws InvalidProtocolBufferException { - DataValueXml xmlValue = protoValue.getXmlValue(); - return createWithNamespaces( - source, valueFromProto(xmlValue), Namespaces.from(xmlValue.getNamespace())); - } - public static DataResourceXml from( Value protoValue, DataSource source, @@ -184,33 +172,6 @@ public class DataResourceXml implements DataResource { return dataResourceXml; } - private static XmlResourceValue valueFromProto(SerializeFormat.DataValueXml proto) - throws InvalidProtocolBufferException { - Preconditions.checkArgument(proto.hasType()); - switch (proto.getType()) { - case ARRAY: - return ArrayXmlResourceValue.from(proto); - case SIMPLE: - return SimpleXmlResourceValue.from(proto); - case ATTR: - return AttrXmlResourceValue.from(proto); - case ID: - return IdXmlResourceValue.of(); - case PLURAL: - return PluralXmlResourceValue.from(proto); - case PUBLIC: - return PublicXmlResourceValue.from(proto); - case STYLE: - return StyleXmlResourceValue.from(proto); - case STYLEABLE: - return StyleableXmlResourceValue.from(proto); - case RESOURCES_ATTRIBUTE: - return ResourcesAttribute.from(proto); - default: - throw new IllegalArgumentException(); - } - } - private static XmlResourceValue valueFromProto( Value proto, ResourceType resourceType, @@ -327,7 +288,7 @@ public class DataResourceXml implements DataResource { } public static DataResourceXml createWithNoNamespace(Path sourcePath, XmlResourceValue xml) { - return createWithNamespaces(sourcePath, xml, ImmutableMap.<String, String>of()); + return createWithNamespaces(sourcePath, xml, ImmutableMap.of()); } public static DataResourceXml createWithNoNamespace(DataSource source, XmlResourceValue xml) { @@ -391,9 +352,8 @@ public class DataResourceXml implements DataResource { } @Override - public int serializeTo(DataSourceTable sourceTable, OutputStream outStream) - throws IOException { - return xml.serializeTo(sourceTable.getSourceId(source), namespaces, outStream); + public SerializeEntryVisitor serializeTo(SerializeEntryVisitor visitor) { + return visitor.setXml(xml).setSource(source).setNamespaces(namespaces); } // TODO(corysmith): Clean up all the casting. The type structure is unclean. diff --git a/src/tools/android/java/com/google/devtools/build/android/DataSource.java b/src/tools/android/java/com/google/devtools/build/android/DataSource.java index 16c79d7f81..994935484a 100644 --- a/src/tools/android/java/com/google/devtools/build/android/DataSource.java +++ b/src/tools/android/java/com/google/devtools/build/android/DataSource.java @@ -17,17 +17,20 @@ import com.android.SdkConstants; import com.google.common.base.MoreObjects; import com.google.common.base.Objects; import com.google.common.collect.ImmutableSet; +import com.google.devtools.build.android.proto.SerializeFormat; import com.google.devtools.build.android.proto.SerializeFormat.ProtoSource; import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; import java.nio.file.FileSystem; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.attribute.BasicFileAttributeView; +import java.util.Set; /** Represents where the DataValue was derived from. */ -public class DataSource implements Comparable<DataSource> { +public class DataSource implements Comparable<DataSource>, Writeable { public static DataSource from(ProtoSource protoSource, FileSystem currentFileSystem) { Path path = currentFileSystem.getPath(protoSource.getFilename()); @@ -35,7 +38,7 @@ public class DataSource implements Comparable<DataSource> { } public static DataSource of(Path sourcePath) { - return new DataSource(sourcePath, ImmutableSet.<DataSource>of()); + return new DataSource(sourcePath, ImmutableSet.of()); } private final Path path; @@ -98,6 +101,9 @@ public class DataSource implements Comparable<DataSource> { } public DataSource overwrite(DataSource... sources) { + if (sources.length == 0) { + return this; + } ImmutableSet.Builder<DataSource> overridesBuilder = ImmutableSet.<DataSource>builder().addAll(this.overrides); for (DataSource dataSource : sources) { @@ -137,4 +143,19 @@ public class DataSource implements Comparable<DataSource> { public String asConflictString() { return path.toString(); } + + @Override + public void writeTo(OutputStream out) throws IOException { + SerializeFormat.ProtoSource.newBuilder() + .setFilename(path.toString()) + .build() + .writeDelimitedTo(out); + } + + public boolean shouldFilter(Set<String> filteredResources) { + return filteredResources.contains(path.getParent().getFileName() + "/" + path.getFileName()) + // Since the filtered resources short path could match multiple sources, check to make sure + // the source doesn't exist. + && !Files.exists(path); + } } diff --git a/src/tools/android/java/com/google/devtools/build/android/DataSourceTable.java b/src/tools/android/java/com/google/devtools/build/android/DataSourceTable.java deleted file mode 100644 index ac953da711..0000000000 --- a/src/tools/android/java/com/google/devtools/build/android/DataSourceTable.java +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright 2016 The Bazel Authors. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -package com.google.devtools.build.android; - -import com.google.common.base.Function; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; -import com.google.devtools.build.android.proto.SerializeFormat; -import com.google.devtools.build.android.proto.SerializeFormat.Header; -import com.google.devtools.build.android.proto.SerializeFormat.ProtoSource; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.nio.file.FileSystem; -import java.util.LinkedHashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.NavigableMap; -import java.util.stream.Collectors; - -/** - * Tracks mappings from resource source paths (/foo/bar/res/values/colors.xml) to an ID for a more - * compact serialization format. - */ -class DataSourceTable { - - private static final Function<DataValue, DataSource> VALUE_TO_SOURCE = DataValue::source; - private final Map<DataSource, Integer> sourceTable = new LinkedHashMap<>(); - private DataSource[] idToSource; - - /** - * Creates a DataSourceTable and serialize to the given outstream. Assigns each resource source - * path a number to enable {@link #getSourceId(DataSource)} queries. - * - * @param map the final map of resources - * @param outStream stream to serialize the source table - * @param headerBuilder the header to serialize - * @throws IOException if this fails to serialize the table to the outStream - */ - public static DataSourceTable createAndWrite( - NavigableMap<DataKey, DataValue> map, OutputStream outStream, Header.Builder headerBuilder) - throws IOException { - DataSourceTable sourceTable = new DataSourceTable(); - sourceTable.writeSourceInfo(map, outStream); - sourceTable.setHeader(headerBuilder); - return sourceTable; - } - - /** Convert the absolute source path to the source table index */ - public int getSourceId(DataSource source) { - return sourceTable.get(source); - } - - private void writeSourceInfo(NavigableMap<DataKey, DataValue> map, OutputStream outStream) - throws IOException { - int sourceNumber = 0; - LinkedList<DataSource> sourceQueue = - map.values() - .stream() - .map(VALUE_TO_SOURCE) - .collect(Collectors.toCollection(LinkedList::new)); - while (!sourceQueue.isEmpty()) { - DataSource source = sourceQueue.pop(); - if (!sourceTable.containsKey(source)) { - sourceTable.put(source, sourceNumber); - ++sourceNumber; - sourceQueue.addAll(source.overrides()); - } - } - for (DataSource dataSource : sourceTable.keySet()) { - ProtoSource.newBuilder() - .setFilename(dataSource.getPath().toString()) - .addAllOverwritten(sourcesToIds(dataSource.overrides())) - .build() - .writeDelimitedTo(outStream); - } - } - - private List<Integer> sourcesToIds(ImmutableSet<DataSource> overrides) { - ImmutableList.Builder<Integer> idsBuilder = ImmutableList.builder(); - for (DataSource dataSource : overrides) { - if (!sourceTable.containsKey(dataSource)) { - throw new IllegalArgumentException( - "Cannot find data source: " + dataSource.toString() + " in " + sourceTable.keySet()); - } - idsBuilder.add(sourceTable.get(dataSource)); - } - return idsBuilder.build(); - } - - /** Fill in the serialize format header information required to deserialize */ - private Header.Builder setHeader(Header.Builder headerBuilder) { - return headerBuilder.setSourceCount(sourceTable.size()); - } - - /** Deserialize the source table and allow {@link #sourceFromId(int)} queries. */ - public static DataSourceTable read(InputStream in, FileSystem currentFileSystem, Header header) - throws IOException { - DataSourceTable sourceTable = new DataSourceTable(); - sourceTable.readSourceInfo(in, currentFileSystem, header); - return sourceTable; - } - - /** Convert the source ID to full Path */ - public DataSource sourceFromId(int sourceId) { - return idToSource[sourceId]; - } - - private void readSourceInfo(InputStream in, FileSystem currentFileSystem, Header header) - throws IOException { - int numberOfSources = header.getSourceCount(); - // Read back the sources. - idToSource = new DataSource[numberOfSources]; - for (int i = 0; i < numberOfSources; i++) { - ProtoSource protoSource = SerializeFormat.ProtoSource.parseDelimitedFrom(in); - idToSource[i] = DataSource.from(protoSource, currentFileSystem); - } - } -} diff --git a/src/tools/android/java/com/google/devtools/build/android/DataValue.java b/src/tools/android/java/com/google/devtools/build/android/DataValue.java index de9574508b..2a2bc51826 100644 --- a/src/tools/android/java/com/google/devtools/build/android/DataValue.java +++ b/src/tools/android/java/com/google/devtools/build/android/DataValue.java @@ -13,8 +13,8 @@ // limitations under the License. package com.google.devtools.build.android; +import com.google.devtools.build.android.AndroidDataSerializer.SerializeEntryVisitor; import java.io.IOException; -import java.io.OutputStream; /** * Represents the value associated with DataKey interface for resource and asset values. @@ -28,9 +28,8 @@ public interface DataValue { */ DataSource source(); - /** Serializes to a supplied stream and returns the number of bytes written. */ - int serializeTo( - DataSourceTable sourceTable, OutputStream output) throws IOException; + /** Serializes the value to the entry visitor. */ + SerializeEntryVisitor serializeTo(SerializeEntryVisitor visitor) throws IOException; DataValue update(DataSource source); 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 3270e3ced0..9a1792f447 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 @@ -14,11 +14,10 @@ package com.google.devtools.build.android; import com.google.common.base.MoreObjects; +import com.google.devtools.build.android.AndroidDataSerializer.SerializeEntryVisitor; import com.google.devtools.build.android.AndroidResourceMerger.MergingException; import com.google.devtools.build.android.proto.SerializeFormat; -import com.google.protobuf.CodedOutputStream; import java.io.IOException; -import java.io.OutputStream; import java.nio.file.Path; import java.util.Objects; @@ -85,13 +84,8 @@ public class DataValueFile implements DataResource, DataAsset { } @Override - public int serializeTo(DataSourceTable sourceTable, OutputStream output) - throws IOException { - SerializeFormat.DataValue.Builder builder = SerializeFormat.DataValue.newBuilder(); - SerializeFormat.DataValue value = builder.setSourceId(sourceTable.getSourceId(source)).build(); - value.writeDelimitedTo(output); - return CodedOutputStream.computeUInt32SizeNoTag(value.getSerializedSize()) - + value.getSerializedSize(); + public SerializeEntryVisitor serializeTo(SerializeEntryVisitor visitor) { + return visitor.setSource(source); } @Override diff --git a/src/tools/android/java/com/google/devtools/build/android/DependencyAndroidData.java b/src/tools/android/java/com/google/devtools/build/android/DependencyAndroidData.java index b4d4b99b3f..2a770464be 100644 --- a/src/tools/android/java/com/google/devtools/build/android/DependencyAndroidData.java +++ b/src/tools/android/java/com/google/devtools/build/android/DependencyAndroidData.java @@ -54,7 +54,7 @@ class DependencyAndroidData extends SerializedAndroidData { // The local symbols.bin is optional -- if it is missing, we'll use the full R.txt Path rTxt = exists(fileSystem.getPath(parts[3])); ImmutableList<Path> assetDirs = - parts[1].length() == 0 ? ImmutableList.<Path>of() : splitPaths(parts[1], fileSystem); + parts[1].length() == 0 ? ImmutableList.of() : splitPaths(parts[1], fileSystem); CompiledResources compiledSymbols = null; Path symbolsBin = null; diff --git a/src/tools/android/java/com/google/devtools/build/android/DeserializationException.java b/src/tools/android/java/com/google/devtools/build/android/DeserializationException.java index 8c6e5990c9..098f4f5316 100644 --- a/src/tools/android/java/com/google/devtools/build/android/DeserializationException.java +++ b/src/tools/android/java/com/google/devtools/build/android/DeserializationException.java @@ -13,7 +13,6 @@ // limitations under the License. package com.google.devtools.build.android; -import java.io.IOException; /** Thrown when there is an error during deserialization. */ public class DeserializationException extends RuntimeException { @@ -30,7 +29,7 @@ public class DeserializationException extends RuntimeException { this.isLegacy = false; } - public DeserializationException(IOException e) { + public DeserializationException(Throwable e) { super(e); this.isLegacy = false; } diff --git a/src/tools/android/java/com/google/devtools/build/android/FullyQualifiedName.java b/src/tools/android/java/com/google/devtools/build/android/FullyQualifiedName.java index 9344412390..d0a5545b4f 100644 --- a/src/tools/android/java/com/google/devtools/build/android/FullyQualifiedName.java +++ b/src/tools/android/java/com/google/devtools/build/android/FullyQualifiedName.java @@ -264,11 +264,6 @@ public class FullyQualifiedName implements DataKey { return KeyType.FULL_QUALIFIED_NAME; } - @Override - public void serializeTo(OutputStream out, int valueSize) throws IOException { - toSerializedBuilder().setValueSize(valueSize).build().writeDelimitedTo(out); - } - public SerializeFormat.DataKey.Builder toSerializedBuilder() { return SerializeFormat.DataKey.newBuilder() .setKeyPackage(pkg) @@ -277,6 +272,11 @@ public class FullyQualifiedName implements DataKey { .setKeyValue(name); } + @Override + public void writeTo(OutputStream out) throws IOException { + toSerializedBuilder().build().writeDelimitedTo(out); + } + /** The non-resource {@link Type}s of a {@link FullyQualifiedName}. */ public enum VirtualType implements Type { RESOURCES_ATTRIBUTE("<resources>", "Resources Attribute"); @@ -284,7 +284,7 @@ public class FullyQualifiedName implements DataKey { private final String name; private final String displayName; - private VirtualType(String name, String displayName) { + VirtualType(String name, String displayName) { this.name = name; this.displayName = displayName; } @@ -349,31 +349,31 @@ public class FullyQualifiedName implements DataKey { /** Represents the type of a {@link FullyQualifiedName}. */ public interface Type { - public String getName(); + String getName(); - public ConcreteType getType(); + ConcreteType getType(); - public boolean isOverwritable(FullyQualifiedName fqn); + boolean isOverwritable(FullyQualifiedName fqn); - public int compareTo(Type other); + int compareTo(Type other); @Override - public boolean equals(Object obj); + boolean equals(Object obj); @Override - public int hashCode(); + int hashCode(); @Override - public String toString(); + String toString(); /** * The category of type that a {@link Type} can be. * * <p><em>Note:</em> used for strict ordering of {@link FullyQualifiedName}s. */ - public enum ConcreteType { + enum ConcreteType { RESOURCE_TYPE, - VIRTUAL_TYPE; + VIRTUAL_TYPE } } @@ -511,7 +511,7 @@ public class FullyQualifiedName implements DataKey { // This is fragile but better than the Gradle scheme of just dropping // entire subtrees. - Builder<String> builder = ImmutableList.<String>builder(); + Builder<String> builder = ImmutableList.builder(); addIfNotNull(config.getCountryCodeQualifier(), builder); addIfNotNull(config.getNetworkCodeQualifier(), builder); if (transformedLocaleQualifiers.isEmpty()) { diff --git a/src/tools/android/java/com/google/devtools/build/android/ManifestMergerAction.java b/src/tools/android/java/com/google/devtools/build/android/ManifestMergerAction.java index a7301f1030..b8249dcda6 100644 --- a/src/tools/android/java/com/google/devtools/build/android/ManifestMergerAction.java +++ b/src/tools/android/java/com/google/devtools/build/android/ManifestMergerAction.java @@ -41,7 +41,6 @@ import java.util.logging.Logger; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; -import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.TransformerFactoryConfigurationError; @@ -162,8 +161,8 @@ public class ManifestMergerAction { private static Options options; private static Path removePermissions(Path manifest, Path outputDir) - throws IOException, ParserConfigurationException, TransformerConfigurationException, - TransformerException, TransformerFactoryConfigurationError, SAXException { + throws IOException, ParserConfigurationException, TransformerException, + TransformerFactoryConfigurationError, SAXException { DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); Document doc = docBuilder.parse(manifest.toFile()); for (String tag : PERMISSION_TAGS) { 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 index 12b7abe4ae..ee1663356d 100644 --- a/src/tools/android/java/com/google/devtools/build/android/PlaceholderIdFieldInitializerBuilder.java +++ b/src/tools/android/java/com/google/devtools/build/android/PlaceholderIdFieldInitializerBuilder.java @@ -453,7 +453,7 @@ class PlaceholderIdFieldInitializerBuilder { // 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(); + arrayInitValues.orderEntriesByValue(Ordering.natural()).build(); initList.put(field, IntArrayFieldInitializer.of(arrayInitMap.values())); int index = 0; for (String attr : arrayInitMap.keySet()) { diff --git a/src/tools/android/java/com/google/devtools/build/android/RelativeAssetPath.java b/src/tools/android/java/com/google/devtools/build/android/RelativeAssetPath.java index 1801026c96..fa5174222d 100644 --- a/src/tools/android/java/com/google/devtools/build/android/RelativeAssetPath.java +++ b/src/tools/android/java/com/google/devtools/build/android/RelativeAssetPath.java @@ -32,6 +32,7 @@ import java.util.Objects; * <p>Note: Assets have no qualifiers or packages. */ public class RelativeAssetPath implements DataKey { + /** A Factory that creates RelativeAssetsPath objects whose paths are relative to a given path. */ public static class Factory { private final Path assetRoot; @@ -110,12 +111,11 @@ public class RelativeAssetPath implements DataKey { } @Override - public void serializeTo(OutputStream output, int valueSize) throws IOException { + public void writeTo(OutputStream out) throws IOException { SerializeFormat.DataKey.newBuilder() .setKeyValue(relativeAssetPath.toString()) - .setValueSize(valueSize) .build() - .writeDelimitedTo(output); + .writeDelimitedTo(out); } @Override diff --git a/src/tools/android/java/com/google/devtools/build/android/ResourceProcessorBusyBox.java b/src/tools/android/java/com/google/devtools/build/android/ResourceProcessorBusyBox.java index a99da9b931..cb1f43c320 100644 --- a/src/tools/android/java/com/google/devtools/build/android/ResourceProcessorBusyBox.java +++ b/src/tools/android/java/com/google/devtools/build/android/ResourceProcessorBusyBox.java @@ -54,7 +54,7 @@ import java.util.logging.Logger; * </pre> */ public class ResourceProcessorBusyBox { - static enum Tool { + enum Tool { PACKAGE() { @Override void call(String[] args) throws Exception { diff --git a/src/tools/android/java/com/google/devtools/build/android/ResourceShrinkerAction.java b/src/tools/android/java/com/google/devtools/build/android/ResourceShrinkerAction.java index 7d9f59fb0d..bb68ef7e65 100644 --- a/src/tools/android/java/com/google/devtools/build/android/ResourceShrinkerAction.java +++ b/src/tools/android/java/com/google/devtools/build/android/ResourceShrinkerAction.java @@ -325,7 +325,7 @@ public class ResourceShrinkerAction { aaptConfigOptions.splits, new MergedAndroidData( shrunkResources, resourceFiles.resolve("assets"), options.primaryManifest), - ImmutableList.<DependencyAndroidData>of() /* libraries */, + ImmutableList.of() /* libraries */, generatedSources, options.shrunkApk, null /* proguardOutput */, diff --git a/src/tools/android/java/com/google/devtools/build/android/UnvalidatedAndroidDirectories.java b/src/tools/android/java/com/google/devtools/build/android/UnvalidatedAndroidDirectories.java index 49fdb18296..7aa3164c7c 100644 --- a/src/tools/android/java/com/google/devtools/build/android/UnvalidatedAndroidDirectories.java +++ b/src/tools/android/java/com/google/devtools/build/android/UnvalidatedAndroidDirectories.java @@ -41,8 +41,8 @@ public class UnvalidatedAndroidDirectories { } String[] parts = text.split(":"); return new UnvalidatedAndroidDirectories( - parts.length > 0 ? splitPaths(parts[0], fileSystem) : ImmutableList.<Path>of(), - parts.length > 1 ? splitPaths(parts[1], fileSystem) : ImmutableList.<Path>of()); + parts.length > 0 ? splitPaths(parts[0], fileSystem) : ImmutableList.of(), + parts.length > 1 ? splitPaths(parts[1], fileSystem) : ImmutableList.of()); } protected static ImmutableList<Path> splitPaths(String pathsString, FileSystem fileSystem) { diff --git a/src/tools/android/java/com/google/devtools/build/android/Writeable.java b/src/tools/android/java/com/google/devtools/build/android/Writeable.java new file mode 100644 index 0000000000..d1ef46dc0d --- /dev/null +++ b/src/tools/android/java/com/google/devtools/build/android/Writeable.java @@ -0,0 +1,23 @@ +// Copyright 2018 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 java.io.IOException; +import java.io.OutputStream; + +/** Common interface for serialization using the {@link AndroidDataSerializer}. */ +public interface Writeable { + /** Writes the current state to the {@link OutputStream}. */ + void writeTo(OutputStream out) throws IOException; +} 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 f24d6b271a..e8cab89c19 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 @@ -13,12 +13,8 @@ // limitations under the License. package com.google.devtools.build.android; -import com.google.devtools.build.android.xml.Namespaces; -import java.io.IOException; -import java.io.OutputStream; - /** An {@link XmlResourceValue} is extracted from xml files in the resource 'values' directory. */ -public interface XmlResourceValue { +public interface XmlResourceValue extends Writeable { /** * Each XmlValue is expected to write a valid representation in xml to the writer. * @@ -28,9 +24,6 @@ public interface XmlResourceValue { */ void write(FullyQualifiedName key, DataSource source, AndroidDataWritingVisitor mergedDataWriter); - /** Serializes the resource value to the OutputStream and returns the bytes written. */ - int serializeTo(int sourceId, Namespaces namespaces, OutputStream out) throws IOException; - /** * Combines these xml values together and returns a single value. * diff --git a/src/tools/android/java/com/google/devtools/build/android/XmlResourceValues.java b/src/tools/android/java/com/google/devtools/build/android/XmlResourceValues.java index 1ea38826bf..20df22251b 100644 --- a/src/tools/android/java/com/google/devtools/build/android/XmlResourceValues.java +++ b/src/tools/android/java/com/google/devtools/build/android/XmlResourceValues.java @@ -20,15 +20,18 @@ import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableMap; import com.google.devtools.build.android.ParsedAndroidData.KeyValueConsumer; import com.google.devtools.build.android.proto.SerializeFormat; +import com.google.devtools.build.android.xml.ArrayXmlResourceValue; import com.google.devtools.build.android.xml.AttrXmlResourceValue; import com.google.devtools.build.android.xml.IdXmlResourceValue; import com.google.devtools.build.android.xml.Namespaces; import com.google.devtools.build.android.xml.PluralXmlResourceValue; import com.google.devtools.build.android.xml.PublicXmlResourceValue; +import com.google.devtools.build.android.xml.ResourcesAttribute; import com.google.devtools.build.android.xml.SimpleXmlResourceValue; import com.google.devtools.build.android.xml.StyleXmlResourceValue; import com.google.devtools.build.android.xml.StyleableXmlResourceValue; import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.InvalidProtocolBufferException; import java.io.IOException; import java.io.OutputStream; import java.io.StringWriter; @@ -443,4 +446,35 @@ public class XmlResourceValues { public static boolean isSkip(StartElement start) { return isTag(start, TAG_SKIP); } + + /** Creates the value from a proto. */ + public static XmlResourceValue valueFromProto(SerializeFormat.DataValueXml proto) { + try { + Preconditions.checkArgument(proto.hasType()); + switch (proto.getType()) { + case ARRAY: + return ArrayXmlResourceValue.from(proto); + case SIMPLE: + return SimpleXmlResourceValue.from(proto); + case ATTR: + return AttrXmlResourceValue.from(proto); + case ID: + return IdXmlResourceValue.of(); + case PLURAL: + return PluralXmlResourceValue.from(proto); + case PUBLIC: + return PublicXmlResourceValue.from(proto); + case STYLE: + return StyleXmlResourceValue.from(proto); + case STYLEABLE: + return StyleableXmlResourceValue.from(proto); + case RESOURCES_ATTRIBUTE: + return ResourcesAttribute.from(proto); + default: + throw new IllegalArgumentException(); + } + } catch (InvalidProtocolBufferException e) { + throw new DeserializationException(e); + } + } } diff --git a/src/tools/android/java/com/google/devtools/build/android/proto/serialize_format.proto b/src/tools/android/java/com/google/devtools/build/android/proto/serialize_format.proto index b87d5fee83..9412cea29f 100644 --- a/src/tools/android/java/com/google/devtools/build/android/proto/serialize_format.proto +++ b/src/tools/android/java/com/google/devtools/build/android/proto/serialize_format.proto @@ -24,25 +24,29 @@ option java_package = "com.google.devtools.build.android.proto"; // See com.google.devtools.build.android.AndroidDataSerializer for details on // how these messages are used. message Header { - // The number of entries stored in a serialized buffer. - optional uint32 entry_count = 1; // The number of ProtoSource entries. - optional uint32 source_count = 2; + optional uint32 source_count = 1; + // The number of keys in the serialized buffer. + optional uint32 key_count = 2; + // The number of values in the serialized buffer. + optional uint32 value_count = 3; + // The number of xml definitions in the serialized buffer. + optional uint32 xml_count = 4; + // The number of xml namespace definitions in the serialized buffer. + optional uint32 namespaces_count = 5; } // The serialized format for a DataKey. message DataKey { // Used for both the FullyQualifiedName name and RelativeAssetPath path - optional string key_value = 2; - // The resource type for FullyQualifiedNames - optional string resource_type = 3; - optional string key_package = 4; - repeated string qualifiers = 5; - // The size of the associated value. Useful for calculating an offset. // Required - optional int32 value_size = 6; + optional string key_value = 1; + // The resource type for FullyQualifiedNames + optional string resource_type = 2; + optional string key_package = 3; + repeated string qualifiers = 4; // Whether this DataKey is a reference to another DataKey. - optional bool reference = 7; + optional bool reference = 5; } // The serialized format for a DataValue. @@ -51,30 +55,41 @@ message DataValue { // Required optional uint32 source_id = 1; - // If xml_value is defined it's an xml value, otherwise, it's a file value. - optional DataValueXml xml_value= 2; + // Index of stored xml value + optional uint32 xml_id = 2; + + // Index of stored key. + // Required + optional uint32 key_id = 3; + + // Sources that have been overwritten. + repeated uint32 overwritten_source_id = 4; + + optional uint32 namespace_id = 5; } // A container for all the source information to be persisted. message ProtoSource { // Required optional string filename = 1; - // The indexes of sources this source replaces. - repeated uint32 overwritten = 2; +} + +message XmlNamespaces { + map<string, string> namespace = 1; } // The container for a serialized xml value. message DataValueXml { enum XmlType { - ARRAY = 0; - ATTR = 1; - ID = 2; - PLURAL = 3; - PUBLIC = 4; - SIMPLE = 5; - STYLEABLE = 6; - STYLE = 7; - RESOURCES_ATTRIBUTE = 8; + ARRAY = 1; + ATTR = 2; + ID = 3; + PLURAL = 4; + PUBLIC = 5; + SIMPLE = 6; + STYLEABLE = 7; + STYLE = 8; + RESOURCES_ATTRIBUTE = 9; } optional XmlType type = 1; @@ -85,5 +100,4 @@ message DataValueXml { optional string value_type = 6; repeated DataKey references = 7; map<string, string> attribute = 8; - map<string, string> namespace = 9; } 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 fb74fd2208..f977c7b315 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 @@ -29,6 +29,8 @@ import com.google.devtools.build.android.FullyQualifiedName; import com.google.devtools.build.android.XmlResourceValue; import com.google.devtools.build.android.XmlResourceValues; import com.google.devtools.build.android.proto.SerializeFormat; +import com.google.devtools.build.android.proto.SerializeFormat.DataValueXml; +import com.google.devtools.build.android.proto.SerializeFormat.DataValueXml.XmlType; import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; @@ -45,14 +47,15 @@ import javax.xml.stream.events.XMLEvent; /** * Represents an Android resource array. * - * There are two flavors of Android Resource arrays: + * <p>There are two flavors of Android Resource arrays: + * * <ul> - * <li>Typed arrays (http://developer.android.com/guide/topics/resources/more-resources - * .html#TypedArray) which which are indicated by a <array> tag.</li> - * <li>Integer array (http://developer.android.com/guide/topics/resources/more-resources - * .html#IntegerArray) which are indicated by <integer-array> tag.</li> - * <li>String array (http://developer.android.com/guide/topics/resources/string-resource - * .html#StringArray) which are indicated by <string-array> tag.</li> + * <li>Typed arrays (http://developer.android.com/guide/topics/resources/more-resources + * .html#TypedArray) which which are indicated by a <array> tag. + * <li>Integer array (http://developer.android.com/guide/topics/resources/more-resources + * .html#IntegerArray) which are indicated by <integer-array> tag. + * <li>String array (http://developer.android.com/guide/topics/resources/string-resource + * .html#StringArray) which are indicated by <string-array> tag. * </ul> * * Both of these are accessed by R.array.<name> in java. @@ -62,9 +65,19 @@ public class ArrayXmlResourceValue implements XmlResourceValue { private static final QName TAG_INTEGER_ARRAY = QName.valueOf("integer-array"); private static final QName TAG_ARRAY = QName.valueOf("array"); private static final QName TAG_STRING_ARRAY = QName.valueOf("string-array"); - /** - * Enumerates the different types of array parentTags. - */ + + @Override + public void writeTo(OutputStream out) throws IOException { + DataValueXml.newBuilder() + .addAllListValue(values) + .setType(XmlType.ARRAY) + .putAllAttribute(attributes) + .setValueType(arrayType.toString()) + .build() + .writeDelimitedTo(out); + } + + /** Enumerates the different types of array parentTags. */ public enum ArrayType { INTEGER_ARRAY(TAG_INTEGER_ARRAY), ARRAY(TAG_ARRAY), @@ -105,7 +118,7 @@ public class ArrayXmlResourceValue implements XmlResourceValue { } public static XmlResourceValue of(ArrayType arrayType, List<String> values) { - return of(arrayType, values, ImmutableMap.<String, String>of()); + return of(arrayType, values, ImmutableMap.of()); } public static XmlResourceValue of( @@ -138,20 +151,20 @@ public class ArrayXmlResourceValue implements XmlResourceValue { } } - return of( - ArrayType.ARRAY, - items, - ImmutableMap.of()); + return of(ArrayType.ARRAY, items, ImmutableMap.of()); } @Override public void write( FullyQualifiedName key, DataSource source, AndroidDataWritingVisitor mergedDataWriter) { - ValuesResourceDefinition definition = mergedDataWriter.define(key).derivedFrom(source) - .startTag(arrayType.tagName) - .named(key) - .addAttributesFrom(attributes.entrySet()) - .closeTag(); + ValuesResourceDefinition definition = + mergedDataWriter + .define(key) + .derivedFrom(source) + .startTag(arrayType.tagName) + .named(key) + .addAttributesFrom(attributes.entrySet()) + .closeTag(); for (String value : values) { definition = definition @@ -165,21 +178,6 @@ public class ArrayXmlResourceValue implements XmlResourceValue { } @Override - public int serializeTo(int sourceId, Namespaces namespaces, OutputStream output) - throws IOException { - return XmlResourceValues.serializeProtoDataValue( - output, - XmlResourceValues.newSerializableDataValueBuilder(sourceId) - .setXmlValue( - SerializeFormat.DataValueXml.newBuilder() - .addAllListValue(values) - .setType(SerializeFormat.DataValueXml.XmlType.ARRAY) - .putAllNamespace(namespaces.asMap()) - .putAllAttribute(attributes) - .setValueType(arrayType.toString()))); - } - - @Override public int hashCode() { return Objects.hash(arrayType, values, attributes); } 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 f2645ac0a3..96ac31c947 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 @@ -404,23 +404,16 @@ public class AttrXmlResourceValue implements XmlResourceValue { } } - @SuppressWarnings("deprecation") @Override - public int serializeTo(int sourceId, Namespaces namespaces, OutputStream output) - throws IOException { - SerializeFormat.DataValue.Builder builder = - XmlResourceValues.newSerializableDataValueBuilder(sourceId); + public void writeTo(OutputStream out) throws IOException { SerializeFormat.DataValueXml.Builder xmlValueBuilder = SerializeFormat.DataValueXml.newBuilder(); - xmlValueBuilder - .setType(SerializeFormat.DataValueXml.XmlType.ATTR) - .putAllNamespace(namespaces.asMap()); + xmlValueBuilder.setType(SerializeFormat.DataValueXml.XmlType.ATTR); for (Entry<String, ResourceXmlAttrValue> entry : formats.entrySet()) { xmlValueBuilder.putMappedXmlValue( - entry.getKey(), entry.getValue().appendTo(builder.getXmlValueBuilder())); + entry.getKey(), entry.getValue().appendTo(SerializeFormat.DataValueXml.newBuilder())); } - builder.setXmlValue(xmlValueBuilder); - return XmlResourceValues.serializeProtoDataValue(output, builder); + xmlValueBuilder.build().writeDelimitedTo(out); } @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 c3fb2faefa..e3fe65aa02 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 @@ -20,11 +20,8 @@ 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; -import com.google.devtools.build.android.XmlResourceValues; import com.google.devtools.build.android.proto.SerializeFormat; -import com.google.devtools.build.android.proto.SerializeFormat.DataValueXml.Builder; import com.google.devtools.build.android.proto.SerializeFormat.DataValueXml.XmlType; -import com.google.protobuf.CodedOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.Objects; @@ -94,20 +91,8 @@ public class IdXmlResourceValue implements XmlResourceValue { } @Override - public int serializeTo(int sourceId, Namespaces namespaces, OutputStream output) - throws IOException { - Builder xmlValue = - SerializeFormat.DataValueXml.newBuilder() - .setType(XmlType.ID) - .putAllNamespace(namespaces.asMap()); - if (value != null) { - xmlValue.setValue(value); - } - SerializeFormat.DataValue dataValue = - XmlResourceValues.newSerializableDataValueBuilder(sourceId).setXmlValue(xmlValue).build(); - dataValue.writeDelimitedTo(output); - return CodedOutputStream.computeUInt32SizeNoTag(dataValue.getSerializedSize()) - + dataValue.getSerializedSize(); + public void writeTo(OutputStream out) throws IOException { + SerializeFormat.DataValueXml.newBuilder().setType(XmlType.ID).build().writeDelimitedTo(out); } @Override diff --git a/src/tools/android/java/com/google/devtools/build/android/xml/Namespaces.java b/src/tools/android/java/com/google/devtools/build/android/xml/Namespaces.java index 82e8810139..10da5ff225 100644 --- a/src/tools/android/java/com/google/devtools/build/android/xml/Namespaces.java +++ b/src/tools/android/java/com/google/devtools/build/android/xml/Namespaces.java @@ -16,8 +16,13 @@ package com.google.devtools.build.android.xml; import com.google.common.base.MoreObjects; import com.google.common.collect.ImmutableMap; import com.google.devtools.build.android.DataResourceXml; +import com.google.devtools.build.android.Writeable; import com.google.devtools.build.android.XmlResourceValue; import com.google.devtools.build.android.XmlResourceValues; +import com.google.devtools.build.android.proto.SerializeFormat; +import com.google.devtools.build.android.proto.SerializeFormat.XmlNamespaces; +import java.io.IOException; +import java.io.OutputStream; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -39,11 +44,23 @@ import javax.xml.stream.events.StartElement; * resources tag to combining multiple {@link DataResourceXml}s, the Namespaces must be tracked and * kept with each value. */ -public class Namespaces implements Iterable<Entry<String, String>> { +public class Namespaces implements Iterable<Entry<String, String>>, Writeable { private static final Logger logger = Logger.getLogger(Namespaces.class.getCanonicalName()); private static final Namespaces EMPTY_INSTANCE = new Namespaces(ImmutableMap.<String, String>of()); + @Override + public void writeTo(OutputStream out) throws IOException { + SerializeFormat.XmlNamespaces.newBuilder() + .putAllNamespace(prefixToUri) + .build() + .writeDelimitedTo(out); + } + + public static Namespaces fromProto(XmlNamespaces xmlNamespaces) { + return from(xmlNamespaces.getNamespaceMap()); + } + /** Collects prefix and uri pairs from elements. */ public static class Collector { private Map<String, String> prefixToUri = new HashMap<>(); 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 b912cc141e..7c1b6532e3 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 @@ -24,10 +24,8 @@ 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; -import com.google.devtools.build.android.XmlResourceValues; import com.google.devtools.build.android.proto.SerializeFormat; import com.google.devtools.build.android.proto.SerializeFormat.DataValueXml.XmlType; -import com.google.protobuf.CodedOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.HashMap; @@ -157,23 +155,13 @@ public class PluralXmlResourceValue implements XmlResourceValue { } @Override - public int serializeTo(int sourceId, Namespaces namespaces, OutputStream output) - throws IOException { - SerializeFormat.DataValue.Builder builder = - XmlResourceValues.newSerializableDataValueBuilder(sourceId); - SerializeFormat.DataValue value = - builder - .setXmlValue( - builder - .getXmlValueBuilder() - .setType(XmlType.PLURAL) - .putAllNamespace(namespaces.asMap()) - .putAllAttribute(attributes) - .putAllMappedStringValue(values)) - .build(); - value.writeDelimitedTo(output); - return CodedOutputStream.computeUInt32SizeNoTag(value.getSerializedSize()) - + value.getSerializedSize(); + public void writeTo(OutputStream out) throws IOException { + SerializeFormat.DataValueXml.newBuilder() + .setType(XmlType.PLURAL) + .putAllAttribute(attributes) + .putAllMappedStringValue(values) + .build() + .writeDelimitedTo(out); } @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 af4184d88a..c1e3eb40a0 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 @@ -19,13 +19,11 @@ import com.google.common.base.MoreObjects; import com.google.common.base.Optional; 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.AndroidResourceSymbolSink; import com.google.devtools.build.android.DataSource; import com.google.devtools.build.android.FullyQualifiedName; import com.google.devtools.build.android.XmlResourceValue; -import com.google.devtools.build.android.XmlResourceValues; import com.google.devtools.build.android.proto.SerializeFormat; import java.io.IOException; import java.io.OutputStream; @@ -33,6 +31,7 @@ import java.util.EnumMap; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; +import java.util.stream.Collectors; /** * Represents an Android resource <public> xml tag. @@ -139,23 +138,19 @@ public class PublicXmlResourceValue implements XmlResourceValue { } @Override - public int serializeTo(int sourceId, Namespaces namespaces, OutputStream output) - throws IOException { - Map<String, String> assignments = Maps.newLinkedHashMapWithExpectedSize(typeToId.size()); - for (Entry<ResourceType, Optional<Integer>> entry : typeToId.entrySet()) { - Optional<Integer> value = entry.getValue(); - String stringValue = value.isPresent() ? value.get().toString() : MISSING_ID_VALUE; - assignments.put(entry.getKey().toString(), stringValue); - } - SerializeFormat.DataValue.Builder builder = - XmlResourceValues.newSerializableDataValueBuilder(sourceId); - builder.setXmlValue( - builder - .getXmlValueBuilder() - .setType(SerializeFormat.DataValueXml.XmlType.PUBLIC) - .putAllNamespace(namespaces.asMap()) - .putAllMappedStringValue(assignments)); - return XmlResourceValues.serializeProtoDataValue(output, builder); + public void writeTo(OutputStream out) throws IOException { + SerializeFormat.DataValueXml.newBuilder() + .setType(SerializeFormat.DataValueXml.XmlType.PUBLIC) + .putAllMappedStringValue( + typeToId + .entrySet() + .stream() + .collect( + Collectors.toMap( + e -> e.getKey().toString(), + e -> e.getValue().transform(Object::toString).or(MISSING_ID_VALUE)))) + .build() + .writeDelimitedTo(out); } @Override @@ -178,7 +173,7 @@ public class PublicXmlResourceValue implements XmlResourceValue { } return of(combined); } - + @Override public String asConflictStringWith(DataSource source) { return source.asConflictString(); 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 a54667ecdb..75a6c0207e 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 @@ -21,9 +21,7 @@ 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; -import com.google.devtools.build.android.XmlResourceValues; import com.google.devtools.build.android.proto.SerializeFormat; -import com.google.devtools.build.android.proto.SerializeFormat.DataValueXml.Builder; import com.google.errorprone.annotations.Immutable; import java.io.IOException; import java.io.OutputStream; @@ -40,13 +38,15 @@ public class ResourcesAttribute implements XmlResourceValue { public String combine(String first, String second); } - private static final Combiner COMMA_SEPARATED_COMBINER = new Combiner() { - private final Joiner joiner = Joiner.on(','); - @Override - public String combine(String first, String second) { - return joiner.join(first, second); - } - }; + private static final Combiner COMMA_SEPARATED_COMBINER = + new Combiner() { + private final Joiner joiner = Joiner.on(','); + + @Override + public String combine(String first, String second) { + return joiner.join(first, second); + } + }; /** * Represents the semantic meaning of an xml attribute and how it is combined with other @@ -92,9 +92,7 @@ public class ResourcesAttribute implements XmlResourceValue { Map.Entry<String, String> attribute = Iterables.getOnlyElement(proto.getAttributeMap().entrySet()); return new ResourcesAttribute( - AttributeType.valueOf(proto.getValueType()), - attribute.getKey(), - attribute.getValue()); + AttributeType.valueOf(proto.getValueType()), attribute.getKey(), attribute.getValue()); } private final AttributeType type; @@ -114,19 +112,13 @@ public class ResourcesAttribute implements XmlResourceValue { } @Override - public int serializeTo(int sourceId, Namespaces namespaces, OutputStream output) - throws IOException { - SerializeFormat.DataValue.Builder builder = - XmlResourceValues.newSerializableDataValueBuilder(sourceId); - Builder xmlValueBuilder = - builder - .getXmlValueBuilder() - .putAllNamespace(namespaces.asMap()) - .setType(SerializeFormat.DataValueXml.XmlType.RESOURCES_ATTRIBUTE) - .setValueType(type.name()) - .putAttribute(name, value); - builder.setXmlValue(xmlValueBuilder); - return XmlResourceValues.serializeProtoDataValue(output, builder); + public void writeTo(OutputStream out) throws IOException { + SerializeFormat.DataValueXml.newBuilder() + .setType(SerializeFormat.DataValueXml.XmlType.RESOURCES_ATTRIBUTE) + .setValueType(type.name()) + .putAttribute(name, value) + .build() + .writeDelimitedTo(out); } @Override 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 32147cd49c..55bef85bc1 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 @@ -27,9 +27,10 @@ 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; -import com.google.devtools.build.android.XmlResourceValues; import com.google.devtools.build.android.proto.SerializeFormat; +import com.google.devtools.build.android.proto.SerializeFormat.DataValueXml; import com.google.devtools.build.android.proto.SerializeFormat.DataValueXml.Builder; +import com.google.devtools.build.android.proto.SerializeFormat.DataValueXml.XmlType; import java.io.IOException; import java.io.OutputStream; import java.util.Arrays; @@ -41,8 +42,7 @@ import javax.xml.namespace.QName; /** * Represents a simple Android resource xml value. * - * <p> - * There is a class of resources that are simple name/value pairs: string + * <p>There is a class of resources that are simple name/value pairs: string * (http://developer.android.com/guide/topics/resources/string-resource.html), bool * (http://developer.android.com/guide/topics/resources/more-resources.html#Bool), color * (http://developer.android.com/guide/topics/resources/more-resources.html#Color), and dimen @@ -66,106 +66,27 @@ public class SimpleXmlResourceValue implements XmlResourceValue { static final QName TAG_RAW = QName.valueOf("raw"); static final QName TAG_STRING = QName.valueOf("string"); - /** Provides an enumeration resource type and simple value validation. */ + /** Provides an enumeration resource type. */ public enum Type { - BOOL(TAG_BOOL) { - @Override - public boolean validate(String value) { - final String cleanValue = value.toLowerCase().trim(); - return "true".equals(cleanValue) || "false".equals(cleanValue); - } - }, - COLOR(TAG_COLOR) { - @Override - public boolean validate(String value) { - // TODO(corysmith): Validate the hex color. - return true; - } - }, - DIMEN(TAG_DIMEN) { - @Override - public boolean validate(String value) { - // TODO(corysmith): Validate the dimension type. - return true; - } - }, - DRAWABLE(TAG_DRAWABLE) { - @Override - public boolean validate(String value) { - // TODO(corysmith): Validate the drawable type. - return true; - } - }, - FRACTION(TAG_FRACTION) { - @Override - public boolean validate(String value) { - // TODO(corysmith): Validate the fraction type. - return true; - } - }, - INTEGER(TAG_INTEGER) { - @Override - public boolean validate(String value) { - // TODO(corysmith): Validate the integer type. - return true; - } - }, - ITEM(TAG_ITEM) { - @Override - public boolean validate(String value) { - // TODO(corysmith): Validate the item type. - return true; - } - }, - LAYOUT(TAG_LAYOUT) { - @Override - public boolean validate(String value) { - // TODO(corysmith): Validate the layout type. - return true; - } - }, - MENU(TAG_MENU) { - @Override - public boolean validate(String value) { - // TODO(corysmith): Validate the menu type. - return true; - } - }, - MIPMAP(TAG_MIPMAP) { - @Override - public boolean validate(String value) { - // TODO(corysmith): Validate the mipmap type. - return true; - } - }, - PUBLIC(TAG_PUBLIC) { - @Override - public boolean validate(String value) { - // TODO(corysmith): Validate the public type. - return true; - } - }, - RAW(TAG_RAW) { - @Override - public boolean validate(String value) { - // TODO(corysmith): Validate the raw type. - return true; - } - }, - STRING(TAG_STRING) { - @Override - public boolean validate(String value) { - return true; - } - }; + BOOL(TAG_BOOL), + COLOR(TAG_COLOR), + DIMEN(TAG_DIMEN), + DRAWABLE(TAG_DRAWABLE), + FRACTION(TAG_FRACTION), + INTEGER(TAG_INTEGER), + ITEM(TAG_ITEM), + LAYOUT(TAG_LAYOUT), + MENU(TAG_MENU), + MIPMAP(TAG_MIPMAP), + PUBLIC(TAG_PUBLIC), + RAW(TAG_RAW), + STRING(TAG_STRING); private final QName tagName; Type(QName tagName) { this.tagName = tagName; } - abstract boolean validate(String value); - public static Type from(ResourceType resourceType) { for (Type valueType : values()) { if (valueType.tagName.getLocalPart().equals(resourceType.getName())) { @@ -177,8 +98,7 @@ public class SimpleXmlResourceValue implements XmlResourceValue { throw new IllegalArgumentException( String.format( "%s resource type not found in available types: %s", - resourceType, - Arrays.toString(values()))); + resourceType, Arrays.toString(values()))); } } @@ -200,8 +120,7 @@ public class SimpleXmlResourceValue implements XmlResourceValue { return of(Type.ITEM, ImmutableMap.of("type", resourceType.getName(), "format", format), value); } - public static XmlResourceValue itemWithValue( - ResourceType resourceType, String value) { + public static XmlResourceValue itemWithValue(ResourceType resourceType, String value) { return of(Type.ITEM, ImmutableMap.of("type", resourceType.getName()), value); } @@ -265,11 +184,11 @@ public class SimpleXmlResourceValue implements XmlResourceValue { String.format(";%s,%d,%d", span.getTag(), span.getFirstChar(), span.getLastChar())); } stringValue = stringBuilder.toString(); - } else if ((resourceType == ResourceType.COLOR - || resourceType == ResourceType.DRAWABLE) && item.hasPrim()) { + } else if ((resourceType == ResourceType.COLOR || resourceType == ResourceType.DRAWABLE) + && item.hasPrim()) { stringValue = String.format("#%1$8s", Integer.toHexString(item.getPrim().getData())).replace(' ', '0'); - } else if (resourceType == ResourceType.INTEGER && item.hasPrim()){ + } else if (resourceType == ResourceType.INTEGER && item.hasPrim()) { stringValue = Integer.toString(item.getPrim().getData()); } else if (resourceType == ResourceType.BOOL && item.hasPrim()) { stringValue = item.getPrim().getData() == 0 ? "false" : "true"; @@ -282,10 +201,7 @@ public class SimpleXmlResourceValue implements XmlResourceValue { String.format("'%s' is not a valid resource type.", resourceType)); } - return of( - Type.valueOf(resourceType.toString().toUpperCase()), - ImmutableMap.of(), - stringValue); + return of(Type.valueOf(resourceType.toString().toUpperCase()), ImmutableMap.of(), stringValue); } @Override @@ -294,23 +210,20 @@ public class SimpleXmlResourceValue implements XmlResourceValue { } @Override - public int serializeTo(int sourceId, Namespaces namespaces, OutputStream output) - throws IOException { - SerializeFormat.DataValue.Builder builder = - XmlResourceValues.newSerializableDataValueBuilder(sourceId); - Builder xmlValueBuilder = - builder - .getXmlValueBuilder() - .putAllNamespace(namespaces.asMap()) - .setType(SerializeFormat.DataValueXml.XmlType.SIMPLE) + public void writeTo(OutputStream out) throws IOException { + final Builder builder = + DataValueXml.newBuilder() + .setType(XmlType.SIMPLE) + .setValueType(valueType.name()) // TODO(corysmith): Find a way to avoid writing strings to the serialized format // it's inefficient use of space and costs more when deserializing. .putAllAttribute(attributes); + if (value != null) { - xmlValueBuilder.setValue(value); + builder.setValue(value); } - builder.setXmlValue(xmlValueBuilder.setValueType(valueType.name())); - return XmlResourceValues.serializeProtoDataValue(output, builder); + + builder.build().writeDelimitedTo(out); } @Override @@ -342,7 +255,7 @@ public class SimpleXmlResourceValue implements XmlResourceValue { public XmlResourceValue combineWith(XmlResourceValue value) { throw new IllegalArgumentException(this + " is not a combinable resource."); } - + @Override public String asConflictStringWith(DataSource source) { if (value != null) { 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 5cd0f6e0a5..6929c9d588 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 @@ -24,7 +24,6 @@ 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; -import com.google.devtools.build.android.XmlResourceValues; import com.google.devtools.build.android.proto.SerializeFormat; import com.google.devtools.build.android.proto.SerializeFormat.DataValueXml.XmlType; import java.io.IOException; @@ -39,8 +38,7 @@ import javax.annotation.concurrent.Immutable; /** * Represents an Android Style Resource. * - * <p> - * Styles (http://developer.android.com/guide/topics/resources/style-resource.html) define a look + * <p>Styles (http://developer.android.com/guide/topics/resources/style-resource.html) define a look * and feel for a layout or other ui construct. They are effectively a s set of values that * correspond to <attr> resources defined either in the base android framework or in other * resources. They also allow inheritance on other styles. For a style to valid in a given resource @@ -154,19 +152,15 @@ public class StyleXmlResourceValue implements XmlResourceValue { } @Override - public int serializeTo(int sourceId, Namespaces namespaces, OutputStream output) - throws IOException { + public void writeTo(OutputStream out) throws IOException { SerializeFormat.DataValueXml.Builder xmlValueBuilder = SerializeFormat.DataValueXml.newBuilder() .setType(XmlType.STYLE) - .putAllNamespace(namespaces.asMap()) .putAllMappedStringValue(values); if (parent != null) { xmlValueBuilder.setValue(parent); } - return XmlResourceValues.serializeProtoDataValue( - output, - XmlResourceValues.newSerializableDataValueBuilder(sourceId).setXmlValue(xmlValueBuilder)); + xmlValueBuilder.build().writeDelimitedTo(out); } @Override @@ -195,7 +189,7 @@ public class StyleXmlResourceValue implements XmlResourceValue { public XmlResourceValue combineWith(XmlResourceValue value) { throw new IllegalArgumentException(this + " is not a combinable resource."); } - + @Override public String asConflictStringWith(DataSource source) { return source.asConflictString(); 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 c9661690cf..5006600a48 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 @@ -27,7 +27,6 @@ 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; -import com.google.devtools.build.android.XmlResourceValues; import com.google.devtools.build.android.proto.SerializeFormat; import com.google.devtools.build.android.proto.SerializeFormat.DataValueXml.XmlType; import java.io.IOException; @@ -38,6 +37,7 @@ import java.util.LinkedHashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; +import java.util.stream.Collectors; import javax.annotation.concurrent.Immutable; /** @@ -65,22 +65,11 @@ public class StyleableXmlResourceValue implements XmlResourceValue { static final Function<Entry<FullyQualifiedName, Boolean>, SerializeFormat.DataKey> FULLY_QUALIFIED_NAME_TO_DATA_KEY = - new Function<Entry<FullyQualifiedName, Boolean>, SerializeFormat.DataKey>() { - @Override - public SerializeFormat.DataKey apply(Entry<FullyQualifiedName, Boolean> input) { - return input.getKey().toSerializedBuilder().setReference(input.getValue()).build(); - } - }; + input -> input.getKey().toSerializedBuilder().setReference(input.getValue()).build(); static final Function<SerializeFormat.DataKey, Entry<FullyQualifiedName, Boolean>> DATA_KEY_TO_FULLY_QUALIFIED_NAME = - new Function<SerializeFormat.DataKey, Entry<FullyQualifiedName, Boolean>>() { - @Override - public Entry<FullyQualifiedName, Boolean> apply(SerializeFormat.DataKey input) { - FullyQualifiedName key = FullyQualifiedName.fromProto(input); - return new SimpleEntry<FullyQualifiedName, Boolean>(key, input.getReference()); - } - }; + input -> new SimpleEntry<>(FullyQualifiedName.fromProto(input), input.getReference()); private final ImmutableMap<FullyQualifiedName, Boolean> attrs; @@ -145,17 +134,17 @@ public class StyleableXmlResourceValue implements XmlResourceValue { } @Override - public int serializeTo(int sourceId, Namespaces namespaces, OutputStream output) - throws IOException { - return XmlResourceValues.serializeProtoDataValue( - output, - XmlResourceValues.newSerializableDataValueBuilder(sourceId) - .setXmlValue( - SerializeFormat.DataValueXml.newBuilder() - .setType(XmlType.STYLEABLE) - .putAllNamespace(namespaces.asMap()) - .addAllReferences( - Iterables.transform(attrs.entrySet(), FULLY_QUALIFIED_NAME_TO_DATA_KEY)))); + public void writeTo(OutputStream out) throws IOException { + SerializeFormat.DataValueXml.newBuilder() + .setType(XmlType.STYLEABLE) + .addAllReferences( + attrs + .entrySet() + .stream() + .map(FULLY_QUALIFIED_NAME_TO_DATA_KEY) + .collect(Collectors.toSet())) + .build() + .writeDelimitedTo(out); } public static XmlResourceValue from(SerializeFormat.DataValueXml proto) { @@ -202,12 +191,12 @@ public class StyleableXmlResourceValue implements XmlResourceValue { /** * Combines this instance with another {@link StyleableXmlResourceValue}. * - * Defining two Styleables (undocumented in the official Android Docs) with the same - * {@link FullyQualifiedName} results in a single Styleable containing a union of all the - * attribute references. + * <p>Defining two Styleables (undocumented in the official Android Docs) with the same {@link + * FullyQualifiedName} results in a single Styleable containing a union of all the attribute + * references. * - * @param value Another {@link StyleableXmlResourceValue} with the same - * {@link FullyQualifiedName}. + * @param value Another {@link StyleableXmlResourceValue} with the same {@link + * FullyQualifiedName}. * @return {@link StyleableXmlResourceValue} containing a sorted union of the attribute * references. * @throws IllegalArgumentException if value is not an {@link StyleableXmlResourceValue}. |