aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/tools/android/java/com/google/devtools/build/android
diff options
context:
space:
mode:
authorGravatar Googler <noreply@google.com>2016-04-05 17:52:42 +0000
committerGravatar Lukacs Berki <lberki@google.com>2016-04-07 11:40:01 +0000
commit0f86dc815c46f5bde00e42fc875ed0502a1fac44 (patch)
tree54661f592792a5c35f6e7b5ec0134f995dcaebf4 /src/tools/android/java/com/google/devtools/build/android
parentc0b4e2c5cc15c749afba7a238d6337c7c4d66004 (diff)
4.25 of 5: Writing of UnwrittenMergedAndroidData
Introduces the AndroidDataWriter and AndroidDataWritingVisitor to abstract the io operations from the data classes. Necessary refactoring to the stubbed write method on DataValue in DataAsset.writeAsset and DataResource.writeResource. New interface for the AttributeValues to reflect the simplifications of writing Resource Attributes. Of special note is the fact all xml is written into a single file, values.xml. This is following the Gradle convention and aapt has demonstrated a preference of only reading a values.xml and ignoring all other xml files in the values directory. Unless profiling demonstrates an advantage to writing multiple files (which I doubt), this merger carries on this convention. -- MOS_MIGRATED_REVID=119066611
Diffstat (limited to 'src/tools/android/java/com/google/devtools/build/android')
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/AndroidDataWriter.java110
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/AndroidDataWritingVisitor.java51
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/DataAsset.java9
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/DataResource.java8
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/DataResourceXml.java7
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/DataValueFile.java21
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/FullyQualifiedName.java27
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/ParsedAndroidData.java15
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/RelativeAssetPath.java5
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/UnwrittenMergedAndroidData.java53
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/XmlResourceValue.java14
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/XmlResourceValues.java7
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/xml/ArrayXmlResourceValue.java49
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/xml/AttrXmlResourceValue.java258
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/xml/IdXmlResourceValue.java17
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/xml/PluralXmlResourceValue.java42
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/xml/SimpleXmlResourceValue.java25
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/xml/StyleXmlResourceValue.java41
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/xml/StyleableXmlResourceValue.java40
19 files changed, 597 insertions, 202 deletions
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
new file mode 100644
index 0000000000..b954aff3bc
--- /dev/null
+++ b/src/tools/android/java/com/google/devtools/build/android/AndroidDataWriter.java
@@ -0,0 +1,110 @@
+// 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 static java.nio.charset.StandardCharsets.UTF_8;
+
+import com.google.common.collect.Ordering;
+
+import java.io.Flushable;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Writer for UnwrittenMergedAndroidData.
+ */
+// TODO(corysmith): Profile this class on large datasets and look for bottlenecks, as this class
+// does all the IO.
+public class AndroidDataWriter implements Flushable, AndroidDataWritingVisitor {
+ private final Path destination;
+ private final Map<FullyQualifiedName, Iterable<String>> valueFragments = new HashMap<>();
+ private Path resourceDirectory;
+ private Path assetDirectory;
+
+ private AndroidDataWriter(Path destination, Path resourceDirectory, Path assetsDirectory) {
+ this.destination = destination;
+ this.resourceDirectory = resourceDirectory;
+ this.assetDirectory = assetsDirectory;
+ }
+
+ public static AndroidDataWriter from(Path destination) {
+ return new AndroidDataWriter(
+ destination, destination.resolve("res"), destination.resolve("assets"));
+ }
+
+ @Override
+ public Path copyManifest(Path sourceManifest) throws IOException {
+ // aapt won't read any manifest that is not named AndroidManifest.xml,
+ // so we hard code it here.
+ Path destinationManifest = destination.resolve("AndroidManifest.xml");
+ copy(sourceManifest, destinationManifest);
+ return destinationManifest;
+ }
+
+ public Path assetDirectory() {
+ return assetDirectory;
+ }
+
+ public Path resourceDirectory() {
+ return resourceDirectory;
+ }
+
+ @Override
+ public void copyAsset(Path source, String relativeDestinationPath) throws IOException {
+ copy(source, assetDirectory.resolve(relativeDestinationPath));
+ }
+
+ @Override
+ public void copyResource(Path source, String relativeDestinationPath) throws IOException {
+ copy(source, resourceDirectory.resolve(relativeDestinationPath));
+ }
+
+ private void copy(Path sourcePath, Path destinationPath) throws IOException {
+ Files.createDirectories(destinationPath.getParent());
+ Files.copy(sourcePath, destinationPath);
+ }
+
+ /**
+ * Finalizes all operations and flushes the buffers.
+ */
+ @Override
+ 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)) {
+ channel.write(ByteBuffer.wrap("<resources>".getBytes(UTF_8)));
+ for (FullyQualifiedName key : Ordering.natural().sortedCopy(valueFragments.keySet())) {
+ for (String line : valueFragments.get(key)) {
+ channel.write(ByteBuffer.wrap(line.getBytes(UTF_8)));
+ }
+ }
+ channel.write(ByteBuffer.wrap("</resources>".getBytes(UTF_8)));
+ }
+ valueFragments.clear();
+ }
+
+ @Override
+ public void writeToValuesXml(FullyQualifiedName key, Iterable<String> xmlFragment) {
+ valueFragments.put(key, xmlFragment);
+ }
+}
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
new file mode 100644
index 0000000000..40abe2f184
--- /dev/null
+++ b/src/tools/android/java/com/google/devtools/build/android/AndroidDataWritingVisitor.java
@@ -0,0 +1,51 @@
+// 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 java.io.IOException;
+import java.nio.file.Path;
+
+/**
+ * An interface for visiting android data for writing.
+ */
+public interface AndroidDataWritingVisitor {
+ /**
+ * Copies the AndroidManifest to the destination directory.
+ */
+ Path copyManifest(Path sourceManifest) throws IOException;
+
+ /**
+ * 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.
+ */
+ void copyAsset(Path source, String relativeDestinationPath) throws IOException;
+
+ /**
+ * 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.
+ */
+ void copyResource(Path source, String relativeDestinationPath) throws IOException;
+
+ /**
+ * Adds a xml string fragment to the values file.
+ *
+ * @param key Used to ensure a constant order of the written xml.
+ * @param xmlFragment the xml fragment as an Iterable<String> which allows lazy generation.
+ */
+ void writeToValuesXml(FullyQualifiedName key, Iterable<String> xmlFragment);
+}
diff --git a/src/tools/android/java/com/google/devtools/build/android/DataAsset.java b/src/tools/android/java/com/google/devtools/build/android/DataAsset.java
index 08dcab4eb8..cbb2f77101 100644
--- a/src/tools/android/java/com/google/devtools/build/android/DataAsset.java
+++ b/src/tools/android/java/com/google/devtools/build/android/DataAsset.java
@@ -14,17 +14,14 @@
package com.google.devtools.build.android;
import java.io.IOException;
-import java.nio.file.Path;
/**
* Represents an Asset created from a binary file.
*/
public interface DataAsset extends DataValue {
-
/**
- * Writes the asset to the given asset directory.
- * @param newAssetDirectory The new directory for this asset.
- * @throws IOException if there are issues with writing the asset.
+ * Write the asset value to mergedDataWriter.
*/
- void write(Path newAssetDirectory) throws IOException;
+ void writeAsset(RelativeAssetPath key, AndroidDataWritingVisitor mergedDataWriter)
+ throws IOException;
}
diff --git a/src/tools/android/java/com/google/devtools/build/android/DataResource.java b/src/tools/android/java/com/google/devtools/build/android/DataResource.java
index 06bd6416af..98fef179ba 100644
--- a/src/tools/android/java/com/google/devtools/build/android/DataResource.java
+++ b/src/tools/android/java/com/google/devtools/build/android/DataResource.java
@@ -14,16 +14,14 @@
package com.google.devtools.build.android;
import java.io.IOException;
-import java.nio.file.Path;
/**
* Represents an Android Resource parsed from an xml or binary file.
*/
public interface DataResource extends DataValue {
/**
- * Writes the resource to the given resource directory.
- * @param newResourceDirectory The new directory for this resource.
- * @throws IOException if there are issues with writing the resource.
+ * Write as a resource using the supplied {@link MergeDataWriter}.
*/
- void write(Path newResourceDirectory) throws IOException;
+ void writeResource(FullyQualifiedName key, AndroidDataWritingVisitor mergedDataWriter)
+ 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 ac5b392800..21e8c92657 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
@@ -58,7 +58,7 @@ public class DataResourceXml implements DataResource {
* @throws FactoryConfigurationError Thrown with the {@link XMLInputFactory} is misconfigured.
* @throws IOException Thrown when there is an error reading a file.
*/
- public static void fromPath(
+ public static void parse(
XMLInputFactory xmlInputFactory,
Path path,
Factory fqnFactory,
@@ -160,8 +160,7 @@ public class DataResourceXml implements DataResource {
}
@Override
- public void write(Path newResourceDirectory) throws IOException {
- // TODO(corysmith): Implement write.
- throw new UnsupportedOperationException();
+ public void writeResource(FullyQualifiedName key, AndroidDataWritingVisitor mergedDataWriter) {
+ xml.write(key, source, mergedDataWriter);
}
}
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 4ee0a7d91d..b92002f783 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
@@ -61,8 +61,23 @@ public class DataValueFile implements DataResource, DataAsset {
}
@Override
- public void write(Path newResourceDirectory) throws IOException {
- // TODO(corysmith): Implement the copy semantics.
- throw new UnsupportedOperationException();
+ public void writeAsset(RelativeAssetPath key, AndroidDataWritingVisitor mergedDataWriter)
+ throws IOException {
+ mergedDataWriter.copyAsset(source, key.toPathString());
+ }
+
+ @Override
+ public void writeResource(FullyQualifiedName key, AndroidDataWritingVisitor mergedDataWriter)
+ throws IOException {
+ mergedDataWriter.copyResource(source, key.toPathString(getSourceExtension()));
+ }
+
+ private String getSourceExtension() {
+ String fileName = source.getFileName().toString();
+ int extensionStart = fileName.lastIndexOf('.');
+ if (extensionStart > 0) {
+ return fileName.substring(extensionStart);
+ }
+ return "";
}
}
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 ccdc6617a4..303196cbfa 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
@@ -21,6 +21,7 @@ import com.google.common.collect.Ordering;
import com.android.resources.ResourceType;
+import java.nio.file.Paths;
import java.util.List;
import java.util.Objects;
import java.util.regex.Matcher;
@@ -44,6 +45,32 @@ public class FullyQualifiedName implements DataKey, Comparable<FullyQualifiedNam
private final String resourceName;
/**
+ * 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]
+ *
+ * @param sourceExtension The extension of the resource represented by the FullyQualifiedName
+ * @return A string representation of the FullyQualifiedName with the provided extension.
+ */
+ 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()),
+ resourceName + sourceExtension)
+ .toString();
+ }
+
+ public String name() {
+ return resourceName;
+ }
+
+ /**
* A factory for parsing an generating FullyQualified names with qualifiers and package.
*/
public static class Factory {
diff --git a/src/tools/android/java/com/google/devtools/build/android/ParsedAndroidData.java b/src/tools/android/java/com/google/devtools/build/android/ParsedAndroidData.java
index cc4311a209..50c62e9001 100644
--- a/src/tools/android/java/com/google/devtools/build/android/ParsedAndroidData.java
+++ b/src/tools/android/java/com/google/devtools/build/android/ParsedAndroidData.java
@@ -18,6 +18,7 @@ import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
import com.android.ide.common.res2.MergingException;
@@ -34,6 +35,7 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
@@ -50,8 +52,7 @@ import javax.xml.stream.XMLStreamException;
*/
@Immutable
public class ParsedAndroidData {
-
- /** A Consumer style interface that will accept a DataKey and DataValue. */
+ /** A Consumer style interface that will appendTo a DataKey and DataValue. */
interface KeyValueConsumer<K extends DataKey, V extends DataValue> {
void consume(K key, V value);
}
@@ -253,7 +254,7 @@ public class ParsedAndroidData {
try {
if (!Files.isDirectory(path)) {
if (inValuesSubtree) {
- DataResourceXml.fromPath(
+ DataResourceXml.parse(
xmlInputFactory, path, fqnFactory, overwritingConsumer, nonOverwritingConsumer);
} else {
String rawFqn = deriveRawFullyQualifiedName(path);
@@ -412,15 +413,19 @@ public class ParsedAndroidData {
return overwritingResources.containsKey(name);
}
- Iterable<Map.Entry<DataKey, DataResource>> iterateOverwritableEntries() {
+ Iterable<Entry<DataKey, DataResource>> iterateOverwritableEntries() {
return overwritingResources.entrySet();
}
+ public Iterable<Entry<DataKey, DataResource>> iterateDataResourceEntries() {
+ return Iterables.concat(overwritingResources.entrySet(), nonOverwritingResources.entrySet());
+ }
+
boolean containsAsset(DataKey name) {
return assets.containsKey(name);
}
- Iterable<Map.Entry<DataKey, DataAsset>> iterateAssetEntries() {
+ Iterable<Entry<DataKey, DataAsset>> iterateAssetEntries() {
return assets.entrySet();
}
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 83b31fd081..609216ff64 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
@@ -29,7 +29,6 @@ import java.util.Objects;
* Note: Assets have no qualifiers or packages.
*/
public class RelativeAssetPath implements DataKey, Comparable<RelativeAssetPath> {
-
/**
* A Factory that creates RelativeAssetsPath objects whose paths are relative to a given path.
*/
@@ -78,6 +77,10 @@ public class RelativeAssetPath implements DataKey, Comparable<RelativeAssetPath>
return Objects.equals(relativeAssetPath, that.relativeAssetPath);
}
+ public String toPathString() {
+ return this.relativeAssetPath.toString();
+ }
+
@Override
public int hashCode() {
return relativeAssetPath.hashCode();
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 30cb43f289..633bd9403a 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
@@ -18,6 +18,7 @@ import com.google.common.base.MoreObjects;
import java.io.IOException;
import java.nio.file.Path;
+import java.util.Map.Entry;
import java.util.Objects;
/**
@@ -26,7 +27,7 @@ import java.util.Objects;
public class UnwrittenMergedAndroidData {
private final Path manifest;
- private final ParsedAndroidData resources;
+ private final ParsedAndroidData primary;
private final ParsedAndroidData deps;
public static UnwrittenMergedAndroidData of(
@@ -35,28 +36,54 @@ public class UnwrittenMergedAndroidData {
}
private UnwrittenMergedAndroidData(
- Path manifest, ParsedAndroidData resources, ParsedAndroidData deps) {
+ Path manifest, ParsedAndroidData primary, ParsedAndroidData deps) {
this.manifest = manifest;
- this.resources = resources;
+ this.primary = primary;
this.deps = deps;
}
/**
- * Writes the android data to directories for consumption by aapt.
- * @param newResourceDirectory The new resource directory to write to.
+ * 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.
*/
- public MergedAndroidData write(Path newResourceDirectory) throws IOException {
- // TODO(corysmith): Implement write.
- throw new UnsupportedOperationException();
+ public MergedAndroidData write(AndroidDataWriter mergedDataWriter) throws IOException {
+ try {
+ writeParsedAndroidData(primary, mergedDataWriter);
+ writeParsedAndroidData(deps, mergedDataWriter);
+ return new MergedAndroidData(
+ mergedDataWriter.resourceDirectory(),
+ mergedDataWriter.assetDirectory(),
+ mergedDataWriter.copyManifest(this.manifest));
+ } finally {
+ // Flush to make sure all writing is completed before returning a MergedAndroidData.
+ // If resources aren't fully written, the MergedAndroidData might be invalid.
+ mergedDataWriter.flush();
+ }
+ }
+
+ private void writeParsedAndroidData(
+ ParsedAndroidData resources, AndroidDataWritingVisitor mergedDataWriter) throws IOException {
+ for (Entry<DataKey, DataAsset> entry : resources.iterateAssetEntries()) {
+ // TODO(corysmith): Resolve the nit of casting to a RelativeAssetPath by sorting
+ // out the type structure and generics of DataKey, ParsedAndroidData, AndroidDataMerger and
+ // MergeConflict.
+ entry.getValue().writeAsset((RelativeAssetPath) entry.getKey(), mergedDataWriter);
+ }
+ for (Entry<DataKey, DataResource> entry : resources.iterateDataResourceEntries()) {
+ // TODO(corysmith): Resolve the nit of casting to a FullyQualifiedName by sorting
+ // out the type structure and generics of DataKey, ParsedAndroidData, AndroidDataMerger and
+ // MergeConflict.
+ entry.getValue().writeResource((FullyQualifiedName) entry.getKey(), mergedDataWriter);
+ }
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("manifest", manifest)
- .add("resources", resources)
+ .add("primary", primary)
.add("deps", deps)
.toString();
}
@@ -71,13 +98,13 @@ public class UnwrittenMergedAndroidData {
}
UnwrittenMergedAndroidData that = (UnwrittenMergedAndroidData) other;
return Objects.equals(manifest, that.manifest)
- && Objects.equals(resources, that.resources)
+ && Objects.equals(primary, that.primary)
&& Objects.equals(deps, that.deps);
}
@Override
public int hashCode() {
- return Objects.hash(manifest, resources, deps);
+ return Objects.hash(manifest, primary, deps);
}
@VisibleForTesting
@@ -86,8 +113,8 @@ public class UnwrittenMergedAndroidData {
}
@VisibleForTesting
- ParsedAndroidData getResources() {
- return resources;
+ ParsedAndroidData getPrimary() {
+ return primary;
}
@VisibleForTesting
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 2a22a5aa2f..1a3cfd1f66 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,16 +13,18 @@
// limitations under the License.
package com.google.devtools.build.android;
-import java.io.Writer;
+import java.nio.file.Path;
/**
- * An XmlValue is a value extracted from an xml resource in the resource 'values' directory.
+ * An {@link XmlResourceValue} is extracted from xml files in the resource 'values' directory.
*/
public interface XmlResourceValue {
/**
- * Each XmlValue is expected to write a valid representation in xml to the supplied buffer.
- * @param buffer The buffer for xml output.
- * @param name The name of the value being written.
+ * Each XmlValue is expected to write a valid representation in xml to the writer.
+ *
+ * @param key The FullyQualified name for the xml resource being written.
+ * @param source The source of the value to allow for proper comment annotation.
+ * @param mergedDataWriter The target writer.
*/
- void write(Writer buffer, FullyQualifiedName name);
+ void write(FullyQualifiedName key, Path source, AndroidDataWritingVisitor mergedDataWriter);
}
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 c686829385..f2525712ea 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,6 +13,7 @@
// limitations under the License.
package com.google.devtools.build.android;
+import com.google.common.collect.ImmutableMap;
import com.google.devtools.build.android.ParsedAndroidData.KeyValueConsumer;
import com.google.devtools.build.android.xml.AttrXmlResourceValue;
import com.google.devtools.build.android.xml.IdXmlResourceValue;
@@ -37,7 +38,7 @@ import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;
/**
- * XmlValues provides methods for getting XmlValue derived classes.
+ * {@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.
@@ -58,7 +59,7 @@ public class XmlResourceValues {
private static final QName ATTR_TYPE = QName.valueOf("type");
static XmlResourceValue parsePlurals(XMLEventReader eventReader) throws XMLStreamException {
- Map<String, String> values = new HashMap<>();
+ ImmutableMap.Builder<String, String> values = ImmutableMap.builder();
for (XMLEvent element = eventReader.nextTag();
!isEndTag(element, TAG_PLURALS);
element = eventReader.nextTag()) {
@@ -68,7 +69,7 @@ public class XmlResourceValues {
eventReader.getElementText());
}
}
- return PluralXmlResourceValue.of(values);
+ return PluralXmlResourceValue.of(values.build());
}
static XmlResourceValue parseStyle(XMLEventReader eventReader, StartElement start)
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 752e803c3e..329ae85405 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
@@ -14,17 +14,23 @@
package com.google.devtools.build.android.xml;
import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Function;
import com.google.common.base.MoreObjects;
+import com.google.common.collect.FluentIterable;
+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 java.io.Writer;
+import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.Immutable;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLStreamException;
@@ -40,20 +46,33 @@ import javax.xml.stream.events.XMLEvent;
* .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.
*/
+@Immutable
public class ArrayXmlResourceValue implements XmlResourceValue {
private static final QName TAG_INTEGER_ARRAY = QName.valueOf("integer-array");
private static final QName TAG_ARRAY = QName.valueOf("array");
+ private static final QName TAG_STRING_ARRAY = QName.valueOf("string-array");
+ private static final Function<String, String> ITEM_TO_XML =
+ new Function<String, String>() {
+ @Nullable
+ @Override
+ public String apply(@Nullable String item) {
+ return String.format("<item>%s</item>", item);
+ }
+ };
/**
* Enumerates the different types of array tags.
*/
public enum ArrayType {
INTEGER_ARRAY(TAG_INTEGER_ARRAY),
- ARRAY(TAG_ARRAY);
+ ARRAY(TAG_ARRAY),
+ STRING_ARRAY(TAG_STRING_ARRAY);
public QName tagName;
@@ -71,12 +90,20 @@ public class ArrayXmlResourceValue implements XmlResourceValue {
throw new IllegalArgumentException(
String.format("%s not found in %s", tagQName, Arrays.toString(values())));
}
+
+ String openTag(FullyQualifiedName key) {
+ return String.format("<%s name='%s'>", tagName.getLocalPart(), key.name());
+ }
+
+ String closeTag() {
+ return String.format("</%s>", tagName.getLocalPart());
+ }
}
- private final List<String> values;
- private ArrayType arrayType;
+ private final ImmutableList<String> values;
+ private final ArrayType arrayType;
- private ArrayXmlResourceValue(ArrayType arrayType, List<String> values) {
+ private ArrayXmlResourceValue(ArrayType arrayType, ImmutableList<String> values) {
this.arrayType = arrayType;
this.values = values;
}
@@ -87,13 +114,17 @@ public class ArrayXmlResourceValue implements XmlResourceValue {
}
public static XmlResourceValue of(ArrayType arrayType, List<String> values) {
- return new ArrayXmlResourceValue(arrayType, values);
+ return new ArrayXmlResourceValue(arrayType, ImmutableList.copyOf(values));
}
@Override
- public void write(Writer buffer, FullyQualifiedName name) {
- // TODO(corysmith): Implement writing xml.
- throw new UnsupportedOperationException();
+ public void write(
+ FullyQualifiedName key, Path source, AndroidDataWritingVisitor mergedDataWriter) {
+ mergedDataWriter.writeToValuesXml(
+ key,
+ FluentIterable.of(String.format("<!-- %s -->", source), arrayType.openTag(key))
+ .append(FluentIterable.from(values).transform(ITEM_TO_XML))
+ .append(arrayType.closeTag()));
}
@Override
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 52f44dbc6e..61dfdefd6a 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
@@ -14,24 +14,32 @@
package com.google.devtools.build.android.xml;
import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Function;
+import com.google.common.base.Joiner;
import com.google.common.base.MoreObjects;
import com.google.common.base.Preconditions;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMap.Builder;
+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 java.io.Writer;
+import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collections;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
+import javax.annotation.CheckReturnValue;
import javax.annotation.Nullable;
+import javax.annotation.concurrent.Immutable;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLStreamException;
@@ -55,9 +63,10 @@ import javax.xml.stream.events.XMLEvent;
* 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 mutiple types of attributes is actually a composite class that
- * contains multiple XmlValue 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 {
private static final String FRACTION = "fraction";
@@ -72,19 +81,19 @@ public class AttrXmlResourceValue implements XmlResourceValue {
private static final String FLAG = "flag";
private static final QName TAG_ENUM = QName.valueOf(ENUM);
private static final QName TAG_FLAG = QName.valueOf(FLAG);
- private final Map<String, XmlResourceValue> formats;
+ private final ImmutableMap<String, ResourceXmlAttrValue> formats;
- private AttrXmlResourceValue(Map<String, XmlResourceValue> formats) {
+ private AttrXmlResourceValue(ImmutableMap<String, ResourceXmlAttrValue> formats) {
this.formats = formats;
}
private static Map<String, String> readSubValues(XMLEventReader reader, QName subTagType)
throws XMLStreamException {
- Map<String, String> enumValue = new HashMap<>();
+ Builder<String, String> builder = ImmutableMap.builder();
while (reader.hasNext()
&& XmlResourceValues.isTag(XmlResourceValues.peekNextTag(reader), subTagType)) {
StartElement element = reader.nextEvent().asStartElement();
- enumValue.put(
+ builder.put(
XmlResourceValues.getElementName(element), XmlResourceValues.getElementValue(element));
XMLEvent endTag = reader.nextEvent();
if (!XmlResourceValues.isEndTag(endTag, subTagType)) {
@@ -92,7 +101,7 @@ public class AttrXmlResourceValue implements XmlResourceValue {
String.format("Unexpected [%s]; Expected %s", endTag, "</enum>"), endTag.getLocation());
}
}
- return enumValue;
+ return builder.build();
}
private static void endAttrElement(XMLEventReader reader) throws XMLStreamException {
@@ -103,11 +112,11 @@ public class AttrXmlResourceValue implements XmlResourceValue {
}
@VisibleForTesting
- private static final class BuilderEntry implements Map.Entry<String, XmlResourceValue> {
+ private static final class BuilderEntry implements Entry<String, ResourceXmlAttrValue> {
private final String name;
- private final XmlResourceValue value;
+ private final ResourceXmlAttrValue value;
- public BuilderEntry(String name, XmlResourceValue value) {
+ BuilderEntry(String name, ResourceXmlAttrValue value) {
this.name = name;
this.value = value;
}
@@ -118,19 +127,19 @@ public class AttrXmlResourceValue implements XmlResourceValue {
}
@Override
- public XmlResourceValue getValue() {
+ public ResourceXmlAttrValue getValue() {
return value;
}
@Override
- public XmlResourceValue setValue(XmlResourceValue value) {
+ public ResourceXmlAttrValue setValue(ResourceXmlAttrValue value) {
throw new UnsupportedOperationException();
}
}
@SafeVarargs
@VisibleForTesting
- public static XmlResourceValue fromFormatEntries(Map.Entry<String, XmlResourceValue>... entries) {
+ public static XmlResourceValue fromFormatEntries(Entry<String, ResourceXmlAttrValue>... entries) {
return of(ImmutableMap.copyOf(Arrays.asList(entries)));
}
@@ -146,52 +155,52 @@ public class AttrXmlResourceValue implements XmlResourceValue {
formatNames.add(nextTag.asStartElement().getName().getLocalPart().toLowerCase());
}
- Map<String, XmlResourceValue> formats = new HashMap<>();
+ Builder<String, ResourceXmlAttrValue> formats = ImmutableMap.builder();
for (String formatName : formatNames) {
switch (formatName) {
case FLAG:
Map<String, String> flags = readSubValues(eventReader, TAG_FLAG);
endAttrElement(eventReader);
- formats.put(formatName, AttrFlagXmlValue.of(flags));
+ formats.put(formatName, FlagResourceXmlAttrValue.of(flags));
break;
case ENUM:
Map<String, String> enums = readSubValues(eventReader, TAG_ENUM);
endAttrElement(eventReader);
- formats.put(formatName, AttrEnumXmlValue.of(enums));
+ formats.put(formatName, EnumResourceXmlAttrValue.of(enums));
break;
case REFERENCE:
- formats.put(formatName, AttrReferenceXmlValue.of());
+ formats.put(formatName, ReferenceResourceXmlAttrValue.of());
break;
case COLOR:
- formats.put(formatName, AttrColorXmlValue.of());
+ formats.put(formatName, ColorResourceXmlAttrValue.of());
break;
case BOOLEAN:
- formats.put(formatName, AttrBooleanXmlValue.of());
+ formats.put(formatName, BooleanResourceXmlAttrValue.of());
break;
case DIMENSION:
- formats.put(formatName, AttrDimensionXmlValue.of());
+ formats.put(formatName, DimensionResourceXmlAttrValue.of());
break;
case FLOAT:
- formats.put(formatName, AttrFloatXmlValue.of());
+ formats.put(formatName, FloatResourceXmlAttrValue.of());
break;
case INTEGER:
- formats.put(formatName, AttrIntegerXmlValue.of());
+ formats.put(formatName, IntegerResourceXmlAttrValue.of());
break;
case STRING:
- formats.put(formatName, AttrStringXmlValue.of());
+ formats.put(formatName, StringResourceXmlAttrValue.of());
break;
case FRACTION:
- formats.put(formatName, AttrFractionXmlValue.of());
+ formats.put(formatName, FractionResourceXmlAttrValue.of());
break;
default:
throw new XMLStreamException(
String.format("Unexpected attr format: %S", formatName), attr.getLocation());
}
}
- return of(formats);
+ return of(formats.build());
}
- public static XmlResourceValue of(Map<String, XmlResourceValue> formats) {
+ public static XmlResourceValue of(ImmutableMap<String, ResourceXmlAttrValue> formats) {
return new AttrXmlResourceValue(formats);
}
@@ -218,40 +227,58 @@ public class AttrXmlResourceValue implements XmlResourceValue {
}
@Override
- public void write(Writer buffer, FullyQualifiedName name) {
- // TODO(corysmith): Implement write.
- throw new UnsupportedOperationException();
+ public void write(
+ FullyQualifiedName key, Path source, AndroidDataWritingVisitor mergedDataWriter) {
+ ImmutableList<String> formatKeys = Ordering.natural().immutableSortedCopy(formats.keySet());
+ FluentIterable<String> iterable =
+ FluentIterable.of(
+ String.format("<!-- %s -->", source),
+ String.format(
+ "<attr name='%s' format='%s'>", key.name(), Joiner.on('|').join(formatKeys)));
+ for (String formatKey : formatKeys) {
+ iterable = formats.get(formatKey).appendTo(iterable);
+ }
+ mergedDataWriter.writeToValuesXml(key, iterable.append("</attr>"));
}
+ @CheckReturnValue
+ interface ResourceXmlAttrValue {
+ FluentIterable<String> appendTo(FluentIterable<String> iterable);
+ }
+
+ // TODO(corysmith): The ResourceXmlAttrValue implementors, other than enum and flag, share a
+ // lot of boilerplate. Determine how to reduce it.
/** Represents an Android Enum Attribute resource. */
@VisibleForTesting
- public static class AttrEnumXmlValue implements XmlResourceValue {
-
+ public static class EnumResourceXmlAttrValue implements ResourceXmlAttrValue {
+
+ private static final Function<Entry<String, String>, String> MAP_TO_ENUM =
+ new Function<Entry<String, String>, String>() {
+ @Nullable
+ @Override
+ public String apply(Entry<String, String> entry) {
+ return String.format("<enum name='%s' value='%s'/>", entry.getKey(), entry.getValue());
+ }
+ };
private Map<String, String> values;
- private AttrEnumXmlValue(Map<String, String> values) {
+ private EnumResourceXmlAttrValue(Map<String, String> values) {
this.values = values;
}
@VisibleForTesting
- public static Map.Entry<String, XmlResourceValue> asEntryOf(String... keyThenValue) {
+ public static Entry<String, ResourceXmlAttrValue> asEntryOf(String... keyThenValue) {
Preconditions.checkArgument(keyThenValue.length > 0);
Preconditions.checkArgument(keyThenValue.length % 2 == 0);
- Builder<String, String> builder = ImmutableMap.<String, String>builder();
+ Builder<String, String> builder = ImmutableMap.builder();
for (int i = 0; i < keyThenValue.length; i += 2) {
builder.put(keyThenValue[i], keyThenValue[i + 1]);
}
return new BuilderEntry(ENUM, of(builder.build()));
}
- public static XmlResourceValue of(Map<String, String> values) {
- return new AttrEnumXmlValue(values);
- }
-
- @Override
- public void write(Writer buffer, FullyQualifiedName name) {
- // TODO(corysmith): Implement write.
- throw new UnsupportedOperationException();
+ public static ResourceXmlAttrValue of(Map<String, String> values) {
+ return new EnumResourceXmlAttrValue(values);
}
@Override
@@ -261,10 +288,10 @@ public class AttrXmlResourceValue implements XmlResourceValue {
@Override
public boolean equals(Object obj) {
- if (!(obj instanceof AttrEnumXmlValue)) {
+ if (!(obj instanceof EnumResourceXmlAttrValue)) {
return false;
}
- AttrEnumXmlValue other = (AttrEnumXmlValue) obj;
+ EnumResourceXmlAttrValue other = (EnumResourceXmlAttrValue) obj;
return Objects.equals(values, other.values);
}
@@ -272,25 +299,31 @@ public class AttrXmlResourceValue implements XmlResourceValue {
public String toString() {
return MoreObjects.toStringHelper(getClass()).add("values", values).toString();
}
+
+ @Override
+ public FluentIterable<String> appendTo(FluentIterable<String> iterable) {
+ return iterable.append(FluentIterable.from(values.entrySet()).transform(MAP_TO_ENUM));
+ }
}
/** Represents an Android Flag Attribute resource. */
@VisibleForTesting
- public static class AttrFlagXmlValue implements XmlResourceValue {
+ public static class FlagResourceXmlAttrValue implements ResourceXmlAttrValue {
private Map<String, String> values;
- private AttrFlagXmlValue(Map<String, String> values) {
+ private FlagResourceXmlAttrValue(Map<String, String> values) {
this.values = values;
}
- public static XmlResourceValue of(Map<String, String> values) {
- return new AttrFlagXmlValue(values);
+ public static ResourceXmlAttrValue of(Map<String, String> values) {
+ // ImmutableMap guarantees a stable order.
+ return new FlagResourceXmlAttrValue(ImmutableMap.copyOf(values));
}
@VisibleForTesting
- public static Map.Entry<String, XmlResourceValue> asEntryOf(String... keyThenValue) {
- Builder<String, String> builder = ImmutableMap.<String, String>builder();
+ public static Entry<String, ResourceXmlAttrValue> asEntryOf(String... keyThenValue) {
+ Builder<String, String> builder = ImmutableMap.builder();
Preconditions.checkArgument(keyThenValue.length > 0);
Preconditions.checkArgument(keyThenValue.length % 2 == 0);
for (int i = 0; i < keyThenValue.length; i += 2) {
@@ -300,22 +333,16 @@ public class AttrXmlResourceValue implements XmlResourceValue {
}
@Override
- public void write(Writer buffer, FullyQualifiedName name) {
- // TODO(corysmith): Implement write.
- throw new UnsupportedOperationException();
- }
-
- @Override
public int hashCode() {
return values.hashCode();
}
@Override
public boolean equals(Object obj) {
- if (!(obj instanceof AttrFlagXmlValue)) {
+ if (!(obj instanceof FlagResourceXmlAttrValue)) {
return false;
}
- AttrFlagXmlValue other = (AttrFlagXmlValue) obj;
+ FlagResourceXmlAttrValue other = (FlagResourceXmlAttrValue) obj;
return Objects.equals(values, other.values);
}
@@ -323,14 +350,30 @@ public class AttrXmlResourceValue implements XmlResourceValue {
public String toString() {
return MoreObjects.toStringHelper(getClass()).add("values", values).toString();
}
+
+ @Override
+ public FluentIterable<String> appendTo(FluentIterable<String> iterable) {
+ return iterable.append(
+ FluentIterable.from(values.entrySet())
+ .transform(
+ new Function<Entry<String, String>, String>() {
+ @Nullable
+ @Override
+ public String apply(Entry<String, String> input) {
+ return String.format(
+ "<flag name='%s' value='%s'/>", input.getKey(), input.getValue());
+ }
+ }));
+ }
}
/** Represents an Android Reference Attribute resource. */
@VisibleForTesting
- public static class AttrReferenceXmlValue implements XmlResourceValue {
- private static final AttrReferenceXmlValue INSTANCE = new AttrReferenceXmlValue();
+ public static class ReferenceResourceXmlAttrValue implements ResourceXmlAttrValue {
+ private static final ReferenceResourceXmlAttrValue INSTANCE =
+ new ReferenceResourceXmlAttrValue();
- public static XmlResourceValue of() {
+ public static ResourceXmlAttrValue of() {
return INSTANCE;
}
@@ -340,18 +383,17 @@ public class AttrXmlResourceValue implements XmlResourceValue {
}
@Override
- public void write(Writer buffer, FullyQualifiedName name) {
- // TODO(corysmith): Implement write.
- throw new UnsupportedOperationException();
+ public FluentIterable<String> appendTo(FluentIterable<String> iterable) {
+ return iterable;
}
}
/** Represents an Android Color Attribute resource. */
@VisibleForTesting
- public static class AttrColorXmlValue implements XmlResourceValue {
- private static final AttrColorXmlValue INSTANCE = new AttrColorXmlValue();
+ public static class ColorResourceXmlAttrValue implements ResourceXmlAttrValue {
+ private static final ColorResourceXmlAttrValue INSTANCE = new ColorResourceXmlAttrValue();
- public static XmlResourceValue of() {
+ public static ResourceXmlAttrValue of() {
return INSTANCE;
}
@@ -361,18 +403,17 @@ public class AttrXmlResourceValue implements XmlResourceValue {
}
@Override
- public void write(Writer buffer, FullyQualifiedName name) {
- // TODO(corysmith): Implement write.
- throw new UnsupportedOperationException();
+ public FluentIterable<String> appendTo(FluentIterable<String> iterable) {
+ return iterable;
}
}
/** Represents an Android Boolean Attribute resource. */
@VisibleForTesting
- public static class AttrBooleanXmlValue implements XmlResourceValue {
- private static final AttrBooleanXmlValue INSTANCE = new AttrBooleanXmlValue();
+ public static class BooleanResourceXmlAttrValue implements ResourceXmlAttrValue {
+ private static final BooleanResourceXmlAttrValue INSTANCE = new BooleanResourceXmlAttrValue();
- public static XmlResourceValue of() {
+ public static ResourceXmlAttrValue of() {
return INSTANCE;
}
@@ -382,18 +423,17 @@ public class AttrXmlResourceValue implements XmlResourceValue {
}
@Override
- public void write(Writer buffer, FullyQualifiedName name) {
- // TODO(corysmith): Implement write.
- throw new UnsupportedOperationException();
+ public FluentIterable<String> appendTo(FluentIterable<String> iterable) {
+ return iterable;
}
}
/** Represents an Android Float Attribute resource. */
@VisibleForTesting
- public static class AttrFloatXmlValue implements XmlResourceValue {
- private static final AttrFloatXmlValue INSTANCE = new AttrFloatXmlValue();
+ public static class FloatResourceXmlAttrValue implements ResourceXmlAttrValue {
+ private static final FloatResourceXmlAttrValue INSTANCE = new FloatResourceXmlAttrValue();
- public static XmlResourceValue of() {
+ public static ResourceXmlAttrValue of() {
return INSTANCE;
}
@@ -403,39 +443,38 @@ public class AttrXmlResourceValue implements XmlResourceValue {
}
@Override
- public void write(Writer buffer, FullyQualifiedName name) {
- // TODO(corysmith): Implement write.
- throw new UnsupportedOperationException();
+ public FluentIterable<String> appendTo(FluentIterable<String> iterable) {
+ return iterable;
}
}
/** Represents an Android Dimension Attribute resource. */
@VisibleForTesting
- public static class AttrDimensionXmlValue implements XmlResourceValue {
- private static final AttrDimensionXmlValue INSTANCE = new AttrDimensionXmlValue();
+ public static class DimensionResourceXmlAttrValue implements ResourceXmlAttrValue {
+ private static final DimensionResourceXmlAttrValue INSTANCE =
+ new DimensionResourceXmlAttrValue();
+
+ public static ResourceXmlAttrValue of() {
+ return INSTANCE;
+ }
@VisibleForTesting
public static BuilderEntry asEntry() {
return new BuilderEntry(DIMENSION, of());
}
- public static XmlResourceValue of() {
- return INSTANCE;
- }
-
@Override
- public void write(Writer buffer, FullyQualifiedName name) {
- // TODO(corysmith): Implement write.
- throw new UnsupportedOperationException();
+ public FluentIterable<String> appendTo(FluentIterable<String> iterable) {
+ return iterable;
}
}
/** Represents an Android Integer Attribute resource. */
@VisibleForTesting
- public static class AttrIntegerXmlValue implements XmlResourceValue {
- private static final AttrIntegerXmlValue INSTANCE = new AttrIntegerXmlValue();
+ public static class IntegerResourceXmlAttrValue implements ResourceXmlAttrValue {
+ private static final IntegerResourceXmlAttrValue INSTANCE = new IntegerResourceXmlAttrValue();
- public static XmlResourceValue of() {
+ public static ResourceXmlAttrValue of() {
return INSTANCE;
}
@@ -445,18 +484,17 @@ public class AttrXmlResourceValue implements XmlResourceValue {
}
@Override
- public void write(Writer buffer, FullyQualifiedName name) {
- // TODO(corysmith): Implement write.
- throw new UnsupportedOperationException();
+ public FluentIterable<String> appendTo(FluentIterable<String> iterable) {
+ return iterable;
}
}
/** Represents an Android String Attribute resource. */
@VisibleForTesting
- public static class AttrStringXmlValue implements XmlResourceValue {
- private static final AttrStringXmlValue INSTANCE = new AttrStringXmlValue();
+ public static class StringResourceXmlAttrValue implements ResourceXmlAttrValue {
+ private static final StringResourceXmlAttrValue INSTANCE = new StringResourceXmlAttrValue();
- public static XmlResourceValue of() {
+ public static ResourceXmlAttrValue of() {
return INSTANCE;
}
@@ -466,18 +504,17 @@ public class AttrXmlResourceValue implements XmlResourceValue {
}
@Override
- public void write(Writer buffer, FullyQualifiedName name) {
- // TODO(corysmith): Implement write.
- throw new UnsupportedOperationException();
+ public FluentIterable<String> appendTo(FluentIterable<String> iterable) {
+ return iterable;
}
}
/** Represents an Android Fraction Attribute resource. */
@VisibleForTesting
- public static class AttrFractionXmlValue implements XmlResourceValue {
- private static final AttrFractionXmlValue INSTANCE = new AttrFractionXmlValue();
+ public static class FractionResourceXmlAttrValue implements ResourceXmlAttrValue {
+ private static final FractionResourceXmlAttrValue INSTANCE = new FractionResourceXmlAttrValue();
- public static XmlResourceValue of() {
+ public static ResourceXmlAttrValue of() {
return INSTANCE;
}
@@ -487,9 +524,8 @@ public class AttrXmlResourceValue implements XmlResourceValue {
}
@Override
- public void write(Writer buffer, FullyQualifiedName name) {
- // TODO(corysmith): Implement write.
- throw new UnsupportedOperationException();
+ public FluentIterable<String> appendTo(FluentIterable<String> iterable) {
+ return iterable;
}
}
}
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 f8221ed332..309971005f 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
@@ -14,10 +14,14 @@
package com.google.devtools.build.android.xml;
import com.google.common.base.MoreObjects;
+import com.google.common.collect.FluentIterable;
+import com.google.devtools.build.android.AndroidDataWritingVisitor;
import com.google.devtools.build.android.FullyQualifiedName;
import com.google.devtools.build.android.XmlResourceValue;
-import java.io.Writer;
+import java.nio.file.Path;
+
+import javax.annotation.concurrent.Immutable;
/**
* Represents an Android Resource id.
@@ -28,6 +32,7 @@ import java.io.Writer;
* 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 {
static final IdXmlResourceValue SINGLETON = new IdXmlResourceValue();
@@ -37,9 +42,13 @@ public class IdXmlResourceValue implements XmlResourceValue {
}
@Override
- public void write(Writer buffer, FullyQualifiedName name) {
- // TODO(corysmith): Implement write.
- throw new UnsupportedOperationException();
+ public void write(
+ FullyQualifiedName key, Path source, AndroidDataWritingVisitor mergedDataWriter) {
+ mergedDataWriter.writeToValuesXml(
+ key,
+ FluentIterable.of(
+ String.format("<!-- %s -->", source),
+ String.format("<item type='id' name='%s'/>", key.name())));
}
@Override
diff --git a/src/tools/android/java/com/google/devtools/build/android/xml/PluralXmlResourceValue.java b/src/tools/android/java/com/google/devtools/build/android/xml/PluralXmlResourceValue.java
index 36c18c0a9e..8df051797a 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
@@ -13,20 +13,27 @@
// limitations under the License.
package com.google.devtools.build.android.xml;
+import com.google.common.base.Function;
import com.google.common.base.MoreObjects;
+import com.google.common.collect.FluentIterable;
+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 java.io.Writer;
-import java.util.Map;
+import java.nio.file.Path;
+import java.util.Map.Entry;
import java.util.Objects;
+import javax.annotation.Nullable;
+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:
+ * 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;
@@ -35,22 +42,37 @@ import java.util.Objects;
* &lt;/plurals&gt;
* </code>
*/
+@Immutable
public class PluralXmlResourceValue implements XmlResourceValue {
- private Map<String, String> values;
+ public static final Function<Entry<String, String>, String> ENTRY_TO_PLURAL =
+ new Function<Entry<String, String>, String>() {
+ @Nullable
+ @Override
+ public String apply(Entry<String, String> input) {
+ return String.format("<item quantity='%s'>%s</item>", input.getKey(), input.getValue());
+ }
+ };
+ private final ImmutableMap<String, String> values;
- private PluralXmlResourceValue(Map<String, String> values) {
+ private PluralXmlResourceValue(ImmutableMap<String, String> values) {
this.values = values;
}
- public static XmlResourceValue of(Map<String, String> values) {
+ public static XmlResourceValue of(ImmutableMap<String, String> values) {
return new PluralXmlResourceValue(values);
}
@Override
- public void write(Writer buffer, FullyQualifiedName name) {
- // TODO(corysmith): Implement write.
- throw new UnsupportedOperationException();
+ public void write(
+ FullyQualifiedName key, Path source, AndroidDataWritingVisitor mergedDataWriter) {
+ mergedDataWriter.writeToValuesXml(
+ key,
+ FluentIterable.of(
+ String.format("<!-- %s -->", source),
+ String.format("<plurals name='%s'>", key.name()))
+ .append(FluentIterable.from(values.entrySet()).transform(ENTRY_TO_PLURAL))
+ .append("</plurals>"));
}
@Override
diff --git a/src/tools/android/java/com/google/devtools/build/android/xml/SimpleXmlResourceValue.java b/src/tools/android/java/com/google/devtools/build/android/xml/SimpleXmlResourceValue.java
index ce7684b764..4df442f35f 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
@@ -14,15 +14,18 @@
package com.google.devtools.build.android.xml;
import com.google.common.base.MoreObjects;
+import com.google.common.collect.FluentIterable;
+import com.google.devtools.build.android.AndroidDataWritingVisitor;
import com.google.devtools.build.android.FullyQualifiedName;
import com.google.devtools.build.android.XmlResourceValue;
import com.android.resources.ResourceType;
-import java.io.Writer;
+import java.nio.file.Path;
import java.util.Arrays;
import java.util.Objects;
+import javax.annotation.concurrent.Immutable;
import javax.xml.namespace.QName;
/**
@@ -37,6 +40,7 @@ import javax.xml.namespace.QName;
* 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 {
static final QName TAG_STRING = QName.valueOf("string");
static final QName TAG_BOOL = QName.valueOf("bool");
@@ -94,8 +98,8 @@ public class SimpleXmlResourceValue implements XmlResourceValue {
}
}
- private String value;
- private Type valueType;
+ private final String value;
+ private final Type valueType;
public static XmlResourceValue of(Type valueType, String value) {
return new SimpleXmlResourceValue(valueType, value);
@@ -107,9 +111,18 @@ public class SimpleXmlResourceValue implements XmlResourceValue {
}
@Override
- public void write(Writer buffer, FullyQualifiedName name) {
- // TODO(corysmith): Implement write.
- throw new UnsupportedOperationException();
+ public void write(
+ FullyQualifiedName key, Path source, AndroidDataWritingVisitor mergedDataWriter) {
+ mergedDataWriter.writeToValuesXml(
+ key,
+ FluentIterable.of(
+ String.format("<!-- %s -->", source),
+ String.format(
+ "<%s name='%s'>%s</%s>",
+ valueType.tagName.getLocalPart(),
+ key.name(),
+ value,
+ valueType.tagName.getLocalPart())));
}
@Override
diff --git a/src/tools/android/java/com/google/devtools/build/android/xml/StyleXmlResourceValue.java b/src/tools/android/java/com/google/devtools/build/android/xml/StyleXmlResourceValue.java
index 3055089ea0..2067a01f77 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
@@ -13,14 +13,22 @@
// limitations under the License.
package com.google.devtools.build.android.xml;
+import com.google.common.base.Function;
import com.google.common.base.MoreObjects;
+import com.google.common.collect.FluentIterable;
+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 java.io.Writer;
+import java.nio.file.Path;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Objects;
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.Immutable;
+
/**
* Represents an Android Style Resource.
*
@@ -38,23 +46,40 @@ import java.util.Objects;
* &lt;/resources&gt;
* </code>
*/
+@Immutable
public class StyleXmlResourceValue implements XmlResourceValue {
- private String parent;
- private Map<String, String> values;
+ public static final Function<Entry<String, String>, String> ENTRY_TO_ITEM =
+ new Function<Entry<String, String>, String>() {
+ @Nullable
+ @Override
+ public String apply(Entry<String, String> input) {
+ return String.format("<item name='%s'>%s</item>", input.getKey(), input.getValue());
+ }
+ };
+ private final String parent;
+ private final ImmutableMap<String, String> values;
public static StyleXmlResourceValue of(String parent, Map<String, String> values) {
- return new StyleXmlResourceValue(parent, values);
+ return new StyleXmlResourceValue(parent, ImmutableMap.copyOf(values));
}
- private StyleXmlResourceValue(String parent, Map<String, String> values) {
+ private StyleXmlResourceValue(String parent, ImmutableMap<String, String> values) {
this.parent = parent;
this.values = values;
}
@Override
- public void write(Writer buffer, FullyQualifiedName name) {
- // TODO(corysmith): Implement write.
- throw new UnsupportedOperationException();
+ public void write(
+ FullyQualifiedName key, Path source, AndroidDataWritingVisitor mergedDataWriter) {
+ mergedDataWriter.writeToValuesXml(
+ key,
+ FluentIterable.of(
+ 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))
+ .append(FluentIterable.from(values.entrySet()).transform(ENTRY_TO_ITEM))
+ .append("</style>"));
}
@Override
diff --git a/src/tools/android/java/com/google/devtools/build/android/xml/StyleableXmlResourceValue.java b/src/tools/android/java/com/google/devtools/build/android/xml/StyleableXmlResourceValue.java
index f1a0486307..76011237f7 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
@@ -14,15 +14,23 @@
package com.google.devtools.build.android.xml;
import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Function;
import com.google.common.base.MoreObjects;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableList;
+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 java.io.Writer;
+import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.Immutable;
+
/**
* Represent an Android styleable resource.
*
@@ -46,26 +54,42 @@ import java.util.Objects;
*
* <p>The StyleableXmlValue only contains names of the attributes it holds, not definitions.
*/
+@Immutable
public class StyleableXmlResourceValue implements XmlResourceValue {
- private final List<String> attrs;
+ public static final Function<String, String> ITEM_TO_ATTR =
+ new Function<String, String>() {
+ @Nullable
+ @Override
+ public String apply(@Nullable String input) {
+ return String.format("<attr name='%s'/>", input);
+ }
+ };
+ private final ImmutableList<String> attrs;
- private StyleableXmlResourceValue(List<String> attrs) {
+ private StyleableXmlResourceValue(ImmutableList<String> attrs) {
this.attrs = attrs;
}
public static XmlResourceValue of(List<String> attrs) {
- return new StyleableXmlResourceValue(attrs);
+ return new StyleableXmlResourceValue(ImmutableList.copyOf(attrs));
}
@VisibleForTesting
public static XmlResourceValue of(String... attrs) {
- return new StyleableXmlResourceValue(Arrays.asList(attrs));
+ return new StyleableXmlResourceValue(
+ Ordering.natural().immutableSortedCopy(Arrays.asList(attrs)));
}
@Override
- public void write(Writer buffer, FullyQualifiedName name) {
- // TODO(corysmith): Implement write.
- throw new UnsupportedOperationException();
+ public void write(
+ FullyQualifiedName key, Path source, AndroidDataWritingVisitor mergedDataWriter) {
+ mergedDataWriter.writeToValuesXml(
+ key,
+ FluentIterable.of(
+ String.format("<!-- %s -->", source),
+ String.format("<declare-styleable name='%s'>", key.name()))
+ .append(FluentIterable.from(attrs).transform(ITEM_TO_ATTR))
+ .append("</declare-styleable>"));
}
@Override