Author: cutting
Date: Wed Nov 21 22:29:42 2012
New Revision: 1412334
URL: http://svn.apache.org/viewvc?rev=1412334&view=rev
Log:
AVRO-1201. Java: Fix GenericData#toString() to generate valid JSON for enum
values. Contributed by Sharmarke Aden.
Added:
avro/trunk/lang/java/avro/src/test/java/org/apache/avro/TypeEnum.java
(with props)
Modified:
avro/trunk/CHANGES.txt
avro/trunk/lang/java/avro/src/main/java/org/apache/avro/generic/GenericData.java
avro/trunk/lang/java/avro/src/test/java/org/apache/avro/FooBarSpecificRecord.java
avro/trunk/lang/java/avro/src/test/java/org/apache/avro/specific/TestSpecificData.java
avro/trunk/lang/java/avro/src/test/resources/FooBarSpecificRecord.avsc
Modified: avro/trunk/CHANGES.txt
URL:
http://svn.apache.org/viewvc/avro/trunk/CHANGES.txt?rev=1412334&r1=1412333&r2=1412334&view=diff
==============================================================================
--- avro/trunk/CHANGES.txt (original)
+++ avro/trunk/CHANGES.txt Wed Nov 21 22:29:42 2012
@@ -49,6 +49,9 @@ Trunk (not yet released)
compare against next. Also improve GenericData#deepCopy() to be
generic, so that its return type matches its parameter type. (cutting)
+ AVRO-1201. Java: Fix GenericData#toString() to generate valid JSON for
+ enum values. (Sharmarke Aden via cutting)
+
Avro 1.7.2 (20 October 2012)
NEW FEATURES
Modified:
avro/trunk/lang/java/avro/src/main/java/org/apache/avro/generic/GenericData.java
URL:
http://svn.apache.org/viewvc/avro/trunk/lang/java/avro/src/main/java/org/apache/avro/generic/GenericData.java?rev=1412334&r1=1412333&r2=1412334&view=diff
==============================================================================
---
avro/trunk/lang/java/avro/src/main/java/org/apache/avro/generic/GenericData.java
(original)
+++
avro/trunk/lang/java/avro/src/main/java/org/apache/avro/generic/GenericData.java
Wed Nov 21 22:29:42 2012
@@ -142,14 +142,18 @@ public class GenericData {
addAll(c);
}
}
+ @Override
public Schema getSchema() { return schema; }
@Override public int size() { return size; }
@Override public void clear() { size = 0; }
@Override public Iterator<T> iterator() {
return new Iterator<T>() {
private int position = 0;
+ @Override
public boolean hasNext() { return position < size; }
+ @Override
public T next() { return (T)elements[position++]; }
+ @Override
public void remove() { throw new UnsupportedOperationException(); }
};
}
@@ -196,12 +200,15 @@ public class GenericData {
elements[size] = null;
return result;
}
+ @Override
public T peek() {
return (size < elements.length) ? (T)elements[size] : null;
}
+ @Override
public int compareTo(GenericArray<T> that) {
return GenericData.get().compare(this, that, this.getSchema());
}
+ @Override
public void reverse() {
int left = 0;
int right = elements.length - 1;
@@ -217,7 +224,7 @@ public class GenericData {
}
@Override
public String toString() {
- StringBuffer buffer = new StringBuffer();
+ StringBuilder buffer = new StringBuilder();
buffer.append("[");
int count = 0;
for (T e : this) {
@@ -253,6 +260,7 @@ public class GenericData {
public void bytes(byte[] bytes) { this.bytes = bytes; }
+ @Override
public byte[] bytes() { return bytes; }
@Override
@@ -268,6 +276,7 @@ public class GenericData {
@Override
public String toString() { return Arrays.toString(bytes); }
+ @Override
public int compareTo(Fixed that) {
return BinaryData.compareBytes(this.bytes, 0, this.bytes.length,
that.bytes, 0, that.bytes.length);
@@ -318,13 +327,13 @@ public class GenericData {
case ENUM:
return schema.getEnumSymbols().contains(datum.toString());
case ARRAY:
- if (!(datum instanceof Collection)) return false;
+ if (!(isArray(datum))) return false;
for (Object element : (Collection<?>)datum)
if (!validate(schema.getElementType(), element))
return false;
return true;
case MAP:
- if (!(datum instanceof Map)) return false;
+ if (!(isMap(datum))) return false;
@SuppressWarnings(value="unchecked")
Map<Object,Object> map = (Map<Object,Object>)datum;
for (Map.Entry<Object,Object> entry : map.entrySet())
@@ -341,11 +350,11 @@ public class GenericData {
&& ((GenericFixed)datum).bytes().length==schema.getFixedSize();
case STRING: return isString(datum);
case BYTES: return isBytes(datum);
- case INT: return datum instanceof Integer;
- case LONG: return datum instanceof Long;
- case FLOAT: return datum instanceof Float;
- case DOUBLE: return datum instanceof Double;
- case BOOLEAN: return datum instanceof Boolean;
+ case INT: return isInteger(datum);
+ case LONG: return isLong(datum);
+ case FLOAT: return isFloat(datum);
+ case DOUBLE: return isDouble(datum);
+ case BOOLEAN: return isBoolean(datum);
case NULL: return datum == null;
default: return false;
}
@@ -371,7 +380,7 @@ public class GenericData {
buffer.append(", ");
}
buffer.append("}");
- } else if (datum instanceof Collection) {
+ } else if (isArray(datum)) {
Collection<?> array = (Collection<?>)datum;
buffer.append("[");
long last = array.size()-1;
@@ -382,7 +391,7 @@ public class GenericData {
buffer.append(", ");
}
buffer.append("]");
- } else if (datum instanceof Map) {
+ } else if (isMap(datum)) {
buffer.append("{");
int count = 0;
@SuppressWarnings(value="unchecked")
@@ -395,12 +404,11 @@ public class GenericData {
buffer.append(", ");
}
buffer.append("}");
- } else if (datum instanceof CharSequence
- || datum instanceof GenericEnumSymbol) {
+ } else if (isString(datum)|| isEnum(datum)) {
buffer.append("\"");
writeEscapedString(datum.toString(), buffer);
buffer.append("\"");
- } else if (datum instanceof ByteBuffer) {
+ } else if (isBytes(datum)) {
buffer.append("{\"bytes\": \"");
ByteBuffer bytes = (ByteBuffer)datum;
for (int i = bytes.position(); i < bytes.limit(); i++)
@@ -459,7 +467,7 @@ public class GenericData {
public Schema induce(Object datum) {
if (isRecord(datum)) {
return getRecordSchema(datum);
- } else if (datum instanceof Collection) {
+ } else if (isArray(datum)) {
Schema elementType = null;
for (Object element : (Collection<?>)datum) {
if (elementType == null) {
@@ -473,7 +481,7 @@ public class GenericData {
}
return Schema.createArray(elementType);
- } else if (datum instanceof Map) {
+ } else if (isMap(datum)) {
@SuppressWarnings(value="unchecked")
Map<Object,Object> map = (Map<Object,Object>)datum;
Schema value = null;
@@ -492,13 +500,13 @@ public class GenericData {
return Schema.createFixed(null, null, null,
((GenericFixed)datum).bytes().length);
}
- else if (datum instanceof CharSequence) return Schema.create(Type.STRING);
- else if (datum instanceof ByteBuffer) return Schema.create(Type.BYTES);
- else if (datum instanceof Integer) return Schema.create(Type.INT);
- else if (datum instanceof Long) return Schema.create(Type.LONG);
- else if (datum instanceof Float) return Schema.create(Type.FLOAT);
- else if (datum instanceof Double) return Schema.create(Type.DOUBLE);
- else if (datum instanceof Boolean) return Schema.create(Type.BOOLEAN);
+ else if (isString(datum)) return Schema.create(Type.STRING);
+ else if (isBytes(datum)) return Schema.create(Type.BYTES);
+ else if (isInteger(datum)) return Schema.create(Type.INT);
+ else if (isLong(datum)) return Schema.create(Type.LONG);
+ else if (isFloat(datum)) return Schema.create(Type.FLOAT);
+ else if (isDouble(datum)) return Schema.create(Type.DOUBLE);
+ else if (isBoolean(datum)) return Schema.create(Type.BOOLEAN);
else if (datum == null) return Schema.create(Type.NULL);
else throw new AvroTypeException("Can't create schema for: "+datum);
@@ -561,15 +569,15 @@ public class GenericData {
return Type.STRING.getName();
if (isBytes(datum))
return Type.BYTES.getName();
- if (datum instanceof Integer)
+ if (isInteger(datum))
return Type.INT.getName();
- if (datum instanceof Long)
+ if (isLong(datum))
return Type.LONG.getName();
- if (datum instanceof Float)
+ if (isFloat(datum))
return Type.FLOAT.getName();
- if (datum instanceof Double)
+ if (isDouble(datum))
return Type.DOUBLE.getName();
- if (datum instanceof Boolean)
+ if (isBoolean(datum))
return Type.BOOLEAN.getName();
throw new AvroRuntimeException("Unknown datum type: "+datum);
}
@@ -593,11 +601,11 @@ public class GenericData {
return schema.getFullName().equals(getFixedSchema(datum).getFullName());
case STRING: return isString(datum);
case BYTES: return isBytes(datum);
- case INT: return datum instanceof Integer;
- case LONG: return datum instanceof Long;
- case FLOAT: return datum instanceof Float;
- case DOUBLE: return datum instanceof Double;
- case BOOLEAN: return datum instanceof Boolean;
+ case INT: return isInteger(datum);
+ case LONG: return isLong(datum);
+ case FLOAT: return isFloat(datum);
+ case DOUBLE: return isDouble(datum);
+ case BOOLEAN: return isBoolean(datum);
case NULL: return datum == null;
default: throw new AvroRuntimeException("Unexpected type: " +schema);
}
@@ -659,6 +667,42 @@ public class GenericData {
return datum instanceof ByteBuffer;
}
+ /**
+ * Called by the default implementation of {@link #instanceOf}.
+ */
+ protected boolean isInteger(Object datum) {
+ return datum instanceof Integer;
+ }
+
+ /**
+ * Called by the default implementation of {@link #instanceOf}.
+ */
+ protected boolean isLong(Object datum) {
+ return datum instanceof Long;
+ }
+
+ /**
+ * Called by the default implementation of {@link #instanceOf}.
+ */
+ protected boolean isFloat(Object datum) {
+ return datum instanceof Float;
+ }
+
+ /**
+ * Called by the default implementation of {@link #instanceOf}.
+ */
+ protected boolean isDouble(Object datum) {
+ return datum instanceof Double;
+ }
+
+ /**
+ * Called by the default implementation of {@link #instanceOf}.
+ */
+ protected boolean isBoolean(Object datum) {
+ return datum instanceof Boolean;
+ }
+
+
/** Compute a hash code according to a schema, consistent with {@link
* #compare(Object,Object,Schema)}. */
public int hashCode(Object o, Schema s) {
Modified:
avro/trunk/lang/java/avro/src/test/java/org/apache/avro/FooBarSpecificRecord.java
URL:
http://svn.apache.org/viewvc/avro/trunk/lang/java/avro/src/test/java/org/apache/avro/FooBarSpecificRecord.java?rev=1412334&r1=1412333&r2=1412334&view=diff
==============================================================================
---
avro/trunk/lang/java/avro/src/test/java/org/apache/avro/FooBarSpecificRecord.java
(original)
+++
avro/trunk/lang/java/avro/src/test/java/org/apache/avro/FooBarSpecificRecord.java
Wed Nov 21 22:29:42 2012
@@ -6,15 +6,32 @@
package org.apache.avro;
@SuppressWarnings("all")
public class FooBarSpecificRecord extends
org.apache.avro.specific.SpecificRecordBase implements
org.apache.avro.specific.SpecificRecord {
- public static final org.apache.avro.Schema SCHEMA$ = new
org.apache.avro.Schema.Parser().parse("{\"type\":\"record\",\"name\":\"FooBarSpecificRecord\",\"namespace\":\"org.apache.avro\",\"fields\":[{\"name\":\"id\",\"type\":\"int\"},{\"name\":\"relatedids\",\"type\":{\"type\":\"array\",\"items\":\"int\"}}]}");
+ public static final org.apache.avro.Schema SCHEMA$ = new
org.apache.avro.Schema.Parser().parse("{\"type\":\"record\",\"name\":\"FooBarSpecificRecord\",\"namespace\":\"org.apache.avro\",\"fields\":[{\"name\":\"id\",\"type\":\"int\"},{\"name\":\"relatedids\",\"type\":{\"type\":\"array\",\"items\":\"int\"}},{\"name\":\"typeEnum\",\"type\":[\"null\",{\"type\":\"enum\",\"name\":\"TypeEnum\",\"symbols\":[\"a\",\"b\",\"c\"]}],\"default\":null}]}");
@Deprecated public int id;
@Deprecated public java.util.List<java.lang.Integer> relatedids;
+ @Deprecated public org.apache.avro.TypeEnum typeEnum;
+
+ /**
+ * Default constructor.
+ */
+ public FooBarSpecificRecord() {}
+
+ /**
+ * All-args constructor.
+ */
+ public FooBarSpecificRecord(java.lang.Integer id,
java.util.List<java.lang.Integer> relatedids, org.apache.avro.TypeEnum
typeEnum) {
+ this.id = id;
+ this.relatedids = relatedids;
+ this.typeEnum = typeEnum;
+ }
+
public org.apache.avro.Schema getSchema() { return SCHEMA$; }
// Used by DatumWriter. Applications should not call.
public java.lang.Object get(int field$) {
switch (field$) {
case 0: return id;
case 1: return relatedids;
+ case 2: return typeEnum;
default: throw new org.apache.avro.AvroRuntimeException("Bad index");
}
}
@@ -24,6 +41,7 @@ public class FooBarSpecificRecord extend
switch (field$) {
case 0: id = (java.lang.Integer)value$; break;
case 1: relatedids = (java.util.List<java.lang.Integer>)value$; break;
+ case 2: typeEnum = (org.apache.avro.TypeEnum)value$; break;
default: throw new org.apache.avro.AvroRuntimeException("Bad index");
}
}
@@ -58,6 +76,21 @@ public class FooBarSpecificRecord extend
this.relatedids = value;
}
+ /**
+ * Gets the value of the 'typeEnum' field.
+ */
+ public org.apache.avro.TypeEnum getTypeEnum() {
+ return typeEnum;
+ }
+
+ /**
+ * Sets the value of the 'typeEnum' field.
+ * @param value the value to set.
+ */
+ public void setTypeEnum(org.apache.avro.TypeEnum value) {
+ this.typeEnum = value;
+ }
+
/** Creates a new FooBarSpecificRecord RecordBuilder */
public static org.apache.avro.FooBarSpecificRecord.Builder newBuilder() {
return new org.apache.avro.FooBarSpecificRecord.Builder();
@@ -81,6 +114,7 @@ public class FooBarSpecificRecord extend
private int id;
private java.util.List<java.lang.Integer> relatedids;
+ private org.apache.avro.TypeEnum typeEnum;
/** Creates a new Builder */
private Builder() {
@@ -96,13 +130,17 @@ public class FooBarSpecificRecord extend
private Builder(org.apache.avro.FooBarSpecificRecord other) {
super(org.apache.avro.FooBarSpecificRecord.SCHEMA$);
if (isValidValue(fields()[0], other.id)) {
- this.id = data().deepCopy(fields()[0].schema(), other.id);
+ this.id = (java.lang.Integer) data().deepCopy(fields()[0].schema(),
other.id);
fieldSetFlags()[0] = true;
}
if (isValidValue(fields()[1], other.relatedids)) {
- this.relatedids = data().deepCopy(fields()[1].schema(),
other.relatedids);
+ this.relatedids = (java.util.List<java.lang.Integer>)
data().deepCopy(fields()[1].schema(), other.relatedids);
fieldSetFlags()[1] = true;
}
+ if (isValidValue(fields()[2], other.typeEnum)) {
+ this.typeEnum = (org.apache.avro.TypeEnum)
data().deepCopy(fields()[2].schema(), other.typeEnum);
+ fieldSetFlags()[2] = true;
+ }
}
/** Gets the value of the 'id' field */
@@ -154,12 +192,38 @@ public class FooBarSpecificRecord extend
return this;
}
+ /** Gets the value of the 'typeEnum' field */
+ public org.apache.avro.TypeEnum getTypeEnum() {
+ return typeEnum;
+ }
+
+ /** Sets the value of the 'typeEnum' field */
+ public org.apache.avro.FooBarSpecificRecord.Builder
setTypeEnum(org.apache.avro.TypeEnum value) {
+ validate(fields()[2], value);
+ this.typeEnum = value;
+ fieldSetFlags()[2] = true;
+ return this;
+ }
+
+ /** Checks whether the 'typeEnum' field has been set */
+ public boolean hasTypeEnum() {
+ return fieldSetFlags()[2];
+ }
+
+ /** Clears the value of the 'typeEnum' field */
+ public org.apache.avro.FooBarSpecificRecord.Builder clearTypeEnum() {
+ typeEnum = null;
+ fieldSetFlags()[2] = false;
+ return this;
+ }
+
@Override
public FooBarSpecificRecord build() {
try {
FooBarSpecificRecord record = new FooBarSpecificRecord();
record.id = fieldSetFlags()[0] ? this.id : (java.lang.Integer)
defaultValue(fields()[0]);
record.relatedids = fieldSetFlags()[1] ? this.relatedids :
(java.util.List<java.lang.Integer>) defaultValue(fields()[1]);
+ record.typeEnum = fieldSetFlags()[2] ? this.typeEnum :
(org.apache.avro.TypeEnum) defaultValue(fields()[2]);
return record;
} catch (Exception e) {
throw new org.apache.avro.AvroRuntimeException(e);
Added: avro/trunk/lang/java/avro/src/test/java/org/apache/avro/TypeEnum.java
URL:
http://svn.apache.org/viewvc/avro/trunk/lang/java/avro/src/test/java/org/apache/avro/TypeEnum.java?rev=1412334&view=auto
==============================================================================
--- avro/trunk/lang/java/avro/src/test/java/org/apache/avro/TypeEnum.java
(added)
+++ avro/trunk/lang/java/avro/src/test/java/org/apache/avro/TypeEnum.java Wed
Nov 21 22:29:42 2012
@@ -0,0 +1,11 @@
+/**
+ * Autogenerated by Avro
+ *
+ * DO NOT EDIT DIRECTLY
+ */
+package org.apache.avro;
+@SuppressWarnings("all")
+public enum TypeEnum {
+ a, b, c ;
+ public static final org.apache.avro.Schema SCHEMA$ = new
org.apache.avro.Schema.Parser().parse("{\"type\":\"enum\",\"name\":\"TypeEnum\",\"namespace\":\"org.apache.avro\",\"symbols\":[\"a\",\"b\",\"c\"]}");
+}
Propchange:
avro/trunk/lang/java/avro/src/test/java/org/apache/avro/TypeEnum.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified:
avro/trunk/lang/java/avro/src/test/java/org/apache/avro/specific/TestSpecificData.java
URL:
http://svn.apache.org/viewvc/avro/trunk/lang/java/avro/src/test/java/org/apache/avro/specific/TestSpecificData.java?rev=1412334&r1=1412333&r2=1412334&view=diff
==============================================================================
---
avro/trunk/lang/java/avro/src/test/java/org/apache/avro/specific/TestSpecificData.java
(original)
+++
avro/trunk/lang/java/avro/src/test/java/org/apache/avro/specific/TestSpecificData.java
Wed Nov 21 22:29:42 2012
@@ -18,14 +18,20 @@
package org.apache.avro.specific;
+import java.io.IOException;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.util.Arrays;
+import org.apache.avro.FooBarSpecificRecord;
import org.apache.avro.Schema;
import org.apache.avro.Schema.Type;
+import org.apache.avro.TypeEnum;
+import org.codehaus.jackson.JsonFactory;
+import org.codehaus.jackson.JsonParser;
+import org.codehaus.jackson.map.ObjectMapper;
import org.junit.Before;
import org.junit.Test;
@@ -73,6 +79,23 @@ public class TestSpecificData {
Reflection.class.getMethod("primitive", integerClass);
}
+ @Test
+ public void testToString() throws IOException {
+ FooBarSpecificRecord foo = FooBarSpecificRecord.newBuilder()
+ .setId(123)
+ .setRelatedids(Arrays.asList(1,2,3))
+ .setTypeEnum(TypeEnum.c)
+ .build();
+
+ String json = foo.toString();
+ JsonFactory factory = new JsonFactory();
+ JsonParser parser = factory.createJsonParser(json);
+ ObjectMapper mapper = new ObjectMapper();
+
+ // will throw exception if string is not parsable json
+ mapper.readTree(parser);
+ }
+
static class Reflection {
public void primitive(int i) {}
public void primitiveWrapper(Integer i) {}
Modified: avro/trunk/lang/java/avro/src/test/resources/FooBarSpecificRecord.avsc
URL:
http://svn.apache.org/viewvc/avro/trunk/lang/java/avro/src/test/resources/FooBarSpecificRecord.avsc?rev=1412334&r1=1412333&r2=1412334&view=diff
==============================================================================
--- avro/trunk/lang/java/avro/src/test/resources/FooBarSpecificRecord.avsc
(original)
+++ avro/trunk/lang/java/avro/src/test/resources/FooBarSpecificRecord.avsc Wed
Nov 21 22:29:42 2012
@@ -5,6 +5,15 @@
"fields": [
{"name": "id", "type": "int"},
{"name": "relatedids", "type":
- {"type": "array", "items": "int"}}
+ {"type": "array", "items": "int"}},
+ {"name": "typeEnum", "type":
+ ["null", {
+ "type": "enum",
+ "name": "TypeEnum",
+ "namespace": "org.apache.avro",
+ "symbols" : ["a","b", "c"]
+ }],
+ "default": null
+ }
]
}