diff options
author | apell <apell@google.com> | 2017-04-10 16:16:11 +0000 |
---|---|---|
committer | Jakob Buchgraber <buchgr@google.com> | 2017-04-11 10:50:10 +0200 |
commit | 219af1a02aed0110f471aa07a2e7e2680e2b96b5 (patch) | |
tree | 857d8d4e87bf724a89d3f9ddbc7f54db75e0cfe1 /src | |
parent | 5dc81a2c9b30f59f143b5e7fd1b1ec6f8ba27fcf (diff) |
Allow combining of resources attributes that can be comma-separated lists. This is not exhaustive of all combinable attributes, but of those that have common uses. More combinable attributes can be added by adding to ResourcesAttribute.AttributeType.
RELNOTES: None.
PiperOrigin-RevId: 152694650
Diffstat (limited to 'src')
7 files changed, 128 insertions, 27 deletions
diff --git a/src/test/java/com/google/devtools/build/android/AndroidDataSerializerAndDeserializerTest.java b/src/test/java/com/google/devtools/build/android/AndroidDataSerializerAndDeserializerTest.java index c7ac512854..198e5a0112 100644 --- a/src/test/java/com/google/devtools/build/android/AndroidDataSerializerAndDeserializerTest.java +++ b/src/test/java/com/google/devtools/build/android/AndroidDataSerializerAndDeserializerTest.java @@ -191,7 +191,8 @@ public class AndroidDataSerializerAndDeserializerTest { .overwritable( file("layout/banker").source("layout/banker.xml"), xml("<resources>/foo").source("values/ids.xml") - .value(ResourcesAttribute.of("foo", "fooVal"))) + .value(ResourcesAttribute.of( + fqnFactory.parse("<resources>/foo"), "foo", "fooVal"))) .combining( xml("id/snark").source("values/ids.xml").value(IdXmlResourceValue.of())) .assets(file().source("hunting/of/the/boojum")) @@ -232,7 +233,8 @@ public class AndroidDataSerializerAndDeserializerTest { .overwritable( file("layout/banker").source("layout/banker.xml"), xml("<resources>/foo").source("values/ids.xml") - .value(ResourcesAttribute.of("foo", "fooVal"))) + .value(ResourcesAttribute.of( + fqnFactory.parse("<resources>/foo"), "foo", "fooVal"))) .combining( xml("id/snark").source("values/ids.xml").value(IdXmlResourceValue.of())) .assets(file().source("hunting/of/the/boojum")) diff --git a/src/test/java/com/google/devtools/build/android/DataResourceXmlTest.java b/src/test/java/com/google/devtools/build/android/DataResourceXmlTest.java index 653e7b7044..b839b395a4 100644 --- a/src/test/java/com/google/devtools/build/android/DataResourceXmlTest.java +++ b/src/test/java/com/google/devtools/build/android/DataResourceXmlTest.java @@ -43,8 +43,10 @@ import com.google.devtools.build.android.xml.AttrXmlResourceValue.IntegerResourc import com.google.devtools.build.android.xml.AttrXmlResourceValue.ReferenceResourceXmlAttrValue; import com.google.devtools.build.android.xml.AttrXmlResourceValue.StringResourceXmlAttrValue; import com.google.devtools.build.android.xml.IdXmlResourceValue; +import com.google.devtools.build.android.xml.Namespaces; import com.google.devtools.build.android.xml.PluralXmlResourceValue; import com.google.devtools.build.android.xml.PublicXmlResourceValue; +import com.google.devtools.build.android.xml.ResourcesAttribute; import com.google.devtools.build.android.xml.SimpleXmlResourceValue; import com.google.devtools.build.android.xml.StyleXmlResourceValue; import com.google.devtools.build.android.xml.StyleableXmlResourceValue; @@ -652,6 +654,41 @@ public class DataResourceXmlTest { } @Test + public void resourcesAttribute() throws Exception { + Namespaces namespaces = Namespaces.from( + ImmutableMap.of("tools", "http://schemas.android.com/tools")); + + Path path = writeResourceXml( + namespaces.asMap(), + ImmutableMap.of("tools:foo", "fooVal", "tools:ignore", "ignoreVal")); + + final Map<DataKey, DataResource> toOverwrite = new HashMap<>(); + final Map<DataKey, DataResource> toCombine = new HashMap<>(); + parseResourcesFrom(path, toOverwrite, toCombine); + + FullyQualifiedName fooFqn = fqn("<resources>/{http://schemas.android.com/tools}foo"); + assertThat(toOverwrite) + .containsExactly( + fooFqn, + DataResourceXml.createWithNamespaces( + path, + ResourcesAttribute.of(fooFqn, "tools:foo", "fooVal"), + namespaces) + ); + + FullyQualifiedName ignoreFqn = fqn("<resources>/{http://schemas.android.com/tools}ignore"); + assertThat(toCombine) + .containsExactly( + ignoreFqn, + DataResourceXml.createWithNamespaces( + path, + ResourcesAttribute.of(ignoreFqn, "tools:ignore", "ignoreVal"), + namespaces) + ); + + } + + @Test public void writeSimpleXmlResources() throws Exception { Path source = writeResourceXml( @@ -986,8 +1023,9 @@ public class DataResourceXmlTest { Path source = writeResourceXml( ImmutableMap.of("tools", "http://schemas.android.com/tools"), ImmutableMap.of("tools:foo", "fooVal")); + assertAbout(resourcePaths) - .that(parsedAndWritten(source, fqn("<resources>/tools:foo"))) + .that(parsedAndWritten(source, fqn("<resources>/{http://schemas.android.com/tools}foo"))) .xmlContentsIsEqualTo( resourcesXmlFrom( ImmutableMap.of("tools", "http://schemas.android.com/tools"), diff --git a/src/test/java/com/google/devtools/build/android/ParsedAndroidDataTest.java b/src/test/java/com/google/devtools/build/android/ParsedAndroidDataTest.java index 6337a6382d..51971ceb17 100644 --- a/src/test/java/com/google/devtools/build/android/ParsedAndroidDataTest.java +++ b/src/test/java/com/google/devtools/build/android/ParsedAndroidDataTest.java @@ -234,7 +234,7 @@ public class ParsedAndroidDataTest { attributeFoo, // key DataResourceXml.createWithNoNamespace( root.resolve("res/values/attr.xml"), - ResourcesAttribute.of("foo", "fooVal")), // value + ResourcesAttribute.of(attributeFoo, "foo", "fooVal")), // value stringExit, // key DataResourceXml.createWithNoNamespace( root.resolve("res/values/attr.xml"), @@ -360,9 +360,10 @@ public class ParsedAndroidDataTest { MergeConflict.of( attributeFoo, DataResourceXml.createWithNoNamespace( - rootValuesPath, ResourcesAttribute.of("foo", "fooVal")), + rootValuesPath, ResourcesAttribute.of(attributeFoo, "foo", "fooVal")), DataResourceXml.createWithNoNamespace( - otherRootValuesPath, ResourcesAttribute.of("foo", "fooVal")))), + otherRootValuesPath, + ResourcesAttribute.of(attributeFoo, "foo", "fooVal")))), ImmutableMap.of( drawableMenu, // key DataValueFile.of( @@ -370,7 +371,7 @@ public class ParsedAndroidDataTest { attributeFoo, // key DataResourceXml.createWithNoNamespace( otherRootValuesPath.overwrite(rootValuesPath), - ResourcesAttribute.of("foo", "fooVal")), // value + ResourcesAttribute.of(attributeFoo, "foo", "fooVal")), // value stringExit, // key DataResourceXml.createWithNoNamespace( otherRootValuesPath.overwrite(rootValuesPath), diff --git a/src/tools/android/java/com/google/devtools/build/android/AndroidDataWriter.java b/src/tools/android/java/com/google/devtools/build/android/AndroidDataWriter.java index 433620d144..22a3bc7f25 100644 --- a/src/tools/android/java/com/google/devtools/build/android/AndroidDataWriter.java +++ b/src/tools/android/java/com/google/devtools/build/android/AndroidDataWriter.java @@ -309,12 +309,12 @@ public class AndroidDataWriter implements AndroidDataWritingVisitor { } @Override - public void defineAttribute(FullyQualifiedName fqn, String value) { + public void defineAttribute(FullyQualifiedName fqn, String name, String value) { String valuesPath = fqn.valuesPath(); if (!valueTags.containsKey(valuesPath)) { valueTags.put(valuesPath, new ResourceValuesDefinitions()); } - valueTags.get(valuesPath).addAttribute(fqn.name(), value); + valueTags.get(valuesPath).addAttribute(name, value); } @Override diff --git a/src/tools/android/java/com/google/devtools/build/android/AndroidDataWritingVisitor.java b/src/tools/android/java/com/google/devtools/build/android/AndroidDataWritingVisitor.java index 6e9cd45884..25d218176d 100644 --- a/src/tools/android/java/com/google/devtools/build/android/AndroidDataWritingVisitor.java +++ b/src/tools/android/java/com/google/devtools/build/android/AndroidDataWritingVisitor.java @@ -54,9 +54,10 @@ public interface AndroidDataWritingVisitor extends Flushable { * * @param fqn The fully qualified name of the attribute indicating both the name of the attribute * and which qualified values.xml file to be associated with. + * @param name The simple name of the attribute given as {@code [prefix:]name}. * @param value The value of the attribute. */ - void defineAttribute(FullyQualifiedName fqn, String value); + void defineAttribute(FullyQualifiedName fqn, String name, String value); /** * Adds the namespaces associated with a {@link FullyQualifiedName}. 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 0beb9a2a1b..d2100dc4eb 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 @@ -101,15 +101,20 @@ public class DataResourceXml implements DataResource { attribute.getName().getNamespaceURI().isEmpty() ? attribute.getName().getLocalPart() : attribute.getName().getPrefix() + ":" + attribute.getName().getLocalPart(); - overwritingConsumer.consume( - fqnFactory.create( + FullyQualifiedName fqn = fqnFactory.create( VirtualType.RESOURCES_ATTRIBUTE, - attributeName), - DataResourceXml.createWithNamespaces( + attribute.getName().toString()); + ResourcesAttribute resourceAttribute = + ResourcesAttribute.of(fqn, attributeName, attribute.getValue()); + DataResourceXml resource = DataResourceXml.createWithNamespaces( path, - ResourcesAttribute.of(attributeName, attribute.getValue()), - namespaces) - ); + resourceAttribute, + namespaces); + if (resourceAttribute.isCombining()) { + combiningConsumer.consume(fqn, resource); + } else { + overwritingConsumer.consume(fqn, resource); + } } // Process resource declarations. for (StartElement start = XmlResourceValues.findNextStart(eventReader); diff --git a/src/tools/android/java/com/google/devtools/build/android/xml/ResourcesAttribute.java b/src/tools/android/java/com/google/devtools/build/android/xml/ResourcesAttribute.java index e92565fb35..01424f8683 100644 --- a/src/tools/android/java/com/google/devtools/build/android/xml/ResourcesAttribute.java +++ b/src/tools/android/java/com/google/devtools/build/android/xml/ResourcesAttribute.java @@ -24,6 +24,7 @@ import com.google.devtools.build.android.XmlResourceValue; import com.google.devtools.build.android.XmlResourceValues; import com.google.devtools.build.android.proto.SerializeFormat; import com.google.devtools.build.android.proto.SerializeFormat.DataValueXml.Builder; +import com.google.errorprone.annotations.Immutable; import java.io.IOException; import java.io.OutputStream; import java.util.Map; @@ -34,20 +35,67 @@ import java.util.Objects; */ public class ResourcesAttribute implements XmlResourceValue { - public static ResourcesAttribute of(String name, String value) { - return new ResourcesAttribute(name, value); + @Immutable + private interface Combiner { + public String combine(String first, String second); + } + + private static final Combiner COMMA_SEPARATED_COMBINER = new Combiner() { + private final Joiner joiner = Joiner.on(','); + @Override + public String combine(String first, String second) { + return joiner.join(first, second); + } + }; + + private static enum AttributeType { + UNCOMBINABLE(null, null), + TOOLS_IGNORE("{http://schemas.android.com/tools}ignore", COMMA_SEPARATED_COMBINER), + TOOLS_MENU("{http://schemas.android.com/tools}menu", COMMA_SEPARATED_COMBINER), + TOOLS_KEEP("{http://schemas.android.com/tools}keep", COMMA_SEPARATED_COMBINER), + TOOLS_DISCARD("{http://schemas.android.com/tools}discard", COMMA_SEPARATED_COMBINER); + + public static AttributeType from(String name) { + if (name == null) { + return UNCOMBINABLE; + } + for (AttributeType type : AttributeType.values()) { + if (name.equals(type.name)) { + return type; + } + } + return UNCOMBINABLE; + } + + private final String name; + public final Combiner combiner; + + private AttributeType(String name, Combiner combiner) { + this.name = name; + this.combiner = combiner; + } + } + + public static ResourcesAttribute of(FullyQualifiedName fqn, String name, String value) { + return new ResourcesAttribute(AttributeType.from(fqn.name()), name, value); } public static ResourcesAttribute from(SerializeFormat.DataValueXml proto) { Map.Entry<String, String> attribute = Iterables.getOnlyElement(proto.getAttributeMap().entrySet()); - return of(attribute.getKey(), attribute.getValue()); + return new ResourcesAttribute( + AttributeType.valueOf(proto.getValueType()), + attribute.getKey(), + attribute.getValue()); } + private final AttributeType type; private final String name; private final String value; - private ResourcesAttribute(String name, String value) { + private ResourcesAttribute(AttributeType type, String name, String value) { + + this.type = type; this.name = name; this.value = value; } @@ -55,7 +103,7 @@ public class ResourcesAttribute implements XmlResourceValue { @Override public void write( FullyQualifiedName key, DataSource source, AndroidDataWritingVisitor mergedDataWriter) { - mergedDataWriter.defineAttribute(key, value); + mergedDataWriter.defineAttribute(key, name, value); } @Override @@ -68,6 +116,7 @@ public class ResourcesAttribute implements XmlResourceValue { .getXmlValueBuilder() .putAllNamespace(namespaces.asMap()) .setType(SerializeFormat.DataValueXml.XmlType.RESOURCES_ATTRIBUTE) + .setValueType(type.name()) .putAttribute(name, value); builder.setXmlValue(xmlValueBuilder); return XmlResourceValues.serializeProtoDataValue(output, builder); @@ -75,7 +124,7 @@ public class ResourcesAttribute implements XmlResourceValue { @Override public int hashCode() { - return Objects.hash(name, value); + return Objects.hash(type, name, value); } @Override @@ -84,27 +133,32 @@ public class ResourcesAttribute implements XmlResourceValue { return false; } ResourcesAttribute other = (ResourcesAttribute) obj; - return Objects.equals(name, other.name) + return Objects.equals(type, other.type) + && Objects.equals(name, other.name) && Objects.equals(value, other.value); } @Override public String toString() { return MoreObjects.toStringHelper(getClass()) + .add("type", type) .add("name", name) .add("value", value) .toString(); } + public boolean isCombining() { + return type != AttributeType.UNCOMBINABLE; + } + @Override public XmlResourceValue combineWith(XmlResourceValue value) { if (!(value instanceof ResourcesAttribute)) { throw new IllegalArgumentException(value + "is not combinable with " + this); } ResourcesAttribute other = (ResourcesAttribute) value; - if ((name.startsWith("tools:keep") && other.name.startsWith("tools:keep")) - || (name.startsWith("tools:discard") && other.name.startsWith("tools:discard"))) { - return of(name, Joiner.on(',').join(value, other.value)); + if (type == other.type && isCombining()) { + return new ResourcesAttribute(type, name, type.combiner.combine(this.value, other.value)); } throw new IllegalArgumentException(value + "is not combinable with " + this); } |