aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/tools/android/java/com/google/devtools
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/android/java/com/google/devtools')
-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();
}