diff options
author | Googler <noreply@google.com> | 2016-03-07 14:14:29 +0000 |
---|---|---|
committer | Damien Martin-Guillerez <dmarting@google.com> | 2016-03-08 03:44:29 +0000 |
commit | 3a55f01cc98068d0133349ddb5c0043b74555565 (patch) | |
tree | 2e9fa5753d66d5098922ced4baa9612820e7260d /src/tools/android/java/com/google/devtools/build/android/xml | |
parent | a9b7f4e1bd474afd005ad6943ce84bc8ef61eb97 (diff) |
Part 2 of 5: Optimize the resource merge process, XmlProcessing.
Introduces the XmlDataResource and all the associated cruft that comes with parsing Android Resource value xmls: XmlValues, XmlValue.
--
MOS_MIGRATED_REVID=116540181
Diffstat (limited to 'src/tools/android/java/com/google/devtools/build/android/xml')
7 files changed, 1057 insertions, 0 deletions
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 new file mode 100644 index 0000000000..752e803c3e --- /dev/null +++ b/src/tools/android/java/com/google/devtools/build/android/xml/ArrayXmlResourceValue.java @@ -0,0 +1,133 @@ +// 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.xml; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.MoreObjects; +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.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +import javax.xml.namespace.QName; +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.events.StartElement; +import javax.xml.stream.events.XMLEvent; + +/** + * Represents an Android resource array. + * + * There are two flavors of Android Resource arrays: + * <ul> + * <li>Typed arrays (http://developer.android.com/guide/topics/resources/more-resources + * .html#TypedArray) which which are indicated by a <array> tag.</li> + * <li>Integer array (http://developer.android.com/guide/topics/resources/more-resources + * .html#IntegerArray) which are indicated by <integer-array> tag.</li> + * </ul> + * + * Both of these are accessed by R.array.<name> in java. + */ +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"); + + /** + * Enumerates the different types of array tags. + */ + public enum ArrayType { + INTEGER_ARRAY(TAG_INTEGER_ARRAY), + ARRAY(TAG_ARRAY); + + public QName tagName; + + ArrayType(QName tagName) { + this.tagName = tagName; + } + + public static ArrayType fromTagName(StartElement start) { + final QName tagQName = start.getName(); + for (ArrayType arrayType : values()) { + if (arrayType.tagName.equals(tagQName)) { + return arrayType; + } + } + throw new IllegalArgumentException( + String.format("%s not found in %s", tagQName, Arrays.toString(values()))); + } + } + + private final List<String> values; + private ArrayType arrayType; + + private ArrayXmlResourceValue(ArrayType arrayType, List<String> values) { + this.arrayType = arrayType; + this.values = values; + } + + @VisibleForTesting + public static XmlResourceValue of(ArrayType arrayType, String... values) { + return of(arrayType, Arrays.asList(values)); + } + + public static XmlResourceValue of(ArrayType arrayType, List<String> values) { + return new ArrayXmlResourceValue(arrayType, values); + } + + @Override + public void write(Writer buffer, FullyQualifiedName name) { + // TODO(corysmith): Implement writing xml. + throw new UnsupportedOperationException(); + } + + @Override + public int hashCode() { + return Objects.hash(arrayType, values); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof ArrayXmlResourceValue)) { + return false; + } + ArrayXmlResourceValue other = (ArrayXmlResourceValue) obj; + return Objects.equals(arrayType, other.arrayType) && Objects.equals(values, other.values); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(getClass()) + .add("arrayType", arrayType) + .add("values", values) + .toString(); + } + + public static XmlResourceValue parseArray(XMLEventReader eventReader, StartElement start) + throws XMLStreamException { + List<String> values = new ArrayList<>(); + for (XMLEvent element = eventReader.nextTag(); + !XmlResourceValues.isEndTag(element, start.getName()); + element = eventReader.nextTag()) { + if (XmlResourceValues.isItem(element)) { + values.add(eventReader.getElementText()); + } + } + return of(ArrayType.fromTagName(start), values); + } +} 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 new file mode 100644 index 0000000000..52f44dbc6e --- /dev/null +++ b/src/tools/android/java/com/google/devtools/build/android/xml/AttrXmlResourceValue.java @@ -0,0 +1,495 @@ +// 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.xml; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.MoreObjects; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableMap.Builder; +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.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +import javax.annotation.Nullable; +import javax.xml.namespace.QName; +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.events.StartElement; +import javax.xml.stream.events.XMLEvent; + +/** + * Represents an Android Resource custom attribute. + * + * <p>Attribute are the most complicated Android resource, and therefore the least documented. Most + * of the information about them is found by reading the android compatibility library source. An + * Attribute defines a parameter that can be passed into a view class -- as such you can think of + * attributes as creating slots for other resources to fit into. Each slot will have at least one + * format, and can have multiples. Simple attributes (color, boolean, reference, dimension, + * float, integer, string, and fraction) are defined as <attr name="<em>name</em>" + * format="<em>format</em>" /> while the complex ones, flag and enum, have sub tags: + * <attr name="<em>name</em>" ><flag name="<em>name</em>" value="<em>value</em>"> + * </attr>. + * + * <p>Attributes also have a double duty as defining validation logic for layout resources -- + * 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. + */ +public class AttrXmlResourceValue implements XmlResourceValue { + + private static final String FRACTION = "fraction"; + private static final String STRING = "string"; + private static final String INTEGER = "integer"; + private static final String FLOAT = "float"; + private static final String DIMENSION = "dimension"; + private static final String BOOLEAN = "boolean"; + private static final String COLOR = "color"; + private static final String REFERENCE = "reference"; + private static final String ENUM = "enum"; + 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 AttrXmlResourceValue(Map<String, XmlResourceValue> formats) { + this.formats = formats; + } + + private static Map<String, String> readSubValues(XMLEventReader reader, QName subTagType) + throws XMLStreamException { + Map<String, String> enumValue = new HashMap<>(); + while (reader.hasNext() + && XmlResourceValues.isTag(XmlResourceValues.peekNextTag(reader), subTagType)) { + StartElement element = reader.nextEvent().asStartElement(); + enumValue.put( + XmlResourceValues.getElementName(element), XmlResourceValues.getElementValue(element)); + XMLEvent endTag = reader.nextEvent(); + if (!XmlResourceValues.isEndTag(endTag, subTagType)) { + throw new XMLStreamException( + String.format("Unexpected [%s]; Expected %s", endTag, "</enum>"), endTag.getLocation()); + } + } + return enumValue; + } + + private static void endAttrElement(XMLEventReader reader) throws XMLStreamException { + XMLEvent endTag = reader.nextTag(); + if (!endTag.isEndElement() || !QName.valueOf("attr").equals(endTag.asEndElement().getName())) { + throw new XMLStreamException("Unexpected Tag:" + endTag, endTag.getLocation()); + } + } + + @VisibleForTesting + private static final class BuilderEntry implements Map.Entry<String, XmlResourceValue> { + private final String name; + private final XmlResourceValue value; + + public BuilderEntry(String name, XmlResourceValue value) { + this.name = name; + this.value = value; + } + + @Override + public String getKey() { + return name; + } + + @Override + public XmlResourceValue getValue() { + return value; + } + + @Override + public XmlResourceValue setValue(XmlResourceValue value) { + throw new UnsupportedOperationException(); + } + } + + @SafeVarargs + @VisibleForTesting + public static XmlResourceValue fromFormatEntries(Map.Entry<String, XmlResourceValue>... entries) { + return of(ImmutableMap.copyOf(Arrays.asList(entries))); + } + + public static XmlResourceValue from( + StartElement attr, @Nullable String format, XMLEventReader eventReader) + throws XMLStreamException { + Set<String> formatNames = new HashSet<>(); + if (format != null) { + Collections.addAll(formatNames, format.split("\\|")); + } + XMLEvent nextTag = XmlResourceValues.peekNextTag(eventReader); + if (nextTag != null && nextTag.isStartElement()) { + formatNames.add(nextTag.asStartElement().getName().getLocalPart().toLowerCase()); + } + + Map<String, XmlResourceValue> formats = new HashMap<>(); + for (String formatName : formatNames) { + switch (formatName) { + case FLAG: + Map<String, String> flags = readSubValues(eventReader, TAG_FLAG); + endAttrElement(eventReader); + formats.put(formatName, AttrFlagXmlValue.of(flags)); + break; + case ENUM: + Map<String, String> enums = readSubValues(eventReader, TAG_ENUM); + endAttrElement(eventReader); + formats.put(formatName, AttrEnumXmlValue.of(enums)); + break; + case REFERENCE: + formats.put(formatName, AttrReferenceXmlValue.of()); + break; + case COLOR: + formats.put(formatName, AttrColorXmlValue.of()); + break; + case BOOLEAN: + formats.put(formatName, AttrBooleanXmlValue.of()); + break; + case DIMENSION: + formats.put(formatName, AttrDimensionXmlValue.of()); + break; + case FLOAT: + formats.put(formatName, AttrFloatXmlValue.of()); + break; + case INTEGER: + formats.put(formatName, AttrIntegerXmlValue.of()); + break; + case STRING: + formats.put(formatName, AttrStringXmlValue.of()); + break; + case FRACTION: + formats.put(formatName, AttrFractionXmlValue.of()); + break; + default: + throw new XMLStreamException( + String.format("Unexpected attr format: %S", formatName), attr.getLocation()); + } + } + return of(formats); + } + + public static XmlResourceValue of(Map<String, XmlResourceValue> formats) { + return new AttrXmlResourceValue(formats); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + AttrXmlResourceValue other = (AttrXmlResourceValue) o; + return Objects.equals(formats, other.formats); + } + + @Override + public int hashCode() { + return formats.hashCode(); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this).add("formats", formats).toString(); + } + + @Override + public void write(Writer buffer, FullyQualifiedName name) { + // TODO(corysmith): Implement write. + throw new UnsupportedOperationException(); + } + + /** Represents an Android Enum Attribute resource. */ + @VisibleForTesting + public static class AttrEnumXmlValue implements XmlResourceValue { + + private Map<String, String> values; + + private AttrEnumXmlValue(Map<String, String> values) { + this.values = values; + } + + @VisibleForTesting + public static Map.Entry<String, XmlResourceValue> asEntryOf(String... keyThenValue) { + Preconditions.checkArgument(keyThenValue.length > 0); + Preconditions.checkArgument(keyThenValue.length % 2 == 0); + Builder<String, String> builder = ImmutableMap.<String, String>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(); + } + + @Override + public int hashCode() { + return values.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof AttrEnumXmlValue)) { + return false; + } + AttrEnumXmlValue other = (AttrEnumXmlValue) obj; + return Objects.equals(values, other.values); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(getClass()).add("values", values).toString(); + } + } + + /** Represents an Android Flag Attribute resource. */ + @VisibleForTesting + public static class AttrFlagXmlValue implements XmlResourceValue { + + private Map<String, String> values; + + private AttrFlagXmlValue(Map<String, String> values) { + this.values = values; + } + + public static XmlResourceValue of(Map<String, String> values) { + return new AttrFlagXmlValue(values); + } + + @VisibleForTesting + public static Map.Entry<String, XmlResourceValue> asEntryOf(String... keyThenValue) { + Builder<String, String> builder = ImmutableMap.<String, String>builder(); + Preconditions.checkArgument(keyThenValue.length > 0); + Preconditions.checkArgument(keyThenValue.length % 2 == 0); + for (int i = 0; i < keyThenValue.length; i += 2) { + builder.put(keyThenValue[i], keyThenValue[i + 1]); + } + return new BuilderEntry(FLAG, of(builder.build())); + } + + @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)) { + return false; + } + AttrFlagXmlValue other = (AttrFlagXmlValue) obj; + return Objects.equals(values, other.values); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(getClass()).add("values", values).toString(); + } + } + + /** Represents an Android Reference Attribute resource. */ + @VisibleForTesting + public static class AttrReferenceXmlValue implements XmlResourceValue { + private static final AttrReferenceXmlValue INSTANCE = new AttrReferenceXmlValue(); + + public static XmlResourceValue of() { + return INSTANCE; + } + + @VisibleForTesting + public static BuilderEntry asEntry() { + return new BuilderEntry(REFERENCE, of()); + } + + @Override + public void write(Writer buffer, FullyQualifiedName name) { + // TODO(corysmith): Implement write. + throw new UnsupportedOperationException(); + } + } + + /** Represents an Android Color Attribute resource. */ + @VisibleForTesting + public static class AttrColorXmlValue implements XmlResourceValue { + private static final AttrColorXmlValue INSTANCE = new AttrColorXmlValue(); + + public static XmlResourceValue of() { + return INSTANCE; + } + + @VisibleForTesting + public static BuilderEntry asEntry() { + return new BuilderEntry(COLOR, of()); + } + + @Override + public void write(Writer buffer, FullyQualifiedName name) { + // TODO(corysmith): Implement write. + throw new UnsupportedOperationException(); + } + } + + /** Represents an Android Boolean Attribute resource. */ + @VisibleForTesting + public static class AttrBooleanXmlValue implements XmlResourceValue { + private static final AttrBooleanXmlValue INSTANCE = new AttrBooleanXmlValue(); + + public static XmlResourceValue of() { + return INSTANCE; + } + + @VisibleForTesting + public static BuilderEntry asEntry() { + return new BuilderEntry(BOOLEAN, of()); + } + + @Override + public void write(Writer buffer, FullyQualifiedName name) { + // TODO(corysmith): Implement write. + throw new UnsupportedOperationException(); + } + } + + /** Represents an Android Float Attribute resource. */ + @VisibleForTesting + public static class AttrFloatXmlValue implements XmlResourceValue { + private static final AttrFloatXmlValue INSTANCE = new AttrFloatXmlValue(); + + public static XmlResourceValue of() { + return INSTANCE; + } + + @VisibleForTesting + public static BuilderEntry asEntry() { + return new BuilderEntry(FLOAT, of()); + } + + @Override + public void write(Writer buffer, FullyQualifiedName name) { + // TODO(corysmith): Implement write. + throw new UnsupportedOperationException(); + } + } + + /** Represents an Android Dimension Attribute resource. */ + @VisibleForTesting + public static class AttrDimensionXmlValue implements XmlResourceValue { + private static final AttrDimensionXmlValue INSTANCE = new AttrDimensionXmlValue(); + + @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(); + } + } + + /** Represents an Android Integer Attribute resource. */ + @VisibleForTesting + public static class AttrIntegerXmlValue implements XmlResourceValue { + private static final AttrIntegerXmlValue INSTANCE = new AttrIntegerXmlValue(); + + public static XmlResourceValue of() { + return INSTANCE; + } + + @VisibleForTesting + public static BuilderEntry asEntry() { + return new BuilderEntry(INTEGER, of()); + } + + @Override + public void write(Writer buffer, FullyQualifiedName name) { + // TODO(corysmith): Implement write. + throw new UnsupportedOperationException(); + } + } + + /** Represents an Android String Attribute resource. */ + @VisibleForTesting + public static class AttrStringXmlValue implements XmlResourceValue { + private static final AttrStringXmlValue INSTANCE = new AttrStringXmlValue(); + + public static XmlResourceValue of() { + return INSTANCE; + } + + @VisibleForTesting + public static BuilderEntry asEntry() { + return new BuilderEntry(STRING, of()); + } + + @Override + public void write(Writer buffer, FullyQualifiedName name) { + // TODO(corysmith): Implement write. + throw new UnsupportedOperationException(); + } + } + + /** Represents an Android Fraction Attribute resource. */ + @VisibleForTesting + public static class AttrFractionXmlValue implements XmlResourceValue { + private static final AttrFractionXmlValue INSTANCE = new AttrFractionXmlValue(); + + public static XmlResourceValue of() { + return INSTANCE; + } + + @VisibleForTesting + public static BuilderEntry asEntry() { + return new BuilderEntry(FRACTION, of()); + } + + @Override + public void write(Writer buffer, FullyQualifiedName name) { + // TODO(corysmith): Implement write. + throw new UnsupportedOperationException(); + } + } +} 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 new file mode 100644 index 0000000000..f8221ed332 --- /dev/null +++ b/src/tools/android/java/com/google/devtools/build/android/xml/IdXmlResourceValue.java @@ -0,0 +1,49 @@ +// 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.xml; + +import com.google.common.base.MoreObjects; +import com.google.devtools.build.android.FullyQualifiedName; +import com.google.devtools.build.android.XmlResourceValue; + +import java.io.Writer; + +/** + * Represents an Android Resource id. + * + * <p>Ids (http://developer.android.com/guide/topics/resources/more-resources.html#Id) are + * special -- unlike other resources they cannot be overridden. This is due to the + * nature of their usage. Each id corresponds to context sensitive resource of component, meaning + * 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. + */ +public class IdXmlResourceValue implements XmlResourceValue { + + static final IdXmlResourceValue SINGLETON = new IdXmlResourceValue(); + + public static XmlResourceValue of() { + return SINGLETON; + } + + @Override + public void write(Writer buffer, FullyQualifiedName name) { + // TODO(corysmith): Implement write. + throw new UnsupportedOperationException(); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(getClass()).toString(); + } +} 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 new file mode 100644 index 0000000000..f9db759bd1 --- /dev/null +++ b/src/tools/android/java/com/google/devtools/build/android/xml/PluralXmlResourceValue.java @@ -0,0 +1,74 @@ +// 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.xml; + +import com.google.common.base.MoreObjects; +import com.google.devtools.build.android.FullyQualifiedName; +import com.google.devtools.build.android.XmlResourceValue; + +import java.io.Writer; +import java.util.Map; +import java.util.Objects; + +/** + * 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: + * <code> + * <plurals name="plural_name"> + * <item quantity=["zero" | "one" | "two" | "few" | "many" | "other"]> + * text_string + * </item> + * </plurals> + * </code> + */ +public class PluralXmlResourceValue implements XmlResourceValue { + + private Map<String, String> values; + + private PluralXmlResourceValue(Map<String, String> values) { + this.values = values; + } + + public static XmlResourceValue of(Map<String, String> values) { + return new PluralXmlResourceValue(values); + } + + @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 PluralXmlResourceValue)) { + return false; + } + PluralXmlResourceValue other = (PluralXmlResourceValue) obj; + return Objects.equals(values, other.values); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(getClass()).add("values", values).toString(); + } +} 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 new file mode 100644 index 0000000000..333fb088a2 --- /dev/null +++ b/src/tools/android/java/com/google/devtools/build/android/xml/SimpleXmlResourceValue.java @@ -0,0 +1,136 @@ +// 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.xml; + +import com.google.common.base.MoreObjects; +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.util.Arrays; +import java.util.Objects; + +import javax.xml.namespace.QName; + +/** + * Represents a simple Android resource xml value. + * + * <p>There is a class of resources that are simple name/value pairs: string(http://developer + * .android.com/guide/topics/resources/string-resource.html), bool + * (http://developer.android.com/guide/topics/resources/more-resources.html#Bool), color + * (http://developer.android.com/guide/topics/resources/more-resources.html#Color), and + * dimen (http://developer.android.com/guide/topics/resources/more-resources.html#Dimension). + * These are defined in xml as <<em>resource type</em> name="<em>name</em>" + * value="<em>value</em>">. In the interest of keeping the parsing svelte, these are + * represented by a single class. + */ +public class SimpleXmlResourceValue implements XmlResourceValue { + static final QName TAG_STRING = QName.valueOf("string"); + static final QName TAG_BOOL = QName.valueOf("bool"); + static final QName TAG_COLOR = QName.valueOf("color"); + static final QName TAG_DIMEN = QName.valueOf("dimen"); + + /** Provides an enumeration resource type and simple value validation. */ + public enum Type { + STRING(TAG_STRING) { + @Override + public boolean validate(String value) { + return true; + } + }, + BOOL(TAG_BOOL) { + @Override + public boolean validate(String value) { + final String cleanValue = value.toLowerCase().trim(); + return "true".equals(cleanValue) || "false".equals(cleanValue); + } + }, + COLOR(TAG_COLOR) { + @Override + public boolean validate(String value) { + // TODO(corysmith): Validate the hex color. + return true; + } + }, + DIMEN(TAG_DIMEN) { + @Override + public boolean validate(String value) { + // TODO(corysmith): Validate the dimension type. + return true; + } + }; + private QName tagName; + + Type(QName tagName) { + this.tagName = tagName; + } + + abstract boolean validate(String value); + + public static Type from(ResourceType resourceType) { + for (Type valueType : values()) { + if (valueType.tagName.getLocalPart().equals(resourceType.getName())) { + return valueType; + } + } + throw new IllegalArgumentException( + String.format( + "%s resource type not found in available types: %s", + resourceType, + Arrays.toString(values()))); + } + } + + private String value; + private Type valueType; + + public static XmlResourceValue of(Type valueType, String value) { + return new SimpleXmlResourceValue(valueType, value); + } + + private SimpleXmlResourceValue(Type valueType, String value) { + this.valueType = valueType; + this.value = value; + } + + @Override + public void write(Writer buffer, FullyQualifiedName name) { + // TODO(corysmith): Implement write. + throw new UnsupportedOperationException(); + } + + @Override + public int hashCode() { + return Objects.hash(valueType, value); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof SimpleXmlResourceValue)) { + return false; + } + SimpleXmlResourceValue other = (SimpleXmlResourceValue) obj; + return Objects.equals(valueType, other.valueType) && Objects.equals(value, other.value); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(getClass()) + .add("valueType", valueType) + .add("value", value) + .toString(); + } +} 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 new file mode 100644 index 0000000000..3055089ea0 --- /dev/null +++ b/src/tools/android/java/com/google/devtools/build/android/xml/StyleXmlResourceValue.java @@ -0,0 +1,81 @@ +// 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.xml; + +import com.google.common.base.MoreObjects; +import com.google.devtools.build.android.FullyQualifiedName; +import com.google.devtools.build.android.XmlResourceValue; + +import java.io.Writer; +import java.util.Map; +import java.util.Objects; + +/** + * Represents an Android Style Resource. + * + * <p>Styles (http://developer.android.com/guide/topics/resources/style-resource.html) define a + * look and feel for a layout or other ui construct. They are effectively a s set of values that + * correspond to <attr> resources defined either in the base android framework or in other + * resources. They also allow inheritance on other styles. For a style to valid in a given resource + * pass, they must only contain definer attributes with acceptable values. + * <code> + * <resources> + * <style name="CustomText" parent="@style/Text"> + * <item name="android:textSize">20sp</item> + * <item name="android:textColor">#008</item> + * </style> + * </resources> + * </code> + */ +public class StyleXmlResourceValue implements XmlResourceValue { + private String parent; + private Map<String, String> values; + + public static StyleXmlResourceValue of(String parent, Map<String, String> values) { + return new StyleXmlResourceValue(parent, values); + } + + private StyleXmlResourceValue(String parent, Map<String, String> values) { + this.parent = parent; + this.values = values; + } + + @Override + public void write(Writer buffer, FullyQualifiedName name) { + // TODO(corysmith): Implement write. + throw new UnsupportedOperationException(); + } + + @Override + public int hashCode() { + return Objects.hash(parent, values); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof StyleXmlResourceValue)) { + return false; + } + StyleXmlResourceValue other = (StyleXmlResourceValue) obj; + return Objects.equals(parent, other.parent) && Objects.equals(values, other.values); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(getClass()) + .add("parent", parent) + .add("values", values) + .toString(); + } +} 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 new file mode 100644 index 0000000000..f1a0486307 --- /dev/null +++ b/src/tools/android/java/com/google/devtools/build/android/xml/StyleableXmlResourceValue.java @@ -0,0 +1,89 @@ +// 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.xml; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.MoreObjects; +import com.google.devtools.build.android.FullyQualifiedName; +import com.google.devtools.build.android.XmlResourceValue; + +import java.io.Writer; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +/** + * 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 + * <declare-styleable> 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>The StyleableXmlValue only contains names of the attributes it holds, not definitions. + */ +public class StyleableXmlResourceValue implements XmlResourceValue { + private final List<String> attrs; + + private StyleableXmlResourceValue(List<String> attrs) { + this.attrs = attrs; + } + + public static XmlResourceValue of(List<String> attrs) { + return new StyleableXmlResourceValue(attrs); + } + + @VisibleForTesting + public static XmlResourceValue of(String... attrs) { + return new StyleableXmlResourceValue(Arrays.asList(attrs)); + } + + @Override + public void write(Writer buffer, FullyQualifiedName name) { + // TODO(corysmith): Implement write. + throw new UnsupportedOperationException(); + } + + @Override + public int hashCode() { + return attrs.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof StyleableXmlResourceValue)) { + return false; + } + StyleableXmlResourceValue other = (StyleableXmlResourceValue) obj; + return Objects.equals(attrs, other.attrs); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(getClass()).add("attrs", attrs).toString(); + } +} |