aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Googler <noreply@google.com>2016-04-19 22:07:47 +0000
committerGravatar Damien Martin-Guillerez <dmarting@google.com>2016-04-20 11:17:11 +0000
commit66cf13874a3c8f866aeace2d59231e30ca4a5032 (patch)
treee926e87be0c3f8ff8322cd2be20e5a534085010f
parent9b35d8a81b0d5cb92a22e7d9c7bf30a834711d7f (diff)
4 of 5: Serialization of UnwrittenMergedAndroidData.
Adding AndroidDataSerializer, the serialize_format proto, and KeyValueConsumers (utility class for keeping consumers straight). The serializtion is a bit more manual as previous experience has proven to me that simply writing all the resources into a proto map and pulling them out is not performant in the least. So, the serializer stores each message independent, the keys and then the values allowing for potential lazy loading and other optimizations in the future. Also adds tests for parsing and writing style resources. -- MOS_MIGRATED_REVID=120274904
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/AndroidDataSerializer.java201
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/AndroidDataWriter.java8
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/AndroidDataWritingVisitor.java2
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/BUILD1
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/DataKey.java16
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/DataResourceXml.java55
-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.java25
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/FullyQualifiedName.java46
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/KeyValueConsumers.java43
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/RelativeAssetPath.java20
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/UnwrittenMergedAndroidData.java36
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/XmlResourceValue.java7
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/XmlResourceValues.java63
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/xml/ArrayXmlResourceValue.java31
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/xml/AttrXmlResourceValue.java140
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/xml/IdXmlResourceValue.java29
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/xml/PluralXmlResourceValue.java35
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/xml/SimpleXmlResourceValue.java36
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/xml/StyleXmlResourceValue.java33
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/xml/StyleableXmlResourceValue.java35
21 files changed, 788 insertions, 81 deletions
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
new file mode 100644
index 0000000000..e4d7cead9d
--- /dev/null
+++ b/src/tools/android/java/com/google/devtools/build/android/AndroidDataSerializer.java
@@ -0,0 +1,201 @@
+// 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.Preconditions;
+import com.google.common.collect.Ordering;
+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.protobuf.InvalidProtocolBufferException;
+
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayOutputStream;
+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.StandardOpenOption;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.NavigableMap;
+import java.util.NavigableSet;
+import java.util.TreeMap;
+
+/**
+ * Writes an {@link UnwrittenMergedAndroidData} to a binary file.
+ */
+public class AndroidDataSerializer {
+
+ // TODO(corysmith): We might need a more performant comparasion methodology than toString.
+ private final NavigableMap<DataKey, DataValue> primary =
+ new TreeMap<DataKey, DataValue>(Ordering.usingToString());
+
+ private final NavigableMap<DataKey, DataValue> deps =
+ new TreeMap<DataKey, DataValue>(Ordering.usingToString());
+ private Path manifest;
+
+ public static AndroidDataSerializer create() {
+ return new AndroidDataSerializer();
+ }
+
+ private AndroidDataSerializer() {}
+
+ /**
+ * Writes all of the collected DataKey -> DataValue.
+ *
+ * The binary format will be:
+ * <pre>
+ * {@link Header}
+ * {@link com.google.devtools.build.android.proto.SerializeFormat.DataKey} primary...
+ * {@link com.google.devtools.build.android.proto.SerializeFormat.DataValue} primary...
+ * {@link com.google.devtools.build.android.proto.SerializeFormat.DataKey} transitive...
+ * {@link com.google.devtools.build.android.proto.SerializeFormat.DataValue} transitive...
+ * </pre>
+ *
+ * The key and values will be written in comparable order, allowing for the optimization of not
+ * converting the DataValue from binary, only writing it into a merged serialized binary.
+ */
+ public void flushTo(Path out) throws IOException {
+ Preconditions.checkNotNull(manifest, "Manifest is required to serialize AndroidData.");
+ try (OutputStream outStream =
+ new BufferedOutputStream(
+ Files.newOutputStream(out, StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE))) {
+ // Set the header for the deserialization process.
+ Header.newBuilder()
+ .setPrimaryEntries(primary.size())
+ .setTransitiveEntries(deps.size())
+ .setManifestPath(ProtoSource.newBuilder().setFilename(manifest.toString()))
+ .build()
+ .writeDelimitedTo(outStream);
+
+ writeKeyValuesTo(primary, outStream);
+ writeKeyValuesTo(deps, outStream);
+ }
+ }
+
+ private void writeKeyValuesTo(NavigableMap<DataKey, DataValue> map, OutputStream outStream)
+ throws IOException {
+ NavigableSet<DataKey> keys = map.navigableKeySet();
+ int[] orderedValueSizes = new int[keys.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 (DataKey key : keys) {
+ orderedValueSizes[valueSizeIndex++] = map.get(key).serializeTo(key, valuesOutputStream);
+ }
+ // Serialize all the keys in sorted order
+ valueSizeIndex = 0;
+ for (DataKey key : map.navigableKeySet()) {
+ key.serializeTo(outStream, orderedValueSizes[valueSizeIndex]);
+ }
+ // write the values to the output stream.
+ outStream.write(valuesOutputStream.toByteArray());
+ }
+
+ /**
+ * Reads the serialized {@link DataKey} and {@link DataValue} to the {@link KeyValueConsumers}.
+ *
+ * @param inPath The path to the serialized protocol buffer.
+ * @param primaryConsumers The {@link KeyValueConsumers} for the primary {@link DataKey} ->
+ * {@link DataValue} entries.
+ * @param transitiveConsumers The {@link KeyValueConsumers} for the transitive {@link DataKey} ->
+ * {@link DataValue} entries.
+ * @returns The manifest path for the serialized data.
+ * @throws IOException when io operations fail.
+ * @throws InvalidProtocolBufferException When the inPath is not a valid proto buffer -- e.g.
+ * legacy R.txts generated from android_resources.
+ */
+ public Path read(
+ Path inPath, KeyValueConsumers primaryConsumers, KeyValueConsumers transitiveConsumers)
+ throws IOException, InvalidProtocolBufferException {
+ try (InputStream in = Files.newInputStream(inPath, StandardOpenOption.READ)) {
+ FileSystem currentFileSystem = inPath.getFileSystem();
+ Header header = Header.parseDelimitedFrom(in);
+ readEntriesSegment(primaryConsumers, in, currentFileSystem, header.getPrimaryEntries());
+ readEntriesSegment(transitiveConsumers, in, currentFileSystem, header.getTransitiveEntries());
+ // Assuming that the inPath will be the same filesystem as the manifest.
+ // It seems highly unlikely they would be different.
+ Path manifestPath = inPath.getFileSystem().getPath(header.getManifestPath().getFilename());
+ Preconditions.checkArgument(
+ Files.exists(manifestPath), "The manifest %s, does not exist.", manifestPath);
+ return manifestPath;
+ }
+ }
+
+ private void readEntriesSegment(
+ KeyValueConsumers consumers,
+ InputStream in,
+ FileSystem currentFileSystem,
+ int numberOfEntries)
+ throws IOException, InvalidProtocolBufferException {
+ Map<DataKey, KeyValueConsumer<DataKey, ? extends DataValue>> keys = new LinkedHashMap<>();
+ 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,
+ FullyQualifiedName.isOverwritable(resourceName)
+ ? consumers.overwritingConsumer
+ : consumers.nonOverwritingConsumer);
+ } else {
+ keys.put(RelativeAssetPath.fromProto(protoKey, currentFileSystem), consumers.assetConsumer);
+ }
+ }
+
+ // 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);
+ 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 consume.
+ // 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.consume(entry.getKey(), DataResourceXml.from(protoValue, currentFileSystem));
+ } else {
+ @SuppressWarnings("unchecked")
+ KeyValueConsumer<DataKey, DataValue> value =
+ (KeyValueConsumer<DataKey, DataValue>) entry.getValue();
+ value.consume(entry.getKey(), DataValueFile.from(protoValue, currentFileSystem));
+ }
+ }
+ }
+
+ /** Queues the manifest for serialization. */
+ public void serializeManifest(Path manifest) {
+ this.manifest = manifest;
+ }
+
+ /** Queues the key and value for serialization as a primary entry. */
+ public void serializeToPrimary(DataKey key, DataValue value) {
+ primary.put(key, value);
+ }
+
+ /** Queues the key and value for serialization as a transitive entry. */
+ public void serializeToTransitive(DataKey key, DataValue value) {
+ deps.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 b954aff3bc..c025871aad 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
@@ -88,10 +88,10 @@ public class AndroidDataWriter implements Flushable, AndroidDataWritingVisitor {
public void flush() throws IOException {
Path values = Files.createDirectories(resourceDirectory().resolve("values"));
try (FileChannel channel =
- FileChannel.open(
- values.resolve("values.xml"),
- StandardOpenOption.CREATE_NEW,
- StandardOpenOption.WRITE)) {
+ FileChannel.open(
+ values.resolve("values.xml"),
+ StandardOpenOption.CREATE_NEW,
+ StandardOpenOption.WRITE)) {
channel.write(ByteBuffer.wrap("<resources>".getBytes(UTF_8)));
for (FullyQualifiedName key : Ordering.natural().sortedCopy(valueFragments.keySet())) {
for (String line : valueFragments.get(key)) {
diff --git a/src/tools/android/java/com/google/devtools/build/android/AndroidDataWritingVisitor.java b/src/tools/android/java/com/google/devtools/build/android/AndroidDataWritingVisitor.java
index 40abe2f184..55ce652bf1 100644
--- a/src/tools/android/java/com/google/devtools/build/android/AndroidDataWritingVisitor.java
+++ b/src/tools/android/java/com/google/devtools/build/android/AndroidDataWritingVisitor.java
@@ -27,6 +27,7 @@ public interface AndroidDataWritingVisitor {
/**
* Copies the source asset to the relative destination path.
+ *
* @param source The source file to copy.
* @param relativeDestinationPath The relative destination path to write the asset to.
* @throws IOException if there are errors during copying.
@@ -35,6 +36,7 @@ public interface AndroidDataWritingVisitor {
/**
* Copies the source resource to the relative destination path.
+ *
* @param source The source file to copy.
* @param relativeDestinationPath The relative destination path to write the resource to.
* @throws IOException if there are errors during copying.
diff --git a/src/tools/android/java/com/google/devtools/build/android/BUILD b/src/tools/android/java/com/google/devtools/build/android/BUILD
index d794f265e3..4caa6557a9 100644
--- a/src/tools/android/java/com/google/devtools/build/android/BUILD
+++ b/src/tools/android/java/com/google/devtools/build/android/BUILD
@@ -54,6 +54,7 @@ java_library(
"//third_party:asm",
"//third_party:guava",
"//third_party:jsr305",
+ "//third_party/bazel/src/tools/android/java/com/google/devtools/build/android/proto:serialize_format_proto",
"//third_party/protobuf",
],
)
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 ef447e0e25..e363bd74c4 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,8 +13,11 @@
// limitations under the License.
package com.google.devtools.build.android;
+import java.io.IOException;
+import java.io.OutputStream;
+
/**
- * A general marker interface for resource and asset keys.
+ * A general interface for resource and asset keys.
*
* Resource and Assets are merged on the basis of a key value:
*
@@ -23,4 +26,13 @@ package com.google.devtools.build.android;
*
* For Assets, it is the asset path from the assets directory.
*/
-public interface DataKey {}
+public interface 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;
+}
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 21e8c92657..8e79ed4738 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
@@ -17,14 +17,25 @@ import static com.android.resources.ResourceType.DECLARE_STYLEABLE;
import static com.android.resources.ResourceType.ID;
import com.google.common.base.MoreObjects;
+import com.google.common.base.Preconditions;
import com.google.devtools.build.android.FullyQualifiedName.Factory;
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.PluralXmlResourceValue;
+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.InvalidProtocolBufferException;
import com.android.resources.ResourceType;
import java.io.IOException;
+import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
+import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Objects;
@@ -38,8 +49,9 @@ import javax.xml.stream.events.StartElement;
/**
* Represents an Android Resource defined in the xml and value folder.
*
- * <p>Basically, if the resource is defined inside a &lt;resources&gt; tag, this class will
- * handle it. Layouts are treated separately as they don't declare anything besides ids.
+ * <p>
+ * Basically, if the resource is defined inside a &lt;resources&gt; tag, this class will handle it.
+ * Layouts are treated separately as they don't declare anything besides ids.
*/
public class DataResourceXml implements DataResource {
@@ -53,7 +65,7 @@ public class DataResourceXml implements DataResource {
* @param path The path to the xml resource to be parsed.
* @param fqnFactory Used to create {@link FullyQualifiedName}s from the resource names.
* @param overwritingConsumer A consumer for overwritable {@link DataResourceXml}s.
- * @param nonOverwritingConsumer A consumer for nonoverwritable {@link DataResourceXml}s.
+ * @param nonOverwritingConsumer A consumer for nonoverwritable {@link DataResourceXml}s.
* @throws XMLStreamException Thrown with the resource format is invalid.
* @throws FactoryConfigurationError Thrown with the {@link XMLInputFactory} is misconfigured.
* @throws IOException Thrown when there is an error reading a file.
@@ -91,6 +103,36 @@ public class DataResourceXml implements DataResource {
}
}
+ public static DataValue from(SerializeFormat.DataValue protoValue, FileSystem currentFileSystem)
+ throws InvalidProtocolBufferException {
+ return of(
+ currentFileSystem.getPath(protoValue.getSource().getFilename()),
+ valueFromProto(protoValue.getXmlValue()));
+ }
+
+ 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 STYLE:
+ return StyleXmlResourceValue.from(proto);
+ case STYLEABLE:
+ return StyleableXmlResourceValue.from(proto);
+ default:
+ throw new IllegalArgumentException();
+ }
+ }
+
private static XmlResourceValue parseXmlElements(
ResourceType resourceType, XMLEventReader eventReader, StartElement start)
throws XMLStreamException {
@@ -109,7 +151,7 @@ public class DataResourceXml implements DataResource {
case BOOL:
case COLOR:
case DIMEN:
- return XmlResourceValues.parseSimple(eventReader, resourceType);
+ return XmlResourceValues.parseSimple(eventReader, resourceType, start.getName());
default:
throw new XMLStreamException(
String.format("Unhandled resourceType %s", resourceType), start.getLocation());
@@ -163,4 +205,9 @@ public class DataResourceXml implements DataResource {
public void writeResource(FullyQualifiedName key, AndroidDataWritingVisitor mergedDataWriter) {
xml.write(key, source, mergedDataWriter);
}
+
+ @Override
+ public int serializeTo(DataKey key, OutputStream outStream) throws IOException {
+ return xml.serializeTo(source, outStream);
+ }
}
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 ded0ce5819..f30916f1f6 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,6 +13,8 @@
// limitations under the License.
package com.google.devtools.build.android;
+import java.io.IOException;
+import java.io.OutputStream;
import java.nio.file.Path;
/**
@@ -26,4 +28,9 @@ public interface DataValue {
* Provides the Path to the file from which the DataValue was derived.
*/
Path source();
+
+ /**
+ * Serializes to a supplied stream and returns the number of bytes written.
+ */
+ int serializeTo(DataKey key, OutputStream output) throws IOException;
}
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 b92002f783..91fcc57dc4 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,8 +14,12 @@
package com.google.devtools.build.android;
import com.google.common.base.MoreObjects;
+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.FileSystem;
import java.nio.file.Path;
import java.util.Objects;
@@ -36,6 +40,14 @@ public class DataValueFile implements DataResource, DataAsset {
return new DataValueFile(source);
}
+ /**
+ * Creates a {@link DataValueFile} from a {@link SerializeFormat.DataValue}.
+ */
+ public static DataValueFile from(
+ SerializeFormat.DataValue protoValue, FileSystem currentFileSystem) {
+ return of(currentFileSystem.getPath(protoValue.getSource().getFilename()));
+ }
+
@Override
public int hashCode() {
return source.hashCode();
@@ -71,8 +83,19 @@ public class DataValueFile implements DataResource, DataAsset {
throws IOException {
mergedDataWriter.copyResource(source, key.toPathString(getSourceExtension()));
}
-
+
+ @Override
+ public int serializeTo(DataKey key, OutputStream output) throws IOException {
+ SerializeFormat.DataValue.Builder builder = SerializeFormat.DataValue.newBuilder();
+ SerializeFormat.DataValue value =
+ builder.setSource(builder.getSourceBuilder().setFilename(source.toString())).build();
+ value.writeDelimitedTo(output);
+ return CodedOutputStream.computeUInt32SizeNoTag(value.getSerializedSize())
+ + value.getSerializedSize();
+ }
+
private String getSourceExtension() {
+ // TODO(corysmith): Switch to a filename parser utility.
String fileName = source.getFileName().toString();
int extensionStart = fileName.lastIndexOf('.');
if (extensionStart > 0) {
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 303196cbfa..73f9da2a20 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
@@ -18,9 +18,12 @@ import com.google.common.base.MoreObjects;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Ordering;
+import com.google.devtools.build.android.proto.SerializeFormat;
import com.android.resources.ResourceType;
+import java.io.IOException;
+import java.io.OutputStream;
import java.nio.file.Paths;
import java.util.List;
import java.util.Objects;
@@ -38,6 +41,7 @@ import javax.annotation.concurrent.Immutable;
@Immutable
public class FullyQualifiedName implements DataKey, Comparable<FullyQualifiedName> {
public static final String DEFAULT_PACKAGE = "res-auto";
+ private static final Joiner DASH_JOINER = Joiner.on('-');
private final String pkg;
private final ImmutableList<String> qualifiers;
@@ -47,8 +51,8 @@ public class FullyQualifiedName implements DataKey, Comparable<FullyQualifiedNam
/**
* Returns a string path representation of the FullyQualifiedName.
*
- * Non-values Android Resource have a well defined file layout: From the resource directory,
- * they reside in &lt;resource type&gt;[-&lt;qualifier&gt;]/&lt;resource name&gt;[.extension]
+ * Non-values Android Resource have a well defined file layout: From the resource directory, they
+ * reside in &lt;resource type&gt;[-&lt;qualifier&gt;]/&lt;resource name&gt;[.extension]
*
* @param sourceExtension The extension of the resource represented by the FullyQualifiedName
* @return A string representation of the FullyQualifiedName with the provided extension.
@@ -56,12 +60,11 @@ public class FullyQualifiedName implements DataKey, Comparable<FullyQualifiedNam
public String toPathString(String sourceExtension) {
// TODO(corysmith): Does the extension belong in the FullyQualifiedName?
return Paths.get(
- Joiner.on("-")
- .join(
- ImmutableList.<String>builder()
- .add(resourceType.getName())
- .addAll(qualifiers)
- .build()),
+ DASH_JOINER.join(
+ ImmutableList.<String>builder()
+ .add(resourceType.getName())
+ .addAll(qualifiers)
+ .build()),
resourceName + sourceExtension)
.toString();
}
@@ -106,7 +109,7 @@ public class FullyQualifiedName implements DataKey, Comparable<FullyQualifiedNam
}
/**
- * Parses a FullyQualifiedName from a string .
+ * Parses a FullyQualifiedName from a string.
*
* @param raw A string in the expected format from
* [&lt;package&gt;:]&lt;ResourceType.name&gt;/&lt;resource name&gt;.
@@ -131,8 +134,13 @@ public class FullyQualifiedName implements DataKey, Comparable<FullyQualifiedNam
}
}
+ public static boolean isOverwritable(FullyQualifiedName name) {
+ return !(name.resourceType == ResourceType.ID || name.resourceType == ResourceType.STYLEABLE);
+ }
+
/**
* Creates a new FullyQualifiedName with sorted qualifiers.
+ *
* @param pkg The resource package of the name. If unknown the default should be "res-auto"
* @param qualifiers The resource qualifiers of the name, such as "en" or "xhdpi".
* @param resourceType The resource type of the name.
@@ -145,6 +153,14 @@ public class FullyQualifiedName implements DataKey, Comparable<FullyQualifiedNam
pkg, Ordering.natural().immutableSortedCopy(qualifiers), resourceType, resourceName);
}
+ public static FullyQualifiedName fromProto(SerializeFormat.DataKey protoKey) {
+ return of(
+ protoKey.getKeyPackage(),
+ protoKey.getQualifiersList(),
+ ResourceType.valueOf(protoKey.getResourceType()),
+ protoKey.getKeyValue());
+ }
+
private FullyQualifiedName(
String pkg,
ImmutableList<String> qualifiers,
@@ -215,4 +231,16 @@ public class FullyQualifiedName implements DataKey, Comparable<FullyQualifiedNam
}
return 0;
}
+
+ @Override
+ public void serializeTo(OutputStream out, int valueSize) throws IOException {
+ SerializeFormat.DataKey.newBuilder()
+ .setKeyPackage(pkg)
+ .setValueSize(valueSize)
+ .setResourceType(resourceType.getName().toUpperCase())
+ .addAllQualifiers(qualifiers)
+ .setKeyValue(resourceName)
+ .build()
+ .writeDelimitedTo(out);
+ }
}
diff --git a/src/tools/android/java/com/google/devtools/build/android/KeyValueConsumers.java b/src/tools/android/java/com/google/devtools/build/android/KeyValueConsumers.java
new file mode 100644
index 0000000000..86789b27b4
--- /dev/null
+++ b/src/tools/android/java/com/google/devtools/build/android/KeyValueConsumers.java
@@ -0,0 +1,43 @@
+// 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.devtools.build.android.ParsedAndroidData.KeyValueConsumer;
+
+/**
+ * A group of {@link KeyValueConsumer}s for each DataValue type.
+ *
+ * This class acts as a parameter object for organizing the common grouping of consumer instances.
+ */
+class KeyValueConsumers {
+ static KeyValueConsumers of(
+ KeyValueConsumer<DataKey, DataResource> overwritingConsumer,
+ KeyValueConsumer<DataKey, DataResource> nonOverwritingConsumer,
+ KeyValueConsumer<DataKey, DataAsset> assetConsumer) {
+ return new KeyValueConsumers(overwritingConsumer, nonOverwritingConsumer, assetConsumer);
+ }
+
+ final KeyValueConsumer<DataKey, DataResource> overwritingConsumer;
+ final KeyValueConsumer<DataKey, DataResource> nonOverwritingConsumer;
+ final KeyValueConsumer<DataKey, DataAsset> assetConsumer;
+
+ private KeyValueConsumers(
+ KeyValueConsumer<DataKey, DataResource> overwritingConsumer,
+ KeyValueConsumer<DataKey, DataResource> nonOverwritingConsumer,
+ KeyValueConsumer<DataKey, DataAsset> assetConsumer) {
+ this.overwritingConsumer = overwritingConsumer;
+ this.nonOverwritingConsumer = nonOverwritingConsumer;
+ this.assetConsumer = assetConsumer;
+ }
+}
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 609216ff64..ede9369cf5 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
@@ -15,7 +15,11 @@ package com.google.devtools.build.android;
import com.google.common.base.MoreObjects;
import com.google.common.base.Preconditions;
+import com.google.devtools.build.android.proto.SerializeFormat;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.file.FileSystem;
import java.nio.file.Path;
import java.util.Objects;
@@ -55,6 +59,13 @@ public class RelativeAssetPath implements DataKey, Comparable<RelativeAssetPath>
}
}
+ /**
+ * Reconstitutes the relative asset path from a protocol buffer and {@link FileSystem}.
+ */
+ static RelativeAssetPath fromProto(SerializeFormat.DataKey serialized, FileSystem fileSystem) {
+ return of(fileSystem.getPath(serialized.getKeyValue()));
+ }
+
private final Path relativeAssetPath;
private RelativeAssetPath(Path relativeAssetPath) {
@@ -95,4 +106,13 @@ public class RelativeAssetPath implements DataKey, Comparable<RelativeAssetPath>
public int compareTo(RelativeAssetPath relativeAssetPath) {
return this.relativeAssetPath.compareTo(relativeAssetPath.relativeAssetPath);
}
+
+ @Override
+ public void serializeTo(OutputStream output, int valueSize) throws IOException {
+ SerializeFormat.DataKey.newBuilder()
+ .setKeyValue(relativeAssetPath.toString())
+ .setValueSize(valueSize)
+ .build()
+ .writeDelimitedTo(output);
+ }
}
diff --git a/src/tools/android/java/com/google/devtools/build/android/UnwrittenMergedAndroidData.java b/src/tools/android/java/com/google/devtools/build/android/UnwrittenMergedAndroidData.java
index 633bd9403a..61248d031f 100644
--- a/src/tools/android/java/com/google/devtools/build/android/UnwrittenMergedAndroidData.java
+++ b/src/tools/android/java/com/google/devtools/build/android/UnwrittenMergedAndroidData.java
@@ -28,7 +28,7 @@ public class UnwrittenMergedAndroidData {
private final Path manifest;
private final ParsedAndroidData primary;
- private final ParsedAndroidData deps;
+ private final ParsedAndroidData transitive;
public static UnwrittenMergedAndroidData of(
Path manifest, ParsedAndroidData resources, ParsedAndroidData deps) {
@@ -36,14 +36,15 @@ public class UnwrittenMergedAndroidData {
}
private UnwrittenMergedAndroidData(
- Path manifest, ParsedAndroidData primary, ParsedAndroidData deps) {
+ Path manifest, ParsedAndroidData primary, ParsedAndroidData transitive) {
this.manifest = manifest;
this.primary = primary;
- this.deps = deps;
+ this.transitive = transitive;
}
/**
* Writes the android data to the filesystem.
+ *
* @param mergedDataWriter Destination writer.
* @return A MergedAndroidData that is ready for further tool processing.
* @throws IOException when something goes wrong while writing.
@@ -51,7 +52,7 @@ public class UnwrittenMergedAndroidData {
public MergedAndroidData write(AndroidDataWriter mergedDataWriter) throws IOException {
try {
writeParsedAndroidData(primary, mergedDataWriter);
- writeParsedAndroidData(deps, mergedDataWriter);
+ writeParsedAndroidData(transitive, mergedDataWriter);
return new MergedAndroidData(
mergedDataWriter.resourceDirectory(),
mergedDataWriter.assetDirectory(),
@@ -84,7 +85,7 @@ public class UnwrittenMergedAndroidData {
return MoreObjects.toStringHelper(this)
.add("manifest", manifest)
.add("primary", primary)
- .add("deps", deps)
+ .add("transitive", transitive)
.toString();
}
@@ -99,12 +100,12 @@ public class UnwrittenMergedAndroidData {
UnwrittenMergedAndroidData that = (UnwrittenMergedAndroidData) other;
return Objects.equals(manifest, that.manifest)
&& Objects.equals(primary, that.primary)
- && Objects.equals(deps, that.deps);
+ && Objects.equals(transitive, that.transitive);
}
@Override
public int hashCode() {
- return Objects.hash(manifest, primary, deps);
+ return Objects.hash(manifest, primary, transitive);
}
@VisibleForTesting
@@ -118,7 +119,24 @@ public class UnwrittenMergedAndroidData {
}
@VisibleForTesting
- ParsedAndroidData getDeps() {
- return deps;
+ ParsedAndroidData getTransitive() {
+ return transitive;
+ }
+
+ public void serializeTo(AndroidDataSerializer serializer) {
+ serializer.serializeManifest(manifest);
+ for (Entry<DataKey, DataAsset> entry : primary.iterateAssetEntries()) {
+ serializer.serializeToPrimary(entry.getKey(), entry.getValue());
+ }
+ for (Entry<DataKey, DataResource> entry : primary.iterateDataResourceEntries()) {
+ serializer.serializeToPrimary(entry.getKey(), entry.getValue());
+ }
+
+ for (Entry<DataKey, DataAsset> entry : transitive.iterateAssetEntries()) {
+ serializer.serializeToTransitive(entry.getKey(), entry.getValue());
+ }
+ for (Entry<DataKey, DataResource> entry : transitive.iterateDataResourceEntries()) {
+ serializer.serializeToTransitive(entry.getKey(), entry.getValue());
+ }
}
}
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 1a3cfd1f66..dc916db8d4 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,6 +13,8 @@
// limitations under the License.
package com.google.devtools.build.android;
+import java.io.IOException;
+import java.io.OutputStream;
import java.nio.file.Path;
/**
@@ -27,4 +29,9 @@ public interface XmlResourceValue {
* @param mergedDataWriter The target writer.
*/
void write(FullyQualifiedName key, Path source, AndroidDataWritingVisitor mergedDataWriter);
+
+ /**
+ * Serializes the resource value to the OutputStream and returns the bytes written.
+ */
+ int serializeTo(Path source, OutputStream out) throws IOException;
}
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 f2525712ea..1ed13c1057 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
@@ -13,17 +13,22 @@
// limitations under the License.
package com.google.devtools.build.android;
+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.AttrXmlResourceValue;
import com.google.devtools.build.android.xml.IdXmlResourceValue;
import com.google.devtools.build.android.xml.PluralXmlResourceValue;
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.android.resources.ResourceType;
+import java.io.IOException;
+import java.io.OutputStream;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashMap;
@@ -40,8 +45,9 @@ import javax.xml.stream.events.XMLEvent;
/**
* {@link XmlResourceValues} provides methods for getting {@link XmlResourceValue} derived classes.
*
- * <p>Acts a static factory class containing the general xml parsing logic for resources
- * that are declared inside the &lt;resources&gt; tag.
+ * <p>
+ * Acts a static factory class containing the general xml parsing logic for resources that are
+ * declared inside the &lt;resources&gt; tag.
*/
public class XmlResourceValues {
@@ -120,10 +126,46 @@ public class XmlResourceValues {
return IdXmlResourceValue.of();
}
- static XmlResourceValue parseSimple(XMLEventReader eventReader, ResourceType resourceType)
+ static XmlResourceValue parseSimple(
+ XMLEventReader eventReader, ResourceType resourceType, QName startTag)
throws XMLStreamException {
+ StringBuilder contents = new StringBuilder();
+ while (!isEndTag(eventReader.peek(), startTag)) {
+ XMLEvent xmlEvent = eventReader.nextEvent();
+ if (xmlEvent.isCharacters()) {
+ contents.append(xmlEvent.asCharacters().getData());
+ } else if (xmlEvent.isStartElement()) {
+ QName name = xmlEvent.asStartElement().getName();
+ contents.append("<");
+ if (!name.getNamespaceURI().isEmpty()) {
+ contents
+ .append(name.getPrefix())
+ .append(':')
+ .append(name.getLocalPart())
+ .append(' ')
+ .append("xmlns:")
+ .append(name.getPrefix())
+ .append("='")
+ .append(name.getNamespaceURI())
+ .append("'");
+ } else {
+ contents.append(name.getLocalPart());
+ }
+ contents.append(">");
+ } else if (xmlEvent.isEndElement()) {
+ QName name = xmlEvent.asEndElement().getName();
+ contents.append("</");
+ if (!name.getNamespaceURI().isEmpty()) {
+ contents.append(name.getPrefix()).append(':').append(name.getLocalPart());
+ } else {
+ contents.append(name.getLocalPart());
+ }
+ contents.append(">");
+ }
+ }
+ Preconditions.checkArgument(eventReader.nextEvent().asEndElement().getName().equals(startTag));
return SimpleXmlResourceValue.of(
- SimpleXmlResourceValue.Type.from(resourceType), eventReader.getElementText());
+ SimpleXmlResourceValue.Type.from(resourceType), contents.toString());
}
/* XML helper methods follow. */
@@ -209,4 +251,17 @@ public class XmlResourceValues {
}
return false;
}
+
+ public static SerializeFormat.DataValue.Builder newProtoDataBuilder(Path source) {
+ SerializeFormat.DataValue.Builder builder = SerializeFormat.DataValue.newBuilder();
+ return builder.setSource(builder.getSourceBuilder().setFilename(source.toString()));
+ }
+
+ public static int serializeProtoDataValue(
+ OutputStream output, SerializeFormat.DataValue.Builder builder) throws IOException {
+ SerializeFormat.DataValue value = builder.build();
+ value.writeDelimitedTo(output);
+ return CodedOutputStream.computeUInt32SizeNoTag(value.getSerializedSize())
+ + value.getSerializedSize();
+ }
}
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 2e1a10330f..edb30267f7 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
@@ -22,7 +22,10 @@ import com.google.devtools.build.android.AndroidDataWritingVisitor;
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;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
@@ -42,12 +45,12 @@ import javax.xml.stream.events.XMLEvent;
*
* 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>
- * <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>
+ * <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.
@@ -117,6 +120,10 @@ public class ArrayXmlResourceValue implements XmlResourceValue {
return new ArrayXmlResourceValue(arrayType, ImmutableList.copyOf(values));
}
+ public static XmlResourceValue from(SerializeFormat.DataValueXml proto) {
+ return of(ArrayType.valueOf(proto.getValueType()), proto.getListValueList());
+ }
+
@Override
public void write(
FullyQualifiedName key, Path source, AndroidDataWritingVisitor mergedDataWriter) {
@@ -129,6 +136,18 @@ public class ArrayXmlResourceValue implements XmlResourceValue {
}
@Override
+ public int serializeTo(Path source, OutputStream output) throws IOException {
+ return XmlResourceValues.serializeProtoDataValue(
+ output,
+ XmlResourceValues.newProtoDataBuilder(source)
+ .setXmlValue(
+ SerializeFormat.DataValueXml.newBuilder()
+ .addAllListValue(values)
+ .setType(SerializeFormat.DataValueXml.XmlType.ARRAY)
+ .setValueType(arrayType.toString())));
+ }
+
+ @Override
public int hashCode() {
return Objects.hash(arrayType, values);
}
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 d245294cb4..f8273b37e6 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
@@ -27,7 +27,11 @@ import com.google.devtools.build.android.AndroidDataWritingVisitor;
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.protobuf.InvalidProtocolBufferException;
+import java.io.IOException;
+import java.io.OutputStream;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collections;
@@ -49,22 +53,24 @@ import javax.xml.stream.events.XMLEvent;
/**
* Represents an Android Resource custom attribute.
*
- * <p>Attribute are the most complicated Android resource, and therefore the least documented. Most
- * of the information about them is found by reading the android compatibility library source. An
+ * <p>
+ * Attribute are the most complicated Android resource, and therefore the least documented. Most of
+ * the information about them is found by reading the android compatibility library source. An
* Attribute defines a parameter that can be passed into a view class -- as such you can think of
* attributes as creating slots for other resources to fit into. Each slot will have at least one
- * format, and can have multiples. Simple attributes (color, boolean, reference, dimension,
- * float, integer, string, and fraction) are defined as &lt;attr name="<em>name</em>"
- * format="<em>format</em>" /&gt; while the complex ones, flag and enum, have sub tags:
- * &lt;attr name="<em>name</em>" &gt&lt;flag name="<em>name</em>" value="<em>value</em>"&gt;
- * &lt;/attr&gt;.
+ * format, and can have multiples. Simple attributes (color, boolean, reference, dimension, float,
+ * integer, string, and fraction) are defined as &lt;attr name="<em>name</em>" format=
+ * "<em>format</em>" /&gt; while the complex ones, flag and enum, have sub tags: &lt;attr name=
+ * "<em>name</em>" &gt&lt;flag name="<em>name</em>" value="<em>value</em>"&gt; &lt;/attr&gt;.
*
- * <p>Attributes also have a double duty as defining validation logic for layout resources --
- * each layout attribute *must* have a corresponding attribute which will be used to validate the
+ * <p>
+ * Attributes also have a double duty as defining validation logic for layout resources -- each
+ * layout attribute *must* have a corresponding attribute which will be used to validate the
* value/resource reference defined in it.
*
- * <p>AttrXmlValue, due to the multiple types of attributes is actually a composite class that
- * contains multiple {@link XmlResourceValue} instances for each resource.
+ * <p>
+ * AttrXmlValue, due to the multiple types of attributes is actually a composite class that contains
+ * multiple {@link XmlResourceValue} instances for each resource.
*/
@Immutable
public class AttrXmlResourceValue implements XmlResourceValue {
@@ -143,6 +149,51 @@ public class AttrXmlResourceValue implements XmlResourceValue {
return of(ImmutableMap.copyOf(Arrays.asList(entries)));
}
+ public static XmlResourceValue from(SerializeFormat.DataValueXml proto)
+ throws InvalidProtocolBufferException {
+ Builder<String, ResourceXmlAttrValue> formats =
+ ImmutableMap.<String, AttrXmlResourceValue.ResourceXmlAttrValue>builder();
+ for (Entry<String, SerializeFormat.DataValueXml> entry : proto.getMappedXmlValue().entrySet()) {
+ switch (entry.getKey()) {
+ case FLAG:
+ formats.put(
+ entry.getKey(), FlagResourceXmlAttrValue.of(entry.getValue().getMappedStringValue()));
+ break;
+ case ENUM:
+ formats.put(
+ entry.getKey(), EnumResourceXmlAttrValue.of(entry.getValue().getMappedStringValue()));
+ break;
+ case REFERENCE:
+ formats.put(entry.getKey(), ReferenceResourceXmlAttrValue.of());
+ break;
+ case COLOR:
+ formats.put(entry.getKey(), ColorResourceXmlAttrValue.of());
+ break;
+ case BOOLEAN:
+ formats.put(entry.getKey(), BooleanResourceXmlAttrValue.of());
+ break;
+ case DIMENSION:
+ formats.put(entry.getKey(), DimensionResourceXmlAttrValue.of());
+ break;
+ case FLOAT:
+ formats.put(entry.getKey(), FloatResourceXmlAttrValue.of());
+ break;
+ case INTEGER:
+ formats.put(entry.getKey(), IntegerResourceXmlAttrValue.of());
+ break;
+ case STRING:
+ formats.put(entry.getKey(), StringResourceXmlAttrValue.of());
+ break;
+ case FRACTION:
+ formats.put(entry.getKey(), FractionResourceXmlAttrValue.of());
+ break;
+ default:
+ throw new InvalidProtocolBufferException("Unexpected format: " + entry.getKey());
+ }
+ }
+ return of(formats.build());
+ }
+
public static XmlResourceValue from(
StartElement attr, @Nullable String format, XMLEventReader eventReader)
throws XMLStreamException {
@@ -242,9 +293,26 @@ public class AttrXmlResourceValue implements XmlResourceValue {
mergedDataWriter.writeToValuesXml(key, iterable.append("</attr>"));
}
+ @Override
+ public int serializeTo(Path source, OutputStream output) throws IOException {
+ SerializeFormat.DataValue.Builder builder = XmlResourceValues.newProtoDataBuilder(source);
+ SerializeFormat.DataValueXml.Builder xmlValueBuilder =
+ SerializeFormat.DataValueXml.newBuilder();
+ xmlValueBuilder.setType(SerializeFormat.DataValueXml.XmlType.ATTR);
+ for (Entry<String, ResourceXmlAttrValue> entry : formats.entrySet()) {
+ xmlValueBuilder
+ .getMutableMappedXmlValue()
+ .put(entry.getKey(), entry.getValue().appendTo(builder.getXmlValueBuilder()));
+ }
+ builder.setXmlValue(xmlValueBuilder);
+ return XmlResourceValues.serializeProtoDataValue(output, builder);
+ }
+
@CheckReturnValue
interface ResourceXmlAttrValue {
FluentIterable<String> appendTo(FluentIterable<String> iterable);
+
+ SerializeFormat.DataValueXml appendTo(SerializeFormat.DataValueXml.Builder builder);
}
// TODO(corysmith): The ResourceXmlAttrValue implementors, other than enum and flag, share a
@@ -305,6 +373,11 @@ public class AttrXmlResourceValue implements XmlResourceValue {
public FluentIterable<String> appendTo(FluentIterable<String> iterable) {
return iterable.append(FluentIterable.from(values.entrySet()).transform(MAP_TO_ENUM));
}
+
+ @Override
+ public SerializeFormat.DataValueXml appendTo(SerializeFormat.DataValueXml.Builder builder) {
+ return builder.putAllMappedStringValue(values).build();
+ }
}
/** Represents an Android Flag Attribute resource. */
@@ -366,6 +439,11 @@ public class AttrXmlResourceValue implements XmlResourceValue {
}
}));
}
+
+ @Override
+ public SerializeFormat.DataValueXml appendTo(SerializeFormat.DataValueXml.Builder builder) {
+ return builder.putAllMappedStringValue(values).build();
+ }
}
/** Represents an Android Reference Attribute resource. */
@@ -387,6 +465,11 @@ public class AttrXmlResourceValue implements XmlResourceValue {
public FluentIterable<String> appendTo(FluentIterable<String> iterable) {
return iterable;
}
+
+ @Override
+ public SerializeFormat.DataValueXml appendTo(SerializeFormat.DataValueXml.Builder builder) {
+ return builder.build();
+ }
}
/** Represents an Android Color Attribute resource. */
@@ -407,6 +490,11 @@ public class AttrXmlResourceValue implements XmlResourceValue {
public FluentIterable<String> appendTo(FluentIterable<String> iterable) {
return iterable;
}
+
+ @Override
+ public SerializeFormat.DataValueXml appendTo(SerializeFormat.DataValueXml.Builder builder) {
+ return builder.build();
+ }
}
/** Represents an Android Boolean Attribute resource. */
@@ -427,6 +515,11 @@ public class AttrXmlResourceValue implements XmlResourceValue {
public FluentIterable<String> appendTo(FluentIterable<String> iterable) {
return iterable;
}
+
+ @Override
+ public SerializeFormat.DataValueXml appendTo(SerializeFormat.DataValueXml.Builder builder) {
+ return builder.build();
+ }
}
/** Represents an Android Float Attribute resource. */
@@ -447,6 +540,11 @@ public class AttrXmlResourceValue implements XmlResourceValue {
public FluentIterable<String> appendTo(FluentIterable<String> iterable) {
return iterable;
}
+
+ @Override
+ public SerializeFormat.DataValueXml appendTo(SerializeFormat.DataValueXml.Builder builder) {
+ return builder.build();
+ }
}
/** Represents an Android Dimension Attribute resource. */
@@ -468,6 +566,11 @@ public class AttrXmlResourceValue implements XmlResourceValue {
public FluentIterable<String> appendTo(FluentIterable<String> iterable) {
return iterable;
}
+
+ @Override
+ public SerializeFormat.DataValueXml appendTo(SerializeFormat.DataValueXml.Builder builder) {
+ return builder.build();
+ }
}
/** Represents an Android Integer Attribute resource. */
@@ -488,6 +591,11 @@ public class AttrXmlResourceValue implements XmlResourceValue {
public FluentIterable<String> appendTo(FluentIterable<String> iterable) {
return iterable;
}
+
+ @Override
+ public SerializeFormat.DataValueXml appendTo(SerializeFormat.DataValueXml.Builder builder) {
+ return builder.build();
+ }
}
/** Represents an Android String Attribute resource. */
@@ -508,6 +616,11 @@ public class AttrXmlResourceValue implements XmlResourceValue {
public FluentIterable<String> appendTo(FluentIterable<String> iterable) {
return iterable;
}
+
+ @Override
+ public SerializeFormat.DataValueXml appendTo(SerializeFormat.DataValueXml.Builder builder) {
+ return builder.build();
+ }
}
/** Represents an Android Fraction Attribute resource. */
@@ -528,5 +641,10 @@ public class AttrXmlResourceValue implements XmlResourceValue {
public FluentIterable<String> appendTo(FluentIterable<String> iterable) {
return iterable;
}
+
+ @Override
+ public SerializeFormat.DataValueXml appendTo(SerializeFormat.DataValueXml.Builder builder) {
+ return builder.build();
+ }
}
}
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 2938256af4..07e3ad5940 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
@@ -18,7 +18,12 @@ import com.google.common.collect.ImmutableList;
import com.google.devtools.build.android.AndroidDataWritingVisitor;
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.protobuf.CodedOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
import java.nio.file.Path;
import javax.annotation.concurrent.Immutable;
@@ -26,11 +31,12 @@ import javax.annotation.concurrent.Immutable;
/**
* Represents an Android Resource id.
*
- * <p>Ids (http://developer.android.com/guide/topics/resources/more-resources.html#Id) are
- * special -- unlike other resources they cannot be overridden. This is due to the
- * nature of their usage. Each id corresponds to context sensitive resource of component, meaning
- * that they have no intrinsic defined value. They exist to reference parts of other resources.
- * Ids can also be declared on the fly in components with the syntax @[+][package:]id/resource_name.
+ * <p>
+ * Ids (http://developer.android.com/guide/topics/resources/more-resources.html#Id) are special --
+ * unlike other resources they cannot be overridden. This is due to the nature of their usage. Each
+ * id corresponds to context sensitive resource of component, meaning that they have no intrinsic
+ * defined value. They exist to reference parts of other resources. Ids can also be declared on the
+ * fly in components with the syntax @[+][package:]id/resource_name.
*/
@Immutable
public class IdXmlResourceValue implements XmlResourceValue {
@@ -52,6 +58,19 @@ public class IdXmlResourceValue implements XmlResourceValue {
}
@Override
+ public int serializeTo(Path source, OutputStream output) throws IOException {
+ SerializeFormat.DataValue value =
+ XmlResourceValues.newProtoDataBuilder(source)
+ .setXmlValue(
+ SerializeFormat.DataValueXml.newBuilder()
+ .setType(SerializeFormat.DataValueXml.XmlType.ID))
+ .build();
+ value.writeDelimitedTo(output);
+ return CodedOutputStream.computeUInt32SizeNoTag(value.getSerializedSize())
+ + value.getSerializedSize();
+ }
+
+ @Override
public String toString() {
return MoreObjects.toStringHelper(getClass()).toString();
}
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 e47b5611c0..64397ef35f 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
@@ -21,7 +21,14 @@ import com.google.common.collect.ImmutableMap;
import com.google.devtools.build.android.AndroidDataWritingVisitor;
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.DataValue.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.nio.file.Path;
import java.util.Map.Entry;
import java.util.Objects;
@@ -32,10 +39,10 @@ import javax.annotation.concurrent.Immutable;
/**
* Represents an Android Plural Resource.
*
- * <p>Plurals are a localization construct (http://developer.android.com/guide/topics/resources/
- * string-resource.html#Plurals) that are basically a map of key to
- * value. They are defined in xml as:
- * <code>
+ * <p>
+ * Plurals are a localization construct (http://developer.android.com/guide/topics/resources/
+ * string-resource.html#Plurals) that are basically a map of key to value. They are defined in xml
+ * as: <code>
* &lt;plurals name="plural_name"&gt;
* &lt;item quantity=["zero" | "one" | "two" | "few" | "many" | "other"]&gt;
* text_string
@@ -95,4 +102,24 @@ public class PluralXmlResourceValue implements XmlResourceValue {
public String toString() {
return MoreObjects.toStringHelper(getClass()).add("values", values).toString();
}
+
+ public static XmlResourceValue from(SerializeFormat.DataValueXml proto) {
+ return of(ImmutableMap.copyOf(proto.getMappedStringValue()));
+ }
+
+ @Override
+ public int serializeTo(Path source, OutputStream output) throws IOException {
+ Builder builder = XmlResourceValues.newProtoDataBuilder(source);
+ SerializeFormat.DataValue value =
+ builder
+ .setXmlValue(
+ builder
+ .getXmlValueBuilder()
+ .setType(XmlType.PLURAL)
+ .putAllMappedStringValue(values))
+ .build();
+ value.writeDelimitedTo(output);
+ return CodedOutputStream.computeUInt32SizeNoTag(value.getSerializedSize())
+ + value.getSerializedSize();
+ }
}
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 dadba36c9f..ff8c6ad742 100644
--- a/src/tools/android/java/com/google/devtools/build/android/xml/SimpleXmlResourceValue.java
+++ b/src/tools/android/java/com/google/devtools/build/android/xml/SimpleXmlResourceValue.java
@@ -18,9 +18,13 @@ import com.google.common.collect.ImmutableList;
import com.google.devtools.build.android.AndroidDataWritingVisitor;
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.android.resources.ResourceType;
+import java.io.IOException;
+import java.io.OutputStream;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Objects;
@@ -31,14 +35,14 @@ 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 (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 (http://developer.android.com/guide/topics/resources/more-resources.html#Dimension).
- * These are defined in xml as &lt;<em>resource type</em> name="<em>name</em>"
- * value="<em>value</em>"&gt;. In the interest of keeping the parsing svelte, these are
- * represented by a single class.
+ * <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
+ * (http://developer.android.com/guide/topics/resources/more-resources.html#Dimension). These are
+ * defined in xml as &lt;<em>resource type</em> name="<em>name</em>" value="<em>value</em>"&gt;. In
+ * the interest of keeping the parsing svelte, these are represented by a single class.
*/
@Immutable
public class SimpleXmlResourceValue implements XmlResourceValue {
@@ -125,6 +129,22 @@ public class SimpleXmlResourceValue implements XmlResourceValue {
valueType.tagName.getLocalPart())));
}
+ public static XmlResourceValue from(SerializeFormat.DataValueXml proto) {
+ return of(Type.valueOf(proto.getValueType()), proto.getValue());
+ }
+
+ @Override
+ public int serializeTo(Path source, OutputStream output) throws IOException {
+ SerializeFormat.DataValue.Builder builder = XmlResourceValues.newProtoDataBuilder(source);
+ builder.setXmlValue(
+ builder
+ .getXmlValueBuilder()
+ .setType(SerializeFormat.DataValueXml.XmlType.SIMPLE)
+ .setValue(value)
+ .setValueType(valueType.name()));
+ return XmlResourceValues.serializeProtoDataValue(output, builder);
+ }
+
@Override
public int hashCode() {
return Objects.hash(valueType, value);
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 ce11bbbda1..cb73d07cd1 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
@@ -21,7 +21,11 @@ import com.google.common.collect.ImmutableMap;
import com.google.devtools.build.android.AndroidDataWritingVisitor;
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;
import java.nio.file.Path;
import java.util.Map;
import java.util.Map.Entry;
@@ -33,12 +37,12 @@ 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 and feel for a layout or other ui construct. They are effectively a s set of values that
+ * <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
- * pass, they must only contain definer attributes with acceptable values.
- * <code>
+ * pass, they must only contain definer attributes with acceptable values. <code>
* &lt;resources&gt;
* &lt;style name="CustomText" parent="@style/Text"&gt;
* &lt;item name="android:textSize"&gt;20sp&lt;/item&gt;
@@ -64,7 +68,11 @@ public class StyleXmlResourceValue implements XmlResourceValue {
return new StyleXmlResourceValue(parent, ImmutableMap.copyOf(values));
}
- private StyleXmlResourceValue(String parent, ImmutableMap<String, String> values) {
+ public static XmlResourceValue from(SerializeFormat.DataValueXml proto) {
+ return of(proto.hasValue() ? proto.getValue() : null, proto.getMappedStringValue());
+ }
+
+ private StyleXmlResourceValue(@Nullable String parent, ImmutableMap<String, String> values) {
this.parent = parent;
this.values = values;
}
@@ -79,12 +87,25 @@ public class StyleXmlResourceValue implements XmlResourceValue {
String.format("<!-- %s -->", source),
parent == null || parent.isEmpty()
? String.format("<style name='%s'>", key.name())
- : String.format("<style name='%s' parent='%s'>", key.name(), parent)))
+ : String.format("<style name='%s' parent='@%s'>", key.name(), parent)))
.append(FluentIterable.from(values.entrySet()).transform(ENTRY_TO_ITEM))
.append("</style>"));
}
@Override
+ public int serializeTo(Path source, OutputStream output) throws IOException {
+ SerializeFormat.DataValueXml.Builder xmlValueBuilder =
+ SerializeFormat.DataValueXml.newBuilder()
+ .setType(SerializeFormat.DataValueXml.XmlType.STYLE)
+ .putAllMappedStringValue(values);
+ if (parent != null && !parent.isEmpty()) {
+ xmlValueBuilder.setValue(parent);
+ }
+ return XmlResourceValues.serializeProtoDataValue(
+ output, XmlResourceValues.newProtoDataBuilder(source).setXmlValue(xmlValueBuilder));
+ }
+
+ @Override
public int hashCode() {
return Objects.hash(parent, values);
}
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 4f8fadc081..e46eaf3065 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
@@ -22,7 +22,11 @@ import com.google.common.collect.Ordering;
import com.google.devtools.build.android.AndroidDataWritingVisitor;
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;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
@@ -34,25 +38,25 @@ import javax.annotation.concurrent.Immutable;
/**
* Represent an Android styleable resource.
*
- * <p>Styleable resources are groups of attributes that can be applied to views. They are, for the
- * most part, vaguely documented (http://developer.android.com/training/custom-views/create-view
- * .html#customattr). It's worth noting that attributes declared inside a
- * &lt;declare-styleable&gt; tags, for example;
- * <code>
+ * <p>
+ * Styleable resources are groups of attributes that can be applied to views. They are, for the most
+ * part, vaguely documented (http://developer.android.com/training/custom-views/create-view
+ * .html#customattr). It's worth noting that attributes declared inside a &lt;declare-styleable&gt;
+ * tags, for example; <code>
* <declare-styleable name="PieChart">
* <attr name="showText" format="boolean" />
* </declare-styleable>
* </code>
*
- * Can also be seen as:
- * <code>
+ * Can also be seen as: <code>
* <attr name="showText" format="boolean" />
* <declare-styleable name="PieChart">
* <attr name="showText"/>
* </declare-styleable>
* </code>
*
- * <p>The StyleableXmlValue only contains names of the attributes it holds, not definitions.
+ * <p>
+ * The StyleableXmlValue only contains names of the attributes it holds, not definitions.
*/
@Immutable
public class StyleableXmlResourceValue implements XmlResourceValue {
@@ -94,6 +98,21 @@ public class StyleableXmlResourceValue implements XmlResourceValue {
}
@Override
+ public int serializeTo(Path source, OutputStream output) throws IOException {
+ return XmlResourceValues.serializeProtoDataValue(
+ output,
+ XmlResourceValues.newProtoDataBuilder(source)
+ .setXmlValue(
+ SerializeFormat.DataValueXml.newBuilder()
+ .setType(SerializeFormat.DataValueXml.XmlType.STYLEABLE)
+ .addAllListValue(attrs)));
+ }
+
+ public static XmlResourceValue from(SerializeFormat.DataValueXml proto) {
+ return of(proto.getListValueList());
+ }
+
+ @Override
public int hashCode() {
return attrs.hashCode();
}