Skip to content

Commit

Permalink
Allows enums as element types (googleapis#475)
Browse files Browse the repository at this point in the history
  • Loading branch information
geri-m committed Sep 23, 2018
1 parent c194b3b commit 809a7dd
Show file tree
Hide file tree
Showing 4 changed files with 160 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,8 @@ private static boolean parseElementInternal(XmlPullParser parser,
@SuppressWarnings("unchecked")
Map<String, Object> destinationMap =
genericXml == null && destination instanceof Map<?, ?> ? Map.class.cast(destination) : null;

// if there is a class, we want to put the data into, create the class Info for this
ClassInfo classInfo =
destinationMap != null || destination == null ? null : ClassInfo.of(destination.getClass());
if (parser.getEventType() == XmlPullParser.START_DOCUMENT) {
Expand Down Expand Up @@ -313,7 +315,11 @@ private static boolean parseElementInternal(XmlPullParser parser,
parseNamespacesForElement(parser, namespaceDictionary);
String namespace = parser.getNamespace();
String alias = namespaceDictionary.getNamespaceAliasForUriErrorOnUnknown(namespace);

// get the "real" field name of the
String fieldName = getFieldName(false, alias, namespace, parser.getName());

// fetch the field from the classInfo
field = classInfo == null ? null : classInfo.getField(fieldName);
Type fieldType = field == null ? valueType : field.getGenericType();
fieldType = Data.resolveWildcardTypeOrTypeVariable(context, fieldType);
Expand All @@ -323,10 +329,13 @@ private static boolean parseElementInternal(XmlPullParser parser,
if (fieldType instanceof ParameterizedType) {
fieldClass = Types.getRawClass((ParameterizedType) fieldType);
}

boolean isArray = Types.isArray(fieldType);
// text content
boolean ignore = field == null && destinationMap == null && genericXml == null;
if (ignore || Data.isPrimitive(fieldType)) {
// is the field an Enum
boolean isEnum = fieldClass != null && fieldClass.isEnum();
if (ignore || Data.isPrimitive(fieldType) || isEnum) {
int level = 1;
while (level != 0) {
switch (parser.next()) {
Expand Down Expand Up @@ -425,7 +434,8 @@ private static boolean parseElementInternal(XmlPullParser parser,
if (subFieldType instanceof ParameterizedType) {
subFieldClass = Types.getRawClass((ParameterizedType) subFieldType);
}
if (Data.isPrimitive(subFieldType)) {
boolean isSubEnum = subFieldClass != null && subFieldClass.isEnum();
if (Data.isPrimitive(subFieldType) || isSubEnum) {
elementValue = parseTextContentForElement(parser, context, false, subFieldType);
} else if (subFieldType == null || subFieldClass != null
&& Types.isAssignableToOrFrom(subFieldClass, Map.class)) {
Expand Down Expand Up @@ -482,7 +492,8 @@ private static boolean parseElementInternal(XmlPullParser parser,
}
collectionValue.add(elementValue);
}
} else {
}
else {
// not an array/iterable or a map, but we do have a field
Object value = Types.newInstance(fieldClass);
int contextSize = context.size();
Expand All @@ -497,13 +508,16 @@ private static boolean parseElementInternal(XmlPullParser parser,
setValue(value, field, destination, genericXml, destinationMap, fieldName);
}
}



if (isStopped || parser.getEventType() == XmlPullParser.END_DOCUMENT) {
isStopped = true;
break main;
}
break;
}
}
break; // break Switch;
} // end -- switch (event)
} // end -- main: while (true)
arrayValueMap.setValues();
return isStopped;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ private void computeAliases(Object element, SortedSet<String> aliases) {
aliases.add(alias);
}
Class<?> valueClass = value.getClass();
if (!isAttribute && !Data.isPrimitive(valueClass)) {
if (!isAttribute && !Data.isPrimitive(valueClass) && !valueClass.isEnum() ) {
if (value instanceof Iterable<?> || valueClass.isArray()) {
for (Object subValue : Types.iterableOf(value)) {
computeAliases(subValue, aliases);
Expand All @@ -266,6 +266,7 @@ private void computeAliases(Object element, SortedSet<String> aliases) {
computeAliases(value, aliases);
}
}

}
}
}
Expand Down Expand Up @@ -328,6 +329,8 @@ class ElementSerializer {
Class<?> valueClass = elementValue.getClass();
if (Data.isPrimitive(valueClass) && !Data.isNull(elementValue)) {
textValue = elementValue;
} else if (valueClass.isEnum() && !Data.isNull(elementValue)){
textValue = elementValue;
} else {
for (Map.Entry<String, Object> entry : Data.mapOf(elementValue).entrySet()) {
Object fieldValue = entry.getValue();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package com.google.api.client.xml;

import java.io.ByteArrayOutputStream;
import java.io.StringReader;
import java.util.ArrayList;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlSerializer;
import com.google.api.client.util.Key;
import com.google.api.client.util.Value;
import junit.framework.TestCase;

public class XmlEnumTest extends TestCase {

public enum AnyEnum {
@Value ENUM_1,
@Value ENUM_2
}

public static class AnyType {
@Key("@attr")
public Object attr;
@Key
public Object elem;
@Key
public Object rep;
@Key("@anyEnum")
public XmlEnumTest.AnyEnum anyEnum;
@Key
public XmlEnumTest.AnyEnum anotherEnum;
@Key
public ValueType value;
}

public static class AnyTypeEnumElementOnly {
@Key
public XmlEnumTest.AnyEnum elementEnum;
}

public static class AnyTypeEnumAttributeOnly {
@Key("@attributeEnum")
public XmlEnumTest.AnyEnum attributeEnum;
}

public static class ValueType {
@Key("text()")
public XmlEnumTest.AnyEnum content;
}

private static final String XML =
"<?xml version=\"1.0\"?><any anyEnum=\"ENUM_1\" attr=\"value\" xmlns=\"http://www.w3.org/2005/Atom\">"
+ "<anotherEnum>ENUM_2</anotherEnum><elem>content</elem><rep>rep1</rep><rep>rep2</rep><value>ENUM_1</value></any>";

private static final String XML_ENUM_ELEMENT_ONLY = "<?xml version=\"1.0\"?><any xmlns=\"http://www.w3.org/2005/Atom\"><elementEnum>ENUM_2</elementEnum></any>";

private static final String XML_ENUM_ATTRIBUTE_ONLY = "<?xml version=\"1.0\"?><any attributeEnum=\"ENUM_1\" xmlns=\"http://www.w3.org/2005/Atom\" />";

private static final String XML_ENUM_INCORRECT = "<?xml version=\"1.0\"?><any xmlns=\"http://www.w3.org/2005/Atom\"><elementEnum>ENUM_3</elementEnum></any>";


@SuppressWarnings("cast")
public void testParse_anyType() throws Exception {
AnyType xml = new AnyType();
XmlPullParser parser = Xml.createParser();
parser.setInput(new StringReader(XML));
XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary();
Xml.parseElement(parser, xml, namespaceDictionary, null);
assertTrue(xml.attr instanceof String);
assertTrue(xml.elem.toString(), xml.elem instanceof ArrayList<?>);
assertTrue(xml.rep.toString(), xml.rep instanceof ArrayList<?>);
assertTrue(xml.value instanceof ValueType);
assertTrue(xml.value.content instanceof XmlEnumTest.AnyEnum);
assertTrue(xml.anyEnum instanceof XmlEnumTest.AnyEnum);
assertTrue(xml.anotherEnum instanceof XmlEnumTest.AnyEnum);
assertTrue(xml.anyEnum.equals(AnyEnum.ENUM_1));
assertTrue(xml.anotherEnum.equals(AnyEnum.ENUM_2));
assertTrue(xml.value.content.equals(AnyEnum.ENUM_1));
// serialize
XmlSerializer serializer = Xml.createSerializer();
ByteArrayOutputStream out = new ByteArrayOutputStream();
serializer.setOutput(out, "UTF-8");
namespaceDictionary.serialize(serializer, "any", xml);
assertEquals(XML, out.toString());
}

public void testParse_enumElementType() throws Exception {
XmlEnumTest.AnyTypeEnumElementOnly xml = new XmlEnumTest.AnyTypeEnumElementOnly();
XmlPullParser parser = Xml.createParser();
parser.setInput(new StringReader(XML_ENUM_ELEMENT_ONLY));
XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary();
Xml.parseElement(parser, xml, namespaceDictionary, null);
assertTrue(xml.elementEnum instanceof XmlEnumTest.AnyEnum);
assertTrue(xml.elementEnum.equals(AnyEnum.ENUM_2));
// serialize
XmlSerializer serializer = Xml.createSerializer();
ByteArrayOutputStream out = new ByteArrayOutputStream();
serializer.setOutput(out, "UTF-8");
namespaceDictionary.serialize(serializer, "any", xml);
assertEquals(XML_ENUM_ELEMENT_ONLY, out.toString());
}

public void testParse_enumAttributeType() throws Exception {
XmlEnumTest.AnyTypeEnumAttributeOnly xml = new XmlEnumTest.AnyTypeEnumAttributeOnly();
XmlPullParser parser = Xml.createParser();
parser.setInput(new StringReader(XML_ENUM_ATTRIBUTE_ONLY));
XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary();
Xml.parseElement(parser, xml, namespaceDictionary, null);
assertTrue(xml.attributeEnum instanceof XmlEnumTest.AnyEnum);
assertTrue(xml.attributeEnum.equals(AnyEnum.ENUM_1));
// serialize
XmlSerializer serializer = Xml.createSerializer();
ByteArrayOutputStream out = new ByteArrayOutputStream();
serializer.setOutput(out, "UTF-8");
namespaceDictionary.serialize(serializer, "any", xml);
assertEquals(XML_ENUM_ATTRIBUTE_ONLY, out.toString());
}

public void testParse_enumElementTypeIncorrect() throws Exception {
XmlEnumTest.AnyTypeEnumElementOnly xml = new XmlEnumTest.AnyTypeEnumElementOnly();
XmlPullParser parser = Xml.createParser();
parser.setInput(new StringReader(XML_ENUM_INCORRECT));
XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary();
try{
Xml.parseElement(parser, xml, namespaceDictionary, null);
// fail test, if there is no exception
fail();
} catch (final IllegalArgumentException e){
assertEquals("given enum name ENUM_3 not part of enumeration", e.getMessage());
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,10 @@ public static Object parsePrimitiveValue(Type type, String stringValue) {
return new BigDecimal(stringValue);
}
if (primitiveClass.isEnum()) {
if (!ClassInfo.of(primitiveClass).names.contains(stringValue)) {
throw new IllegalArgumentException(String.format("given enum name %s not part of " +
"enumeration", stringValue));
}
@SuppressWarnings({"unchecked", "rawtypes"})
Enum result = ClassInfo.of(primitiveClass).getFieldInfo(stringValue).<Enum>enumValue();
return result;
Expand Down

0 comments on commit 809a7dd

Please sign in to comment.