aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/tools/android
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/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