aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/tools/android/java/com/google
diff options
context:
space:
mode:
authorGravatar corysmith <corysmith@google.com>2018-02-16 15:48:49 -0800
committerGravatar Copybara-Service <copybara-piper@google.com>2018-02-16 15:50:13 -0800
commitd18d3e2f83f9d582858a3edab7a450c60044028c (patch)
tree16ff3c0dff1e87a60ddcafaea2917623d48ef096 /src/tools/android/java/com/google
parentd7a56179ab35bf21ee6d77d87bb0096bef042175 (diff)
Automated rollback of commit f672a31b8b19baab95373e4f2f6d110aa8b8f0fb.
*** Reason for rollback *** Unclassified general breakages in tests. Rolling back for further investigation. *** Original change description *** Normalized the serialization proto to save space and allow greater versatility in storage. RELNOTES: None PiperOrigin-RevId: 186057879
Diffstat (limited to 'src/tools/android/java/com/google')
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/Aapt2ResourcePackagingAction.java153
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/AarGeneratorAction.java2
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/AndroidCompiledDataDeserializer.java67
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/AndroidDataSerializer.java211
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/AndroidDataWriter.java2
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/AndroidParsedDataDeserializer.java157
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/AndroidResourceParsingAction.java2
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/AndroidResourceValidatorAction.java2
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/Converters.java4
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/DataKey.java14
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/DataResourceXml.java48
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/DataSource.java25
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/DataSourceTable.java131
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/DataValue.java7
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/DataValueFile.java12
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/DependencyAndroidData.java2
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/DeserializationException.java3
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/FullyQualifiedName.java32
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/ManifestMergerAction.java5
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/PlaceholderIdFieldInitializerBuilder.java2
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/RelativeAssetPath.java6
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/ResourceProcessorBusyBox.java2
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/ResourceShrinkerAction.java2
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/UnvalidatedAndroidDirectories.java4
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/Writeable.java23
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/XmlResourceValue.java9
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/XmlResourceValues.java34
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/proto/serialize_format.proto64
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/xml/ArrayXmlResourceValue.java68
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/xml/AttrXmlResourceValue.java15
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/xml/IdXmlResourceValue.java19
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/xml/Namespaces.java19
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/xml/PluralXmlResourceValue.java26
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/xml/PublicXmlResourceValue.java35
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/xml/ResourcesAttribute.java42
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/xml/SimpleXmlResourceValue.java153
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/xml/StyleXmlResourceValue.java14
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/xml/StyleableXmlResourceValue.java49
38 files changed, 783 insertions, 682 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 4ef23e76e2..83b628cd9e 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,82 +125,83 @@ 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 3f3c35c029..20e9e6c9d7 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.of(),
+ /* filteredResources= */ ImmutableList.<String>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 0e6209b2c9..fb4d93dcc6 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,6 +28,9 @@ 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;
@@ -77,8 +80,7 @@ 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);
@@ -111,27 +113,34 @@ 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);
@@ -156,14 +165,13 @@ 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];
@@ -193,10 +201,35 @@ public class AndroidCompiledDataDeserializer implements AndroidDataDeserializer
}
private void readAttributesFile(
- InputStream resourceFileStream, FileSystem fileSystem, KeyValueConsumers consumers)
- throws IOException {
- AndroidParsedDataDeserializer.deserializeEntries(
- consumers, resourceFileStream, fileSystem, ImmutableSet.of());
+ 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);
+ }
+ }
}
@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 9a1c3a07ac..2304f3cb67 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,23 +13,17 @@
// 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.ArrayList;
-import java.util.List;
+import java.util.Map;
import java.util.Map.Entry;
import java.util.NavigableMap;
import java.util.Set;
@@ -39,128 +33,6 @@ 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<>();
@@ -191,61 +63,54 @@ 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());
- 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())));
- }
-
- Header.newBuilder()
- .setKeyCount(keys.size())
- .setSourceCount(sources.size())
- .setValueCount(values.size())
- .setXmlCount(xml.size())
- .setNamespacesCount(namespaces.size())
- .build()
- .writeDelimitedTo(outStream);
-
- /*
- Serialization order:
- keys
- sources
- values
- xml
- namespaces
+ // 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);
- This allows deserializing keys for R generation, as well as sources and entries for
- lightweight merging.
- */
+ headerBuilder.build().writeDelimitedTo(outStream);
- keys.writeTo(outStream);
- sources.writeTo(outStream);
- for (SerializeEntryVisitor value : values) {
- value.writeTo(outStream);
- }
- xml.writeTo(outStream);
- namespaces.writeTo(outStream);
-
- outStream.flush();
+ writeKeyValuesTo(entries, outStream, sourceTable, sourceTableOutputStream.toByteArray());
}
logger.fine(String.format("Serialized merged in %sms", timer.elapsed(TimeUnit.MILLISECONDS)));
}
+ 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++]);
+ }
+ // write the source table
+ outStream.write(sourceTableBytes);
+ // write the values to the output stream.
+ outStream.write(valuesOutputStream.toByteArray());
+ }
+
/** Queues the key and value for serialization as a entries entry. */
public void queueForSerialization(DataKey key, DataValue value) {
entries.put(key, value);
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 a9770f76cd..38d38f3aa0 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 interface Segment {
+ private static 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 3ded0da24f..e704c06552 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,6 +15,7 @@ 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;
@@ -23,9 +24,6 @@ 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;
@@ -36,11 +34,12 @@ 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 {
@@ -88,7 +87,7 @@ public class AndroidParsedDataDeserializer implements AndroidDataDeserializer {
}
public static AndroidParsedDataDeserializer create() {
- return new AndroidParsedDataDeserializer(ImmutableSet.of());
+ return new AndroidParsedDataDeserializer(ImmutableSet.<String>of());
}
private AndroidParsedDataDeserializer(ImmutableSet<String> filteredResources) {
@@ -109,8 +108,12 @@ public class AndroidParsedDataDeserializer implements AndroidDataDeserializer {
Stopwatch timer = Stopwatch.createStarted();
try (InputStream in = Files.newInputStream(inPath, StandardOpenOption.READ)) {
FileSystem currentFileSystem = inPath.getFileSystem();
- deserializeEntries(consumers, in, currentFileSystem, filteredResources);
- } catch (Throwable e) {
+ Header header = Header.parseDelimitedFrom(in);
+ if (header == null) {
+ throw new DeserializationException("No Header found in " + inPath);
+ }
+ readEntriesSegment(consumers, in, currentFileSystem, header);
+ } catch (IOException e) {
throw new DeserializationException("Error deserializing " + inPath, e);
} finally {
logger.fine(
@@ -118,103 +121,59 @@ public class AndroidParsedDataDeserializer implements AndroidDataDeserializer {
}
}
- public static void deserializeEntries(
- KeyValueConsumers consumers,
- InputStream in,
- FileSystem currentFileSystem,
- ImmutableSet<String> filteredResources)
+ private void readEntriesSegment(
+ KeyValueConsumers consumers, InputStream in, FileSystem currentFileSystem, Header header)
throws IOException {
-
- /*
- 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);
+ 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);
+ }
}
- 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)) {
+ // 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.
continue;
}
- 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);
+ 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));
}
}
}
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 ce5cb77147..e2a98e4f13 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.of()));
+ null, parsedPrimary, ParsedAndroidData.from(ImmutableList.<DependencyAndroidData>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 970c7bc512..23f1671a07 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.of(),
+ ImmutableList.<String>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 75c5ead21e..e58dd2d29b 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.of();
+ return ImmutableList.<DependencySymbolFileProvider>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.builder();
+ final Builder<StaticLibrary> builder = ImmutableList.<StaticLibrary>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 f8a76a5768..02f193c0f7 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,6 +13,9 @@
// 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.
*
@@ -23,7 +26,16 @@ package com.google.devtools.build.android;
*
* <p>For Assets, it is the asset path from the assets directory.
*/
-public interface DataKey extends Comparable<DataKey>, Writeable {
+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;
/** 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 eade76072a..2b39c449e6 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,11 +20,13 @@ 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;
@@ -38,6 +40,7 @@ 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;
@@ -158,6 +161,15 @@ 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,
@@ -172,6 +184,33 @@ 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,
@@ -288,7 +327,7 @@ public class DataResourceXml implements DataResource {
}
public static DataResourceXml createWithNoNamespace(Path sourcePath, XmlResourceValue xml) {
- return createWithNamespaces(sourcePath, xml, ImmutableMap.of());
+ return createWithNamespaces(sourcePath, xml, ImmutableMap.<String, String>of());
}
public static DataResourceXml createWithNoNamespace(DataSource source, XmlResourceValue xml) {
@@ -352,8 +391,9 @@ public class DataResourceXml implements DataResource {
}
@Override
- public SerializeEntryVisitor serializeTo(SerializeEntryVisitor visitor) {
- return visitor.setXml(xml).setSource(source).setNamespaces(namespaces);
+ public int serializeTo(DataSourceTable sourceTable, OutputStream outStream)
+ throws IOException {
+ return xml.serializeTo(sourceTable.getSourceId(source), namespaces, outStream);
}
// 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 994935484a..16c79d7f81 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,20 +17,17 @@ 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>, Writeable {
+public class DataSource implements Comparable<DataSource> {
public static DataSource from(ProtoSource protoSource, FileSystem currentFileSystem) {
Path path = currentFileSystem.getPath(protoSource.getFilename());
@@ -38,7 +35,7 @@ public class DataSource implements Comparable<DataSource>, Writeable {
}
public static DataSource of(Path sourcePath) {
- return new DataSource(sourcePath, ImmutableSet.of());
+ return new DataSource(sourcePath, ImmutableSet.<DataSource>of());
}
private final Path path;
@@ -101,9 +98,6 @@ public class DataSource implements Comparable<DataSource>, Writeable {
}
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) {
@@ -143,19 +137,4 @@ public class DataSource implements Comparable<DataSource>, Writeable {
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
new file mode 100644
index 0000000000..ac953da711
--- /dev/null
+++ b/src/tools/android/java/com/google/devtools/build/android/DataSourceTable.java
@@ -0,0 +1,131 @@
+// 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 2a2bc51826..de9574508b 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,8 +28,9 @@ public interface DataValue {
*/
DataSource source();
- /** Serializes the value to the entry visitor. */
- SerializeEntryVisitor serializeTo(SerializeEntryVisitor visitor) throws IOException;
+ /** Serializes to a supplied stream and returns the number of bytes written. */
+ int serializeTo(
+ DataSourceTable sourceTable, OutputStream output) 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 9a1792f447..3270e3ced0 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,10 +14,11 @@
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;
@@ -84,8 +85,13 @@ public class DataValueFile implements DataResource, DataAsset {
}
@Override
- public SerializeEntryVisitor serializeTo(SerializeEntryVisitor visitor) {
- return visitor.setSource(source);
+ 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();
}
@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 2a770464be..b4d4b99b3f 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.of() : splitPaths(parts[1], fileSystem);
+ parts[1].length() == 0 ? ImmutableList.<Path>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 098f4f5316..8c6e5990c9 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,6 +13,7 @@
// 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 {
@@ -29,7 +30,7 @@ public class DeserializationException extends RuntimeException {
this.isLegacy = false;
}
- public DeserializationException(Throwable e) {
+ public DeserializationException(IOException 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 d0a5545b4f..9344412390 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,6 +264,11 @@ 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)
@@ -272,11 +277,6 @@ 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;
- VirtualType(String name, String displayName) {
+ private 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 {
- String getName();
+ public String getName();
- ConcreteType getType();
+ public ConcreteType getType();
- boolean isOverwritable(FullyQualifiedName fqn);
+ public boolean isOverwritable(FullyQualifiedName fqn);
- int compareTo(Type other);
+ public int compareTo(Type other);
@Override
- boolean equals(Object obj);
+ public boolean equals(Object obj);
@Override
- int hashCode();
+ public int hashCode();
@Override
- String toString();
+ public String toString();
/**
* The category of type that a {@link Type} can be.
*
* <p><em>Note:</em> used for strict ordering of {@link FullyQualifiedName}s.
*/
- enum ConcreteType {
+ public 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.builder();
+ Builder<String> builder = ImmutableList.<String>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 b8249dcda6..a7301f1030 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,6 +41,7 @@ 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;
@@ -161,8 +162,8 @@ public class ManifestMergerAction {
private static Options options;
private static Path removePermissions(Path manifest, Path outputDir)
- throws IOException, ParserConfigurationException, TransformerException,
- TransformerFactoryConfigurationError, SAXException {
+ throws IOException, ParserConfigurationException, TransformerConfigurationException,
+ 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 ee1663356d..12b7abe4ae 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.natural()).build();
+ arrayInitValues.orderEntriesByValue(Ordering.<Integer>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 fa5174222d..1801026c96 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,7 +32,6 @@ 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;
@@ -111,11 +110,12 @@ public class RelativeAssetPath implements DataKey {
}
@Override
- public void writeTo(OutputStream out) throws IOException {
+ public void serializeTo(OutputStream output, int valueSize) throws IOException {
SerializeFormat.DataKey.newBuilder()
.setKeyValue(relativeAssetPath.toString())
+ .setValueSize(valueSize)
.build()
- .writeDelimitedTo(out);
+ .writeDelimitedTo(output);
}
@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 cb1f43c320..a99da9b931 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 {
- enum Tool {
+ static 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 bb68ef7e65..7d9f59fb0d 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.of() /* libraries */,
+ ImmutableList.<DependencyAndroidData>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 7aa3164c7c..49fdb18296 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.of(),
- parts.length > 1 ? splitPaths(parts[1], fileSystem) : ImmutableList.of());
+ parts.length > 0 ? splitPaths(parts[0], fileSystem) : ImmutableList.<Path>of(),
+ parts.length > 1 ? splitPaths(parts[1], fileSystem) : ImmutableList.<Path>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
deleted file mode 100644
index d1ef46dc0d..0000000000
--- a/src/tools/android/java/com/google/devtools/build/android/Writeable.java
+++ /dev/null
@@ -1,23 +0,0 @@
-// 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 e8cab89c19..f24d6b271a 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,8 +13,12 @@
// 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 extends Writeable {
+public interface XmlResourceValue {
/**
* Each XmlValue is expected to write a valid representation in xml to the writer.
*
@@ -24,6 +28,9 @@ public interface XmlResourceValue extends Writeable {
*/
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 20df22251b..1ea38826bf 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,18 +20,15 @@ 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;
@@ -446,35 +443,4 @@ 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 9412cea29f..b87d5fee83 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,29 +24,25 @@ 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 = 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;
+ optional uint32 source_count = 2;
}
// The serialized format for a DataKey.
message DataKey {
// Used for both the FullyQualifiedName name and RelativeAssetPath path
- // Required
- optional string key_value = 1;
+ optional string key_value = 2;
// The resource type for FullyQualifiedNames
- optional string resource_type = 2;
- optional string key_package = 3;
- repeated string qualifiers = 4;
+ 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;
// Whether this DataKey is a reference to another DataKey.
- optional bool reference = 5;
+ optional bool reference = 7;
}
// The serialized format for a DataValue.
@@ -55,41 +51,30 @@ message DataValue {
// Required
optional uint32 source_id = 1;
- // 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;
+ // If xml_value is defined it's an xml value, otherwise, it's a file value.
+ optional DataValueXml xml_value= 2;
}
// A container for all the source information to be persisted.
message ProtoSource {
// Required
optional string filename = 1;
-}
-
-message XmlNamespaces {
- map<string, string> namespace = 1;
+ // The indexes of sources this source replaces.
+ repeated uint32 overwritten = 2;
}
// The container for a serialized xml value.
message DataValueXml {
enum XmlType {
- ARRAY = 1;
- ATTR = 2;
- ID = 3;
- PLURAL = 4;
- PUBLIC = 5;
- SIMPLE = 6;
- STYLEABLE = 7;
- STYLE = 8;
- RESOURCES_ATTRIBUTE = 9;
+ ARRAY = 0;
+ ATTR = 1;
+ ID = 2;
+ PLURAL = 3;
+ PUBLIC = 4;
+ SIMPLE = 5;
+ STYLEABLE = 6;
+ STYLE = 7;
+ RESOURCES_ATTRIBUTE = 8;
}
optional XmlType type = 1;
@@ -100,4 +85,5 @@ 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 f977c7b315..fb74fd2208 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,8 +29,6 @@ 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;
@@ -47,15 +45,14 @@ import javax.xml.stream.events.XMLEvent;
/**
* Represents an Android resource array.
*
- * <p>There are two flavors of Android Resource arrays:
- *
+ * 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 &lt;array&gt; tag.
- * <li>Integer array (http://developer.android.com/guide/topics/resources/more-resources
- * .html#IntegerArray) which are indicated by &lt;integer-array&gt; tag.
- * <li>String array (http://developer.android.com/guide/topics/resources/string-resource
- * .html#StringArray) which are indicated by &lt;string-array&gt; tag.
+ * <li>Typed arrays (http://developer.android.com/guide/topics/resources/more-resources
+ * .html#TypedArray) which which are indicated by a &lt;array&gt; tag.</li>
+ * <li>Integer array (http://developer.android.com/guide/topics/resources/more-resources
+ * .html#IntegerArray) which are indicated by &lt;integer-array&gt; tag.</li>
+ * <li>String array (http://developer.android.com/guide/topics/resources/string-resource
+ * .html#StringArray) which are indicated by &lt;string-array&gt; tag.</li>
* </ul>
*
* Both of these are accessed by R.array.&lt;name&gt; in java.
@@ -65,19 +62,9 @@ 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");
-
- @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. */
+ /**
+ * Enumerates the different types of array parentTags.
+ */
public enum ArrayType {
INTEGER_ARRAY(TAG_INTEGER_ARRAY),
ARRAY(TAG_ARRAY),
@@ -118,7 +105,7 @@ public class ArrayXmlResourceValue implements XmlResourceValue {
}
public static XmlResourceValue of(ArrayType arrayType, List<String> values) {
- return of(arrayType, values, ImmutableMap.of());
+ return of(arrayType, values, ImmutableMap.<String, String>of());
}
public static XmlResourceValue of(
@@ -151,20 +138,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
@@ -178,6 +165,21 @@ 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 96ac31c947..f2645ac0a3 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,16 +404,23 @@ public class AttrXmlResourceValue implements XmlResourceValue {
}
}
+ @SuppressWarnings("deprecation")
@Override
- public void writeTo(OutputStream out) throws IOException {
+ public int serializeTo(int sourceId, Namespaces namespaces, OutputStream output)
+ throws IOException {
+ SerializeFormat.DataValue.Builder builder =
+ XmlResourceValues.newSerializableDataValueBuilder(sourceId);
SerializeFormat.DataValueXml.Builder xmlValueBuilder =
SerializeFormat.DataValueXml.newBuilder();
- xmlValueBuilder.setType(SerializeFormat.DataValueXml.XmlType.ATTR);
+ xmlValueBuilder
+ .setType(SerializeFormat.DataValueXml.XmlType.ATTR)
+ .putAllNamespace(namespaces.asMap());
for (Entry<String, ResourceXmlAttrValue> entry : formats.entrySet()) {
xmlValueBuilder.putMappedXmlValue(
- entry.getKey(), entry.getValue().appendTo(SerializeFormat.DataValueXml.newBuilder()));
+ entry.getKey(), entry.getValue().appendTo(builder.getXmlValueBuilder()));
}
- xmlValueBuilder.build().writeDelimitedTo(out);
+ builder.setXmlValue(xmlValueBuilder);
+ return XmlResourceValues.serializeProtoDataValue(output, builder);
}
@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 e3fe65aa02..c3fb2faefa 100644
--- a/src/tools/android/java/com/google/devtools/build/android/xml/IdXmlResourceValue.java
+++ b/src/tools/android/java/com/google/devtools/build/android/xml/IdXmlResourceValue.java
@@ -20,8 +20,11 @@ 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;
@@ -91,8 +94,20 @@ public class IdXmlResourceValue implements XmlResourceValue {
}
@Override
- public void writeTo(OutputStream out) throws IOException {
- SerializeFormat.DataValueXml.newBuilder().setType(XmlType.ID).build().writeDelimitedTo(out);
+ 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();
}
@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 10da5ff225..82e8810139 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,13 +16,8 @@ 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;
@@ -44,23 +39,11 @@ 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>>, Writeable {
+public class Namespaces implements Iterable<Entry<String, String>> {
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 7c1b6532e3..b912cc141e 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,8 +24,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.XmlType;
+import com.google.protobuf.CodedOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.HashMap;
@@ -155,13 +157,23 @@ public class PluralXmlResourceValue implements XmlResourceValue {
}
@Override
- public void writeTo(OutputStream out) throws IOException {
- SerializeFormat.DataValueXml.newBuilder()
- .setType(XmlType.PLURAL)
- .putAllAttribute(attributes)
- .putAllMappedStringValue(values)
- .build()
- .writeDelimitedTo(out);
+ 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();
}
@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 c1e3eb40a0..af4184d88a 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,11 +19,13 @@ 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;
@@ -31,7 +33,6 @@ 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 &lt;public&gt; xml tag.
@@ -138,19 +139,23 @@ public class PublicXmlResourceValue implements XmlResourceValue {
}
@Override
- 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);
+ 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);
}
@Override
@@ -173,7 +178,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 75a6c0207e..a54667ecdb 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,7 +21,9 @@ 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;
@@ -38,15 +40,13 @@ 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,7 +92,9 @@ 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;
@@ -112,13 +114,19 @@ public class ResourcesAttribute implements XmlResourceValue {
}
@Override
- 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);
+ 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);
}
@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 55bef85bc1..32147cd49c 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,10 +27,9 @@ 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;
@@ -42,7 +41,8 @@ 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,27 +66,106 @@ 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. */
+ /** Provides an enumeration resource type and simple value validation. */
public enum Type {
- 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);
+ 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;
+ }
+ };
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())) {
@@ -98,7 +177,8 @@ 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())));
}
}
@@ -120,7 +200,8 @@ 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);
}
@@ -184,11 +265,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";
@@ -201,7 +282,10 @@ 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
@@ -210,20 +294,23 @@ public class SimpleXmlResourceValue implements XmlResourceValue {
}
@Override
- public void writeTo(OutputStream out) throws IOException {
- final Builder builder =
- DataValueXml.newBuilder()
- .setType(XmlType.SIMPLE)
- .setValueType(valueType.name())
+ 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)
// 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) {
- builder.setValue(value);
+ xmlValueBuilder.setValue(value);
}
-
- builder.build().writeDelimitedTo(out);
+ builder.setXmlValue(xmlValueBuilder.setValueType(valueType.name()));
+ return XmlResourceValues.serializeProtoDataValue(output, builder);
}
@Override
@@ -255,7 +342,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 6929c9d588..5cd0f6e0a5 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,6 +24,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.XmlType;
import java.io.IOException;
@@ -38,7 +39,8 @@ 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 &lt;attr&gt; 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
@@ -152,15 +154,19 @@ public class StyleXmlResourceValue implements XmlResourceValue {
}
@Override
- public void writeTo(OutputStream out) throws IOException {
+ public int serializeTo(int sourceId, Namespaces namespaces, OutputStream output)
+ throws IOException {
SerializeFormat.DataValueXml.Builder xmlValueBuilder =
SerializeFormat.DataValueXml.newBuilder()
.setType(XmlType.STYLE)
+ .putAllNamespace(namespaces.asMap())
.putAllMappedStringValue(values);
if (parent != null) {
xmlValueBuilder.setValue(parent);
}
- xmlValueBuilder.build().writeDelimitedTo(out);
+ return XmlResourceValues.serializeProtoDataValue(
+ output,
+ XmlResourceValues.newSerializableDataValueBuilder(sourceId).setXmlValue(xmlValueBuilder));
}
@Override
@@ -189,7 +195,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 5006600a48..c9661690cf 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,6 +27,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.XmlType;
import java.io.IOException;
@@ -37,7 +38,6 @@ 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,11 +65,22 @@ public class StyleableXmlResourceValue implements XmlResourceValue {
static final Function<Entry<FullyQualifiedName, Boolean>, SerializeFormat.DataKey>
FULLY_QUALIFIED_NAME_TO_DATA_KEY =
- input -> input.getKey().toSerializedBuilder().setReference(input.getValue()).build();
+ new Function<Entry<FullyQualifiedName, Boolean>, SerializeFormat.DataKey>() {
+ @Override
+ public SerializeFormat.DataKey apply(Entry<FullyQualifiedName, Boolean> input) {
+ return input.getKey().toSerializedBuilder().setReference(input.getValue()).build();
+ }
+ };
static final Function<SerializeFormat.DataKey, Entry<FullyQualifiedName, Boolean>>
DATA_KEY_TO_FULLY_QUALIFIED_NAME =
- input -> new SimpleEntry<>(FullyQualifiedName.fromProto(input), input.getReference());
+ 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());
+ }
+ };
private final ImmutableMap<FullyQualifiedName, Boolean> attrs;
@@ -134,17 +145,17 @@ public class StyleableXmlResourceValue implements XmlResourceValue {
}
@Override
- 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 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 static XmlResourceValue from(SerializeFormat.DataValueXml proto) {
@@ -191,12 +202,12 @@ public class StyleableXmlResourceValue implements XmlResourceValue {
/**
* Combines this instance with another {@link StyleableXmlResourceValue}.
*
- * <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.
+ * 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}.