Add plumbing to allow querying all resources referenced from (protobuf-)XML
These are needed to validate resource references, i.e. references should be to
internal definitions or public resources from direct dependencies.
This entails adding a field for the protos to android/xml/*XmlResourceValue,
which is technically redundant to the existing strings/maps. The latter can
be removed once we stop parsing XML---presently the XML parsing is only done
in AAR generation for the benefit of merging res/ directories.
PiperOrigin-RevId: 289551860
diff --git a/src/test/java/com/google/devtools/build/android/DataValueFileTest.java b/src/test/java/com/google/devtools/build/android/DataValueFileTest.java
index 0b22e6c..28479c1 100644
--- a/src/test/java/com/google/devtools/build/android/DataValueFileTest.java
+++ b/src/test/java/com/google/devtools/build/android/DataValueFileTest.java
@@ -35,21 +35,24 @@
DataValueFile.of(
Visibility.UNKNOWN,
DataSource.of(DependencyInfo.UNKNOWN, fs.getPath("val1")),
- /*fingerprint=*/ null);
+ /*fingerprint=*/ null,
+ /*rootXmlNode=*/ null);
DataValueFile val2a =
DataValueFile.of(
Visibility.UNKNOWN,
DataSource.of(
DependencyInfo.create("lib2a", DependencyInfo.DependencyType.UNKNOWN),
fs.getPath("val2")),
- /*fingerprint=*/ null);
+ /*fingerprint=*/ null,
+ /*rootXmlNode=*/ null);
DataValueFile val2b =
DataValueFile.of(
Visibility.UNKNOWN,
DataSource.of(
DependencyInfo.create("lib2b", DependencyInfo.DependencyType.UNKNOWN),
fs.getPath("val2")),
- /*fingerprint=*/ null);
+ /*fingerprint=*/ null,
+ /*rootXmlNode=*/ null);
assertThat(val1.valueEquals(val2a)).isFalse();
assertThat(val2a.valueEquals(val2b)).isTrue();
@@ -61,17 +64,20 @@
DataValueFile.of(
Visibility.UNKNOWN,
DataSource.of(DependencyInfo.UNKNOWN, fs.getPath("asdf")),
- HashCode.fromInt(1));
+ HashCode.fromInt(1),
+ /*rootXmlNode=*/ null);
DataValueFile val2a =
DataValueFile.of(
Visibility.UNKNOWN,
DataSource.of(DependencyInfo.UNKNOWN, fs.getPath("qwerty")),
- HashCode.fromInt(2));
+ HashCode.fromInt(2),
+ /*rootXmlNode=*/ null);
DataValueFile val2b =
DataValueFile.of(
Visibility.UNKNOWN,
DataSource.of(DependencyInfo.UNKNOWN, fs.getPath("hunter2")),
- HashCode.fromInt(2));
+ HashCode.fromInt(2),
+ /*rootXmlNode=*/ null);
assertThat(val1.valueEquals(val2a)).isFalse();
assertThat(val2a.valueEquals(val2b)).isTrue();
@@ -80,9 +86,15 @@
@Test
public void valueEquals_checkVisibility() throws Exception {
DataSource dataSource = DataSource.of(DependencyInfo.UNKNOWN, fs.getPath("x"));
- DataValueFile val1 = DataValueFile.of(Visibility.PRIVATE, dataSource, /*fingerprint=*/ null);
- DataValueFile val2a = DataValueFile.of(Visibility.PUBLIC, dataSource, /*fingerprint=*/ null);
- DataValueFile val2b = DataValueFile.of(Visibility.PUBLIC, dataSource, /*fingerprint=*/ null);
+ DataValueFile val1 =
+ DataValueFile.of(
+ Visibility.PRIVATE, dataSource, /*fingerprint=*/ null, /*rootXmlNode=*/ null);
+ DataValueFile val2a =
+ DataValueFile.of(
+ Visibility.PUBLIC, dataSource, /*fingerprint=*/ null, /*rootXmlNode=*/ null);
+ DataValueFile val2b =
+ DataValueFile.of(
+ Visibility.PUBLIC, dataSource, /*fingerprint=*/ null, /*rootXmlNode=*/ null);
assertThat(val1.valueEquals(val2a)).isFalse();
assertThat(val2a.valueEquals(val2b)).isTrue();
diff --git a/src/test/java/com/google/devtools/build/android/ParsedAndroidDataBuilder.java b/src/test/java/com/google/devtools/build/android/ParsedAndroidDataBuilder.java
index 35c5b9b..62689ff 100644
--- a/src/test/java/com/google/devtools/build/android/ParsedAndroidDataBuilder.java
+++ b/src/test/java/com/google/devtools/build/android/ParsedAndroidDataBuilder.java
@@ -151,7 +151,8 @@
KeyValueConsumer<DataKey, DataResource> consumer) {
consumer.accept(
factory.parse(rawKey),
- DataValueFile.of(Visibility.UNKNOWN, source, /*fingerprint=*/ null));
+ DataValueFile.of(
+ Visibility.UNKNOWN, source, /*fingerprint=*/ null, /*rootXmlNode=*/ null));
}
@Override
@@ -159,7 +160,8 @@
target.accept(
RelativeAssetPath.Factory.of(chooseRoot(defaultRoot).resolve("assets"))
.create(source.getPath()),
- DataValueFile.of(Visibility.UNKNOWN, source, /*fingerprint=*/ null));
+ DataValueFile.of(
+ Visibility.UNKNOWN, source, /*fingerprint=*/ null, /*rootXmlNode=*/ null));
}
};
}
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 0e30e05..b03ba57 100644
--- a/src/test/java/com/google/devtools/build/android/ParsedAndroidDataTest.java
+++ b/src/test/java/com/google/devtools/build/android/ParsedAndroidDataTest.java
@@ -101,9 +101,16 @@
ImmutableSet.of(
MergeConflict.of(
key,
- DataValueFile.of(Visibility.UNKNOWN, assetSource, /*fingerprint=*/ null),
DataValueFile.of(
- Visibility.UNKNOWN, otherAssetSource, /*fingerprint=*/ null))),
+ Visibility.UNKNOWN,
+ assetSource,
+ /*fingerprint=*/ null,
+ /*rootXmlNode=*/ null),
+ DataValueFile.of(
+ Visibility.UNKNOWN,
+ otherAssetSource,
+ /*fingerprint=*/ null,
+ /*rootXmlNode=*/ null))),
ImmutableMap.<DataKey, DataResource>of(),
ImmutableMap.<DataKey, DataResource>of(),
ImmutableMap.<DataKey, DataAsset>of(
@@ -111,7 +118,8 @@
DataValueFile.of(
Visibility.UNKNOWN,
otherAssetSource.overwrite(assetSource),
- /*fingerprint=*/ null))));
+ /*fingerprint=*/ null,
+ /*rootXmlNode=*/ null))));
}
@Test
@@ -357,9 +365,15 @@
MergeConflict.of(
drawableMenu,
DataValueFile.of(
- Visibility.UNKNOWN, rootDrawableMenuPath, /*fingerprint=*/ null),
+ Visibility.UNKNOWN,
+ rootDrawableMenuPath,
+ /*fingerprint=*/ null,
+ /*rootXmlNode=*/ null),
DataValueFile.of(
- Visibility.UNKNOWN, otherRootDrawableMenuPath, /*fingerprint=*/ null)),
+ Visibility.UNKNOWN,
+ otherRootDrawableMenuPath,
+ /*fingerprint=*/ null,
+ /*rootXmlNode=*/ null)),
MergeConflict.of(
stringExit,
DataResourceXml.createWithNoNamespace(
@@ -382,7 +396,8 @@
DataValueFile.of(
Visibility.UNKNOWN,
otherRootDrawableMenuPath.overwrite(rootDrawableMenuPath),
- /*fingerprint=*/ null), // value
+ /*fingerprint=*/ null,
+ /*rootXmlNode=*/ null), // value
attributeFoo, // key
DataResourceXml.createWithNoNamespace(
otherRootValuesPath.overwrite(rootValuesPath),
diff --git a/src/tools/android/java/com/google/devtools/build/android/AndroidCompiledDataDeserializer.java b/src/tools/android/java/com/google/devtools/build/android/AndroidCompiledDataDeserializer.java
index 69f4030..8645985 100644
--- a/src/tools/android/java/com/google/devtools/build/android/AndroidCompiledDataDeserializer.java
+++ b/src/tools/android/java/com/google/devtools/build/android/AndroidCompiledDataDeserializer.java
@@ -32,6 +32,7 @@
import com.android.aapt.Resources.Package;
import com.android.aapt.Resources.ResourceTable;
import com.android.aapt.Resources.Value;
+import com.android.aapt.Resources.XmlNode;
import com.android.ide.common.resources.configuration.CountryCodeQualifier;
import com.android.ide.common.resources.configuration.DensityQualifier;
import com.android.ide.common.resources.configuration.FolderConfiguration;
@@ -286,7 +287,11 @@
// TODO(b/26297204): use visibility from ResourceTable instead of UNKNOWN
DataResource dataResource =
resourceValue.getItem().hasFile()
- ? DataValueFile.of(Visibility.UNKNOWN, dataSource, /*fingerprint=*/ null)
+ ? DataValueFile.of(
+ Visibility.UNKNOWN,
+ dataSource,
+ /*fingerprint=*/ null,
+ /*rootXmlNode=*/ null)
: DataResourceXml.from(
resourceValue,
Visibility.UNKNOWN,
@@ -528,7 +533,12 @@
// TODO(b/26297204): use visibility from ResourceTable instead of UNKNOWN
consumers.overwritingConsumer.accept(
- fqn, DataValueFile.of(Visibility.UNKNOWN, dataSource, compiledFileWithData.fingerprint()));
+ fqn,
+ DataValueFile.of(
+ Visibility.UNKNOWN,
+ dataSource,
+ compiledFileWithData.fingerprint(),
+ compiledFileWithData.rootXmlNode()));
for (CompiledFile.Symbol exportedSymbol : compiledFile.getExportedSymbolList()) {
if (exportedSymbol.getResourceName().startsWith("android:")) {
@@ -628,6 +638,20 @@
consumeResourceTable(dependencyInfo, consumers, resourceTable);
}
+ public Map<DataKey, DataResource> read(DependencyInfo dependencyInfo, Path inPath) {
+ Map<DataKey, DataResource> resources = new LinkedHashMap<>();
+ read(
+ dependencyInfo,
+ inPath,
+ KeyValueConsumers.of(
+ resources::put,
+ resources::put,
+ (key, value) -> {
+ throw new IllegalStateException(String.format("Unexpected asset in %s", inPath));
+ }));
+ return resources;
+ }
+
@Override
public void read(DependencyInfo dependencyInfo, Path inPath, KeyValueConsumers consumers) {
Stopwatch timer = Stopwatch.createStarted();
@@ -721,22 +745,27 @@
long payloadSize = dataInputStream.readLong();
byte[] headerBytes = readBytesAndSkipPadding(dataInputStream, headerSize);
+ CompiledFile compiledFile =
+ CompiledFile.parseFrom(headerBytes, ExtensionRegistry.getEmptyRegistry());
+
HashCode fingerprint;
+ XmlNode rootXmlNode;
if (includeFileContentsForValidation) {
byte[] payloadBytes = readBytesAndSkipPadding(dataInputStream, (int) payloadSize);
fingerprint = Hashing.goodFastHash(/*minimumBits=*/ 64).hashBytes(payloadBytes);
- // TODO(b/26297204): verify that XML payloads reference accessible resources
+ rootXmlNode =
+ compiledFile.getType() == Resources.FileReference.Type.PROTO_XML
+ ? XmlNode.parseFrom(payloadBytes, ExtensionRegistry.getEmptyRegistry())
+ : null;
} else {
ByteStreams.skipFully(
dataInputStream,
((payloadSize + 1) / AAPT_FLAT_FILE_ALIGNMENT) * AAPT_FLAT_FILE_ALIGNMENT);
fingerprint = null;
+ rootXmlNode = null;
}
- compiledFiles.add(
- CompiledFileWithData.create(
- CompiledFile.parseFrom(headerBytes, ExtensionRegistry.getEmptyRegistry()),
- fingerprint));
+ compiledFiles.add(CompiledFileWithData.create(compiledFile, fingerprint, rootXmlNode));
}
break; // TODO(b/146528588): remove this line
@@ -832,9 +861,13 @@
@Nullable
abstract HashCode fingerprint();
- static CompiledFileWithData create(CompiledFile compiledFile, @Nullable HashCode fingerprint) {
+ @Nullable
+ abstract XmlNode rootXmlNode();
+
+ static CompiledFileWithData create(
+ CompiledFile compiledFile, @Nullable HashCode fingerprint, @Nullable XmlNode xmlNode) {
return new AutoValue_AndroidCompiledDataDeserializer_CompiledFileWithData(
- compiledFile, fingerprint);
+ compiledFile, fingerprint, xmlNode);
}
}
}
diff --git a/src/tools/android/java/com/google/devtools/build/android/AndroidParsedDataDeserializer.java b/src/tools/android/java/com/google/devtools/build/android/AndroidParsedDataDeserializer.java
index f86f58e..8cd327e 100644
--- a/src/tools/android/java/com/google/devtools/build/android/AndroidParsedDataDeserializer.java
+++ b/src/tools/android/java/com/google/devtools/build/android/AndroidParsedDataDeserializer.java
@@ -138,7 +138,9 @@
KeyValueConsumer<DataKey, DataValue> value =
(KeyValueConsumer<DataKey, DataValue>) entry.getValue();
value.accept(
- entry.getKey(), DataValueFile.of(Visibility.UNKNOWN, source, /*fingerprint=*/ null));
+ entry.getKey(),
+ DataValueFile.of(
+ Visibility.UNKNOWN, source, /*fingerprint=*/ null, /*rootXmlNode=*/ null));
}
}
}
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 1605392..d5768cf 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
@@ -13,7 +13,10 @@
// limitations under the License.
package com.google.devtools.build.android;
+import com.android.aapt.Resources.Reference;
+import com.google.common.collect.ImmutableList;
import com.google.devtools.build.android.AndroidResourceMerger.MergingException;
+import com.google.devtools.build.android.resources.Visibility;
/** Represents an Android Resource parsed from an xml or binary file. */
public interface DataResource extends DataValue {
@@ -35,4 +38,10 @@
/** Overwrite another {@link DataResource}. */
DataResource overwrite(DataResource other);
+
+ /** Visibility of this resource as denoted by a {@code <public>} tag, or lack thereof. */
+ Visibility getVisibility();
+
+ /** Resources referenced via XML attributes or proxying resource definitions. */
+ ImmutableList<Reference> getReferencedResources();
}
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 447ab50..e78025c 100644
--- a/src/tools/android/java/com/google/devtools/build/android/DataResourceXml.java
+++ b/src/tools/android/java/com/google/devtools/build/android/DataResourceXml.java
@@ -17,10 +17,12 @@
import static com.android.resources.ResourceType.ID;
import static com.android.resources.ResourceType.PUBLIC;
+import com.android.aapt.Resources.Reference;
import com.android.aapt.Resources.Value;
import com.android.resources.ResourceType;
import com.google.common.base.MoreObjects;
import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.devtools.build.android.AndroidCompiledDataDeserializer.ReferenceResolver;
import com.google.devtools.build.android.FullyQualifiedName.Factory;
@@ -452,4 +454,14 @@
DataResourceXml other = (DataResourceXml) value;
return xml.compareMergePriorityTo(other.xml);
}
+
+ @Override
+ public Visibility getVisibility() {
+ return xml.getVisibility();
+ }
+
+ @Override
+ public ImmutableList<Reference> getReferencedResources() {
+ return xml.getReferencedResources();
+ }
}
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 1386179..74949b6 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
@@ -13,11 +13,15 @@
// limitations under the License.
package com.google.devtools.build.android;
+import com.android.aapt.Resources.Reference;
+import com.android.aapt.Resources.XmlNode;
import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableList;
import com.google.common.hash.HashCode;
import com.google.devtools.build.android.AndroidResourceMerger.MergingException;
import com.google.devtools.build.android.proto.SerializeFormat;
import com.google.devtools.build.android.resources.Visibility;
+import com.google.devtools.build.android.xml.ProtoXmlUtils;
import com.google.protobuf.CodedOutputStream;
import java.io.IOException;
import java.io.OutputStream;
@@ -35,22 +39,35 @@
private final Visibility visibility;
private final DataSource source;
@Nullable private final HashCode fingerprint;
+ // Set only for XML-based resources, and only when reading the output of aapt2.
+ @Nullable private final XmlNode rootXmlNode;
- private DataValueFile(Visibility visibility, DataSource source, @Nullable HashCode fingerprint) {
+ private DataValueFile(
+ Visibility visibility,
+ DataSource source,
+ @Nullable HashCode fingerprint,
+ @Nullable XmlNode rootXmlNode) {
this.visibility = visibility;
this.source = source;
this.fingerprint = fingerprint;
+ this.rootXmlNode = rootXmlNode;
}
@Deprecated
public static DataValueFile of(Path source) {
return of(
- Visibility.UNKNOWN, DataSource.of(DependencyInfo.UNKNOWN, source), /*fingerprint=*/ null);
+ Visibility.UNKNOWN,
+ DataSource.of(DependencyInfo.UNKNOWN, source),
+ /*fingerprint=*/ null,
+ /*rootXmlNode=*/ null);
}
public static DataValueFile of(
- Visibility visibility, DataSource source, @Nullable HashCode fingerprint) {
- return new DataValueFile(visibility, source, fingerprint);
+ Visibility visibility,
+ DataSource source,
+ @Nullable HashCode fingerprint,
+ @Nullable XmlNode rootXmlNode) {
+ return new DataValueFile(visibility, source, fingerprint, rootXmlNode);
}
/** Creates a {@link DataValueFile} from a {@link SerializeFormat#DataValue}. */
@@ -117,7 +134,8 @@
if (equals(resource)) {
return this;
}
- return new DataValueFile(visibility, source.overwrite(resource.source()), fingerprint);
+ return new DataValueFile(
+ visibility, source.overwrite(resource.source()), fingerprint, rootXmlNode);
}
@Override
@@ -125,7 +143,8 @@
if (equals(asset)) {
return this;
}
- return new DataValueFile(visibility, source.overwrite(asset.source()), fingerprint);
+ return new DataValueFile(
+ visibility, source.overwrite(asset.source()), fingerprint, rootXmlNode);
}
@Override
@@ -135,7 +154,7 @@
@Override
public DataValue update(DataSource source) {
- return new DataValueFile(visibility, source, fingerprint);
+ return new DataValueFile(visibility, source, fingerprint, rootXmlNode);
}
@Override
@@ -168,4 +187,18 @@
public int compareMergePriorityTo(DataValue value) {
return 0;
}
+
+ @Override
+ public Visibility getVisibility() {
+ return visibility;
+ }
+
+ @Override
+ public ImmutableList<Reference> getReferencedResources() {
+ if (rootXmlNode == null) {
+ return ImmutableList.of();
+ } else {
+ return ProtoXmlUtils.getAllResourceReferences(rootXmlNode);
+ }
+ }
}
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 eae002b..2cc7f09 100644
--- a/src/tools/android/java/com/google/devtools/build/android/XmlResourceValue.java
+++ b/src/tools/android/java/com/google/devtools/build/android/XmlResourceValue.java
@@ -13,6 +13,9 @@
// limitations under the License.
package com.google.devtools.build.android;
+import com.android.aapt.Resources.Reference;
+import com.google.common.collect.ImmutableList;
+import com.google.devtools.build.android.resources.Visibility;
import com.google.devtools.build.android.xml.Namespaces;
import java.io.IOException;
import java.io.OutputStream;
@@ -59,4 +62,10 @@
/** Returns a representation of the xml value as a string suitable for conflict messages. */
String asConflictStringWith(DataSource source);
+
+ /** Visibility of this resource as denoted by a {@code <public>} tag, or lack thereof. */
+ Visibility getVisibility();
+
+ /** Resources referenced via XML attributes or proxying resource definitions. */
+ ImmutableList<Reference> getReferencedResources();
}
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 51d0cb6..51f7a9c 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
@@ -16,6 +16,7 @@
import com.android.aapt.Resources.Array;
import com.android.aapt.Resources.Array.Element;
import com.android.aapt.Resources.Item;
+import com.android.aapt.Resources.Reference;
import com.android.aapt.Resources.Value;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.MoreObjects;
@@ -92,16 +93,20 @@
}
private final Visibility visibility;
+ private final Array array;
+ // TODO(b/112848607): remove the weakly-typed "values" member in favor of "array" above.
private final ImmutableList<String> values;
private final ArrayType arrayType;
private final ImmutableMap<String, String> attributes;
private ArrayXmlResourceValue(
Visibility visibility,
+ Array array,
ArrayType arrayType,
List<String> values,
Map<String, String> attributes) {
this.visibility = visibility;
+ this.array = array;
this.arrayType = arrayType;
this.values = ImmutableList.copyOf(values);
this.attributes = ImmutableMap.copyOf(attributes);
@@ -118,7 +123,8 @@
public static XmlResourceValue of(
ArrayType arrayType, List<String> values, ImmutableMap<String, String> attributes) {
- return new ArrayXmlResourceValue(Visibility.UNKNOWN, arrayType, values, attributes);
+ return new ArrayXmlResourceValue(
+ Visibility.UNKNOWN, Array.getDefaultInstance(), arrayType, values, attributes);
}
@SuppressWarnings("deprecation")
@@ -145,7 +151,7 @@
}
}
- return new ArrayXmlResourceValue(visibility, ArrayType.ARRAY, items, ImmutableMap.of());
+ return new ArrayXmlResourceValue(visibility, array, ArrayType.ARRAY, items, ImmutableMap.of());
}
@Override
@@ -195,6 +201,7 @@
}
ArrayXmlResourceValue other = (ArrayXmlResourceValue) obj;
return Objects.equals(visibility, other.visibility)
+ // TODO(b/112848607): include the "array" proto in comparison; right now it's redundant.
&& Objects.equals(arrayType, other.arrayType)
&& Objects.equals(values, other.values)
&& Objects.equals(attributes, other.attributes);
@@ -257,4 +264,17 @@
public String asConflictStringWith(DataSource source) {
return source.asConflictString();
}
+
+ @Override
+ public Visibility getVisibility() {
+ return visibility;
+ }
+
+ @Override
+ public ImmutableList<Reference> getReferencedResources() {
+ return array.getElementList().stream()
+ .filter(element -> element.getItem().hasRef())
+ .map(element -> element.getItem().getRef())
+ .collect(ImmutableList.toImmutableList());
+ }
}
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 f983ff0..dc8113a 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
@@ -18,6 +18,7 @@
import com.android.aapt.Resources.Attribute;
import com.android.aapt.Resources.Attribute.Symbol;
+import com.android.aapt.Resources.Reference;
import com.android.aapt.Resources.Value;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.MoreObjects;
@@ -817,4 +818,14 @@
"%s [format(s): %s], [weak: %s]",
source.asConflictString(), String.join("|", this.formats.keySet()), weak);
}
+
+ @Override
+ public Visibility getVisibility() {
+ return visibility;
+ }
+
+ @Override
+ public ImmutableList<Reference> getReferencedResources() {
+ return ImmutableList.of();
+ }
}
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 1bdf9d0..dd316ce 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
@@ -13,11 +13,13 @@
// limitations under the License.
package com.google.devtools.build.android.xml;
+import com.android.aapt.Resources.Reference;
import com.android.aapt.Resources.Value;
import com.android.resources.ResourceType;
import com.google.common.base.MoreObjects;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableList;
import com.google.devtools.build.android.AndroidDataWritingVisitor;
import com.google.devtools.build.android.AndroidDataWritingVisitor.StartTag;
import com.google.devtools.build.android.AndroidResourceSymbolSink;
@@ -173,4 +175,14 @@
public String asConflictStringWith(DataSource source) {
return source.asConflictString();
}
+
+ @Override
+ public Visibility getVisibility() {
+ return visibility;
+ }
+
+ @Override
+ public ImmutableList<Reference> getReferencedResources() {
+ return ImmutableList.of();
+ }
}
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 0f5acbb..d76cd19 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
@@ -14,8 +14,10 @@
package com.google.devtools.build.android.xml;
import com.android.aapt.Resources.Plural;
+import com.android.aapt.Resources.Reference;
import com.android.aapt.Resources.Value;
import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.xml.XmlEscapers;
import com.google.devtools.build.android.AndroidDataWritingVisitor;
@@ -58,14 +60,18 @@
private static final QName PLURALS = QName.valueOf("plurals");
private final Visibility visibility;
+ private final Plural plural;
+ // TODO(b/112848607): remove the weakly-typed "values" member in favor of "plural" above.
private final ImmutableMap<String, String> values;
private final ImmutableMap<String, String> attributes;
private PluralXmlResourceValue(
Visibility visibility,
+ Plural plural,
ImmutableMap<String, String> attributes,
ImmutableMap<String, String> values) {
this.visibility = visibility;
+ this.plural = plural;
this.attributes = attributes;
this.values = values;
}
@@ -76,7 +82,8 @@
public static XmlResourceValue createWithAttributesAndValues(
ImmutableMap<String, String> attributes, ImmutableMap<String, String> values) {
- return new PluralXmlResourceValue(Visibility.UNKNOWN, attributes, values);
+ return new PluralXmlResourceValue(
+ Visibility.UNKNOWN, Plural.getDefaultInstance(), attributes, values);
}
@Override
@@ -124,6 +131,7 @@
}
PluralXmlResourceValue other = (PluralXmlResourceValue) obj;
return Objects.equals(visibility, other.visibility)
+ // TODO(b/112848607): include the "plural" proto in comparison; right now it's redundant.
&& Objects.equals(values, other.values)
&& Objects.equals(attributes, other.attributes);
}
@@ -158,7 +166,8 @@
items.put(name, value);
}
- return new PluralXmlResourceValue(visibility, ImmutableMap.of(), ImmutableMap.copyOf(items));
+ return new PluralXmlResourceValue(
+ visibility, plural, ImmutableMap.of(), ImmutableMap.copyOf(items));
}
@Override
@@ -195,4 +204,17 @@
public String asConflictStringWith(DataSource source) {
return source.asConflictString();
}
+
+ @Override
+ public Visibility getVisibility() {
+ return visibility;
+ }
+
+ @Override
+ public ImmutableList<Reference> getReferencedResources() {
+ return plural.getEntryList().stream()
+ .filter(entry -> entry.getItem().hasRef())
+ .map(entry -> entry.getItem().getRef())
+ .collect(ImmutableList.toImmutableList());
+ }
}
diff --git a/src/tools/android/java/com/google/devtools/build/android/xml/PublicXmlResourceValue.java b/src/tools/android/java/com/google/devtools/build/android/xml/PublicXmlResourceValue.java
index 6de5158..c188401 100644
--- a/src/tools/android/java/com/google/devtools/build/android/xml/PublicXmlResourceValue.java
+++ b/src/tools/android/java/com/google/devtools/build/android/xml/PublicXmlResourceValue.java
@@ -14,10 +14,12 @@
package com.google.devtools.build.android.xml;
import com.android.SdkConstants;
+import com.android.aapt.Resources.Reference;
import com.android.resources.ResourceType;
import com.google.common.base.MoreObjects;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.google.devtools.build.android.AndroidDataWritingVisitor;
@@ -28,6 +30,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.resources.Visibility;
import java.io.IOException;
import java.io.OutputStream;
import java.util.EnumMap;
@@ -185,4 +188,15 @@
public String asConflictStringWith(DataSource source) {
return source.asConflictString();
}
+
+ @Override
+ public Visibility getVisibility() {
+ // <public id="..."> itself is not a value
+ return Visibility.UNKNOWN;
+ }
+
+ @Override
+ public ImmutableList<Reference> getReferencedResources() {
+ return ImmutableList.of();
+ }
}
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 0979c9b..62086fb 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
@@ -13,8 +13,10 @@
// limitations under the License.
package com.google.devtools.build.android.xml;
+import com.android.aapt.Resources.Reference;
import com.google.common.base.Joiner;
import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.devtools.build.android.AndroidDataWritingVisitor;
import com.google.devtools.build.android.AndroidResourceSymbolSink;
@@ -25,6 +27,7 @@
import com.google.devtools.build.android.XmlResourceValues;
import com.google.devtools.build.android.proto.SerializeFormat;
import com.google.devtools.build.android.proto.SerializeFormat.DataValueXml;
+import com.google.devtools.build.android.resources.Visibility;
import com.google.errorprone.annotations.Immutable;
import java.io.IOException;
import java.io.OutputStream;
@@ -189,4 +192,14 @@
}
return source.asConflictString();
}
+
+ @Override
+ public Visibility getVisibility() {
+ return Visibility.UNKNOWN;
+ }
+
+ @Override
+ public ImmutableList<Reference> getReferencedResources() {
+ return ImmutableList.of();
+ }
}
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 99b81d37..1a17f3c1 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
@@ -15,11 +15,13 @@
import com.android.aapt.Resources.Item;
import com.android.aapt.Resources.Primitive;
+import com.android.aapt.Resources.Reference;
import com.android.aapt.Resources.StyledString;
import com.android.aapt.Resources.StyledString.Span;
import com.android.aapt.Resources.Value;
import com.android.resources.ResourceType;
import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.xml.XmlEscapers;
import com.google.devtools.build.android.AndroidDataWritingVisitor;
@@ -110,7 +112,9 @@
}
private final Visibility visibility;
+ private final Item item;
private final ImmutableMap<String, String> attributes;
+ // TODO(b/112848607): remove untyped "value" String in favor of "item" above.
@Nullable private final String value;
private final Type valueType;
@@ -138,16 +142,19 @@
public static XmlResourceValue of(
Type valueType, ImmutableMap<String, String> attributes, @Nullable String value) {
- return new SimpleXmlResourceValue(Visibility.UNKNOWN, valueType, attributes, value);
+ return new SimpleXmlResourceValue(
+ Visibility.UNKNOWN, valueType, Item.getDefaultInstance(), attributes, value);
}
private SimpleXmlResourceValue(
Visibility visibility,
Type valueType,
+ Item item,
ImmutableMap<String, String> attributes,
String value) {
this.visibility = visibility;
this.valueType = valueType;
+ this.item = item;
this.value = value;
this.attributes = attributes;
}
@@ -209,6 +216,7 @@
return new SimpleXmlResourceValue(
visibility,
Type.valueOf(resourceType.toString().toUpperCase(Locale.ENGLISH)),
+ item,
attributes.build(),
stringValue);
}
@@ -326,6 +334,7 @@
return Objects.equals(visibility, other.visibility)
&& Objects.equals(valueType, other.valueType)
&& Objects.equals(attributes, other.attributes)
+ // TODO(b/112848607): include the "item" proto in comparison; right now it's redundant.
&& Objects.equals(value, other.value);
}
@@ -355,4 +364,14 @@
}
return source.asConflictString();
}
+
+ @Override
+ public Visibility getVisibility() {
+ return visibility;
+ }
+
+ @Override
+ public ImmutableList<Reference> getReferencedResources() {
+ return item.hasRef() ? ImmutableList.of(item.getRef()) : ImmutableList.of();
+ }
}
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 36c3b2b..39212c0 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,10 +13,12 @@
// limitations under the License.
package com.google.devtools.build.android.xml;
+import com.android.aapt.Resources.Reference;
import com.android.aapt.Resources.Style;
import com.android.aapt.Resources.Value;
import com.google.common.base.Function;
import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.devtools.build.android.AndroidDataWritingVisitor;
import com.google.devtools.build.android.AndroidDataWritingVisitor.ValuesResourceDefinition;
@@ -66,11 +68,15 @@
};
private final Visibility visibility;
+ private final Style style;
+ // TODO(b/112848607): remove parent/values in favor of "style" above, or replace the Strings with
+ // stronger types.
private final String parent;
private final ImmutableMap<String, String> values;
public static StyleXmlResourceValue of(String parent, Map<String, String> values) {
- return new StyleXmlResourceValue(Visibility.UNKNOWN, parent, ImmutableMap.copyOf(values));
+ return new StyleXmlResourceValue(
+ Visibility.UNKNOWN, Style.getDefaultInstance(), parent, ImmutableMap.copyOf(values));
}
@SuppressWarnings("deprecation")
@@ -92,12 +98,16 @@
Map<String, String> items = itemMapFromProto(style);
- return new StyleXmlResourceValue(visibility, parent, ImmutableMap.copyOf(items));
+ return new StyleXmlResourceValue(visibility, style, parent, ImmutableMap.copyOf(items));
}
private StyleXmlResourceValue(
- Visibility visibility, @Nullable String parent, ImmutableMap<String, String> values) {
+ Visibility visibility,
+ Style style,
+ @Nullable String parent,
+ ImmutableMap<String, String> values) {
this.visibility = visibility;
+ this.style = style;
this.parent = parent;
this.values = values;
}
@@ -188,6 +198,7 @@
StyleXmlResourceValue other = (StyleXmlResourceValue) obj;
return Objects.equals(visibility, other.visibility)
&& Objects.equals(parent, other.parent)
+ // TODO(b/112848607): include the "style" proto in comparison; right now it's redundant.
&& Objects.equals(values, other.values);
}
@@ -213,4 +224,24 @@
public String asConflictStringWith(DataSource source) {
return source.asConflictString();
}
+
+ @Override
+ public Visibility getVisibility() {
+ return visibility;
+ }
+
+ @Override
+ public ImmutableList<Reference> getReferencedResources() {
+ ImmutableList.Builder<Reference> result = ImmutableList.builder();
+ if (style.hasParent()) {
+ result.add(style.getParent());
+ }
+ for (Style.Entry entry : style.getEntryList()) {
+ result.add(entry.getKey());
+ if (entry.getItem().hasRef()) {
+ result.add(entry.getItem().getRef());
+ }
+ }
+ return result.build();
+ }
}
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 0e17fe1..e996e42 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
@@ -13,11 +13,13 @@
// limitations under the License.
package com.google.devtools.build.android.xml;
+import com.android.aapt.Resources.Reference;
import com.android.aapt.Resources.Styleable;
import com.android.aapt.Resources.Value;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.devtools.build.android.AndroidCompiledDataDeserializer.ReferenceResolver;
@@ -83,11 +85,15 @@
};
private final Visibility visibility;
- private final ImmutableMap<FullyQualifiedName, Boolean> attrs;
+ private final Styleable styleable;
+ // TODO(b/145837824,b/112848607): change to a set, if not removing this outright. Per the Javadoc
+ // for this class, the "should inline" bit is used to mimic how AAPT1 assigns IDs.
+ private final ImmutableMap<FullyQualifiedName, /*shouldInline=*/ Boolean> attrs;
private StyleableXmlResourceValue(
- Visibility visibility, ImmutableMap<FullyQualifiedName, Boolean> attrs) {
+ Visibility visibility, Styleable styleable, ImmutableMap<FullyQualifiedName, Boolean> attrs) {
this.visibility = visibility;
+ this.styleable = styleable;
this.attrs = attrs;
}
@@ -111,11 +117,13 @@
}
public static XmlResourceValue of(Map<FullyQualifiedName, Boolean> attrs) {
- return new StyleableXmlResourceValue(Visibility.UNKNOWN, ImmutableMap.copyOf(attrs));
+ return new StyleableXmlResourceValue(
+ Visibility.UNKNOWN, Styleable.getDefaultInstance(), ImmutableMap.copyOf(attrs));
}
public static XmlResourceValue of(Visibility visibility, Map<FullyQualifiedName, Boolean> attrs) {
- return new StyleableXmlResourceValue(visibility, ImmutableMap.copyOf(attrs));
+ return new StyleableXmlResourceValue(
+ visibility, Styleable.getDefaultInstance(), ImmutableMap.copyOf(attrs));
}
@Override
@@ -186,7 +194,7 @@
}
}
- return of(visibility, ImmutableMap.copyOf(attributes));
+ return new StyleableXmlResourceValue(visibility, styleable, ImmutableMap.copyOf(attributes));
}
@Override
@@ -199,6 +207,7 @@
if (!(obj instanceof StyleableXmlResourceValue)) {
return false;
}
+ // TODO(b/112848607): include the "styleable" proto in comparison; right now it's redundant.
StyleableXmlResourceValue other = (StyleableXmlResourceValue) obj;
return Objects.equals(visibility, other.visibility) && Objects.equals(attrs, other.attrs);
}
@@ -226,10 +235,10 @@
if (!(value instanceof StyleableXmlResourceValue)) {
throw new IllegalArgumentException(value + "is not combinable with " + this);
}
- StyleableXmlResourceValue styleable = (StyleableXmlResourceValue) value;
+ StyleableXmlResourceValue other = (StyleableXmlResourceValue) value;
Map<FullyQualifiedName, Boolean> combined = new LinkedHashMap<>();
combined.putAll(attrs);
- for (Map.Entry<FullyQualifiedName, Boolean> attr : styleable.attrs.entrySet()) {
+ for (Map.Entry<FullyQualifiedName, Boolean> attr : other.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())) {
@@ -243,7 +252,9 @@
}
// TODO(b/26297204): test that this makes sense and works
return new StyleableXmlResourceValue(
- Visibility.merge(visibility, styleable.visibility), ImmutableMap.copyOf(combined));
+ Visibility.merge(visibility, other.visibility),
+ styleable.toBuilder().mergeFrom(other.styleable).build(),
+ ImmutableMap.copyOf(combined));
}
@Override
@@ -255,4 +266,16 @@
public String asConflictStringWith(DataSource source) {
return source.asConflictString();
}
+
+ @Override
+ public Visibility getVisibility() {
+ return visibility;
+ }
+
+ @Override
+ public ImmutableList<Reference> getReferencedResources() {
+ return styleable.getEntryList().stream()
+ .map(entry -> entry.getAttr())
+ .collect(ImmutableList.toImmutableList());
+ }
}