aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/tools/android/java
diff options
context:
space:
mode:
authorGravatar Googler <noreply@google.com>2016-06-28 22:24:56 +0000
committerGravatar Dmitry Lomov <dslomov@google.com>2016-06-29 08:56:18 +0000
commitd9bf45b0ed79aa0a02edccaa5f0b2dab33f25ddc (patch)
treecc9472a06317c3abd5c256560968d60ae70fbba6 /src/tools/android/java
parent242ff7fcf34ec05785725e3f441beae320b64688 (diff)
Record whether the attr is defined or referenced in the styleable. Despite the functional equality, the definition type of the attribute has direct impact on the order in which the attribute appears in the styleable array.
-- MOS_MIGRATED_REVID=126126122
Diffstat (limited to 'src/tools/android/java')
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/FullyQualifiedName.java11
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/XmlResourceValues.java11
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/proto/serialize_format.proto3
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/xml/StyleableXmlResourceValue.java127
4 files changed, 101 insertions, 51 deletions
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 d554861e3f..eeebbbcfab 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
@@ -422,13 +422,14 @@ public class FullyQualifiedName implements DataKey, Comparable<FullyQualifiedNam
@Override
public void serializeTo(OutputStream out, int valueSize) throws IOException {
- SerializeFormat.DataKey.newBuilder()
+ toSerializedBuilder().setValueSize(valueSize).build().writeDelimitedTo(out);
+ }
+
+ public SerializeFormat.DataKey.Builder toSerializedBuilder() {
+ return SerializeFormat.DataKey.newBuilder()
.setKeyPackage(pkg)
- .setValueSize(valueSize)
.setResourceType(resourceType.getName().toUpperCase())
.addAllQualifiers(qualifiers)
- .setKeyValue(resourceName)
- .build()
- .writeDelimitedTo(out);
+ .setKeyValue(resourceName);
}
}
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 f6c3ae978f..09b1780f7f 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
@@ -131,22 +131,23 @@ public class XmlResourceValues {
XMLEventReader eventReader,
StartElement start)
throws XMLStreamException {
- List<String> members = new ArrayList<>();
+ Map<FullyQualifiedName, Boolean> members = new LinkedHashMap<>();
for (XMLEvent element = nextTag(eventReader);
!isEndTag(element, TAG_DECLARE_STYLEABLE);
element = nextTag(eventReader)) {
if (isStartTag(element, TAG_ATTR)) {
StartElement attr = element.asStartElement();
- String attrName = getElementName(attr);
- members.add(attrName);
+ FullyQualifiedName attrName = fqnFactory.create(ResourceType.ATTR, getElementName(attr));
// If there is format and the next tag is a starting tag, treat it as an attr definition.
// Without those, it will be an attr reference.
if (XmlResourceValues.getElementAttributeByName(attr, ATTR_FORMAT) != null
|| (XmlResourceValues.peekNextTag(eventReader) != null
&& XmlResourceValues.peekNextTag(eventReader).isStartElement())) {
overwritingConsumer.consume(
- fqnFactory.create(ResourceType.ATTR, attrName),
- DataResourceXml.of(path, parseAttr(eventReader, attr)));
+ attrName, DataResourceXml.of(path, parseAttr(eventReader, attr)));
+ members.put(attrName, Boolean.TRUE);
+ } else {
+ members.put(attrName, Boolean.FALSE);
}
}
}
diff --git a/src/tools/android/java/com/google/devtools/build/android/proto/serialize_format.proto b/src/tools/android/java/com/google/devtools/build/android/proto/serialize_format.proto
index 32670daab2..e546aa0ad7 100644
--- a/src/tools/android/java/com/google/devtools/build/android/proto/serialize_format.proto
+++ b/src/tools/android/java/com/google/devtools/build/android/proto/serialize_format.proto
@@ -38,6 +38,8 @@ message DataKey {
// The size of the associated value. Useful for calculating an offset.
// Required
optional int32 value_size = 6;
+ // Whether this DataKey is a reference to another DataKey.
+ optional bool reference = 7;
}
// The serialized format for a DataValue.
@@ -72,4 +74,5 @@ message DataValueXml {
repeated string list_value = 4;
optional string value = 5;
optional string value_type = 6;
+ repeated DataKey references = 7;
}
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 62fcf4b629..0ed66afaf7 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
@@ -18,8 +18,9 @@ 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.ImmutableSet;
-import com.google.common.collect.Ordering;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMap.Builder;
+import com.google.common.collect.Iterables;
import com.google.devtools.build.android.AndroidDataWritingVisitor;
import com.google.devtools.build.android.FullyQualifiedName;
import com.google.devtools.build.android.XmlResourceValue;
@@ -30,60 +31,90 @@ import com.google.devtools.build.android.proto.SerializeFormat.DataValueXml.XmlT
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Path;
-import java.util.Arrays;
-import java.util.List;
+import java.util.AbstractMap.SimpleEntry;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Map.Entry;
import java.util.Objects;
-import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
/**
* Represent an Android styleable resource.
*
- * <p>
- * Styleable resources are groups of attributes that can be applied to views. They are, for the most
- * part, vaguely documented (http://developer.android.com/training/custom-views/create-view
- * .html#customattr). It's worth noting that attributes declared inside a &lt;declare-styleable&gt;
- * tags, for example; <code>
- * <declare-styleable name="PieChart">
- * <attr name="showText" format="boolean" />
- * </declare-styleable>
- * </code>
+ * <p>Styleable resources are groups of attributes that can be applied to views. They are, for the
+ * most part, vaguely documented (http://developer.android.com/training/custom-views/create-view
+ * .html#customattr). It is important to note that attributes declared inside
+ * &lt;declare-styleable&gt; tags, for example; <code> <declare-styleable name="PieChart"> <attr
+ * name="showText" format="boolean" /> </declare-styleable> </code>
*
- * Can also be seen as: <code>
- * <attr name="showText" format="boolean" />
- * <declare-styleable name="PieChart">
- * <attr name="showText"/>
- * </declare-styleable>
- * </code>
+ * <p>Can also be seen as: <code> <attr name="showText" format="boolean" /> <declare-styleable
+ * name="PieChart"> <attr name="showText"/> </declare-styleable> </code>
*
- * <p>
- * The StyleableXmlValue only contains names of the attributes it holds, not definitions.
+ * <p>However, aapt will parse these two cases differently. In order to maintain the expected
+ * indexing for the styleable array
+ * (http://developer.android.com/reference/android/content/res/Resources.Theme.html
+ * #obtainStyledAttributes(android.util.AttributeSet, int[], int, int)) the styleable must track
+ * whether the attr is a reference or a definition, as aapt will sort the attributes first by attr
+ * format (the absence of format comes first, followed by alphabetical sorting by format, then
+ * sorting by declaration order in the source xml.)
*/
@Immutable
public class StyleableXmlResourceValue implements XmlResourceValue {
- public static final Function<String, String> ITEM_TO_ATTR =
- new Function<String, String>() {
- @Nullable
+ public static final Function<FullyQualifiedName, String> ITEM_TO_ATTR =
+ new Function<FullyQualifiedName, String>() {
@Override
- public String apply(@Nullable String input) {
- return String.format("<attr name='%s'/>", input);
+ public String apply(FullyQualifiedName input) {
+ return String.format("<attr name='%s'/>", input.name());
}
};
- private final ImmutableList<String> attrs;
- private StyleableXmlResourceValue(ImmutableList<String> attrs) {
+ static final Function<Entry<FullyQualifiedName, Boolean>, SerializeFormat.DataKey>
+ FULLY_QUALIFIED_NAME_TO_DATA_KEY =
+ new Function<Entry<FullyQualifiedName, Boolean>, SerializeFormat.DataKey>() {
+ @Override
+ public SerializeFormat.DataKey apply(Entry<FullyQualifiedName, Boolean> input) {
+ return input.getKey().toSerializedBuilder().setReference(input.getValue()).build();
+ }
+ };
+
+ static final Function<SerializeFormat.DataKey, Entry<FullyQualifiedName, Boolean>>
+ DATA_KEY_TO_FULLY_QUALIFIED_NAME =
+ new Function<SerializeFormat.DataKey, Entry<FullyQualifiedName, Boolean>>() {
+ @Override
+ public Entry<FullyQualifiedName, Boolean> apply(SerializeFormat.DataKey input) {
+ FullyQualifiedName key = FullyQualifiedName.fromProto(input);
+ return new SimpleEntry<FullyQualifiedName, Boolean>(key, input.getReference());
+ }
+ };
+
+ private final ImmutableMap<FullyQualifiedName, Boolean> attrs;
+
+ private StyleableXmlResourceValue(ImmutableMap<FullyQualifiedName, Boolean> attrs) {
this.attrs = attrs;
}
- public static XmlResourceValue of(List<String> attrs) {
- return new StyleableXmlResourceValue(ImmutableList.copyOf(attrs));
+ @VisibleForTesting
+ public static XmlResourceValue createAllAttrAsReferences(FullyQualifiedName... attrNames) {
+ return of(createAttrDefinitionMap(attrNames, Boolean.FALSE));
+ }
+
+ private static Map<FullyQualifiedName, Boolean> createAttrDefinitionMap(
+ FullyQualifiedName[] attrNames, Boolean definitionType) {
+ Builder<FullyQualifiedName, Boolean> builder = ImmutableMap.builder();
+ for (FullyQualifiedName attrName : attrNames) {
+ builder.put(attrName, definitionType);
+ }
+ return builder.build();
}
@VisibleForTesting
- public static XmlResourceValue of(String... attrs) {
- return new StyleableXmlResourceValue(
- Ordering.natural().immutableSortedCopy(Arrays.asList(attrs)));
+ public static XmlResourceValue createAllAttrAsDefinitions(FullyQualifiedName... attrNames) {
+ return of(createAttrDefinitionMap(attrNames, Boolean.TRUE));
+ }
+
+ public static XmlResourceValue of(Map<FullyQualifiedName, Boolean> attrs) {
+ return new StyleableXmlResourceValue(ImmutableMap.copyOf(attrs));
}
@Override
@@ -95,7 +126,7 @@ public class StyleableXmlResourceValue implements XmlResourceValue {
ImmutableList.of(
String.format("<!-- %s -->", source),
String.format("<declare-styleable name='%s'>", key.name())))
- .append(FluentIterable.from(attrs).transform(ITEM_TO_ATTR))
+ .append(FluentIterable.from(attrs.keySet()).transform(ITEM_TO_ATTR))
.append("</declare-styleable>"));
}
@@ -107,11 +138,14 @@ public class StyleableXmlResourceValue implements XmlResourceValue {
.setXmlValue(
SerializeFormat.DataValueXml.newBuilder()
.setType(XmlType.STYLEABLE)
- .addAllListValue(attrs)));
+ .addAllReferences(
+ Iterables.transform(attrs.entrySet(), FULLY_QUALIFIED_NAME_TO_DATA_KEY))));
}
public static XmlResourceValue from(SerializeFormat.DataValueXml proto) {
- return of(proto.getListValueList());
+ return of(
+ ImmutableMap.copyOf(
+ Iterables.transform(proto.getReferencesList(), DATA_KEY_TO_FULLY_QUALIFIED_NAME)));
}
@Override
@@ -152,9 +186,20 @@ public class StyleableXmlResourceValue implements XmlResourceValue {
throw new IllegalArgumentException(value + "is not combinable with " + this);
}
StyleableXmlResourceValue styleable = (StyleableXmlResourceValue) value;
- return of(
- Ordering.natural()
- .sortedCopy(
- ImmutableSet.<String>builder().addAll(attrs).addAll(styleable.attrs).build()));
+ Map<FullyQualifiedName, Boolean> combined = new LinkedHashMap<>();
+ combined.putAll(attrs);
+ for (Entry<FullyQualifiedName, Boolean> attr : styleable.attrs.entrySet()) {
+ if (combined.containsKey(attr.getKey())) {
+ // if either attr is defined in the styleable, the attr will be defined in the styleable.
+ if (attr.getValue() || combined.get(attr.getKey())) {
+ combined.put(attr.getKey(), Boolean.TRUE);
+ } else {
+ combined.put(attr.getKey(), Boolean.FALSE);
+ }
+ } else {
+ combined.put(attr.getKey(), attr.getValue());
+ }
+ }
+ return of(combined);
}
}