You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@avro.apache.org by cu...@apache.org on 2010/03/04 00:27:49 UTC

svn commit: r918770 - in /hadoop/avro/trunk: ./ lang/java/src/java/org/apache/avro/reflect/ lang/java/src/java/org/apache/avro/specific/ lang/java/src/test/java/org/apache/avro/

Author: cutting
Date: Wed Mar  3 23:27:48 2010
New Revision: 918770

URL: http://svn.apache.org/viewvc?rev=918770&view=rev
Log:
AVRO-432.  Add @Nullable annotation to Java reflect API.

Added:
    hadoop/avro/trunk/lang/java/src/java/org/apache/avro/reflect/Nullable.java
Modified:
    hadoop/avro/trunk/CHANGES.txt
    hadoop/avro/trunk/lang/java/src/java/org/apache/avro/reflect/ReflectData.java
    hadoop/avro/trunk/lang/java/src/java/org/apache/avro/specific/SpecificData.java
    hadoop/avro/trunk/lang/java/src/test/java/org/apache/avro/TestReflect.java

Modified: hadoop/avro/trunk/CHANGES.txt
URL: http://svn.apache.org/viewvc/hadoop/avro/trunk/CHANGES.txt?rev=918770&r1=918769&r2=918770&view=diff
==============================================================================
--- hadoop/avro/trunk/CHANGES.txt (original)
+++ hadoop/avro/trunk/CHANGES.txt Wed Mar  3 23:27:48 2010
@@ -2,6 +2,10 @@
 
 Avro 1.3.1 (unreleased)
 
+  NEW FEATURES
+
+    AVRO-432. Add @Nullable annotation to Java reflect API. (cutting)
+
   IMPROVEMENTS
 
     AVRO-426. Include a ruby gem in distributions.

Added: hadoop/avro/trunk/lang/java/src/java/org/apache/avro/reflect/Nullable.java
URL: http://svn.apache.org/viewvc/hadoop/avro/trunk/lang/java/src/java/org/apache/avro/reflect/Nullable.java?rev=918770&view=auto
==============================================================================
--- hadoop/avro/trunk/lang/java/src/java/org/apache/avro/reflect/Nullable.java (added)
+++ hadoop/avro/trunk/lang/java/src/java/org/apache/avro/reflect/Nullable.java Wed Mar  3 23:27:48 2010
@@ -0,0 +1,33 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.avro.reflect;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/** Declares that null is a valid value for a Java type.  Causes an Avro union
+ * with null to be used.  May be applied to parameters, fields and methods (to
+ * declare the return type).
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD})
+@Documented
+public @interface Nullable {}

Modified: hadoop/avro/trunk/lang/java/src/java/org/apache/avro/reflect/ReflectData.java
URL: http://svn.apache.org/viewvc/hadoop/avro/trunk/lang/java/src/java/org/apache/avro/reflect/ReflectData.java?rev=918770&r1=918769&r2=918770&view=diff
==============================================================================
--- hadoop/avro/trunk/lang/java/src/java/org/apache/avro/reflect/ReflectData.java (original)
+++ hadoop/avro/trunk/lang/java/src/java/org/apache/avro/reflect/ReflectData.java Wed Mar  3 23:27:48 2010
@@ -61,9 +61,7 @@
 
     protected Schema createFieldSchema(Field field, Map<String, Schema> names) {
       Schema schema = super.createFieldSchema(field, names);
-      return Schema.createUnion(Arrays.asList(new Schema[] {
-            schema,
-            Schema.create(Schema.Type.NULL) }));
+      return makeNullable(schema);
     }
   }
   
@@ -310,6 +308,12 @@
     return Schema.createUnion(branches);
   }
 
+  /** Create and return a union of the null schema and the provided schema. */
+  public static Schema makeNullable(Schema schema) {
+    return Schema.createUnion(Arrays.asList(Schema.create(Schema.Type.NULL),
+                                            schema));
+  }
+
   // Return of this class and its superclasses to serialize.
   // Not cached, since this is only used to create schemas, which are cached.
   private Collection<Field> getFields(Class recordClass) {
@@ -329,7 +333,10 @@
 
   /** Create a schema for a field. */
   protected Schema createFieldSchema(Field field, Map<String, Schema> names) {
-    return createSchema(field.getGenericType(), names);
+    Schema schema = createSchema(field.getGenericType(), names);
+    if (field.isAnnotationPresent(Nullable.class))           // nullable
+      schema = makeNullable(schema);
+    return schema;
   }
 
   /** Return the protocol for a Java interface.
@@ -365,12 +372,12 @@
     Type[] paramTypes = method.getGenericParameterTypes();
     Annotation[][] annotations = method.getParameterAnnotations();
     for (int i = 0; i < paramTypes.length; i++) {
-      Schema paramSchema = null;
+      Schema paramSchema = getSchema(paramTypes[i], names);
       for (int j = 0; j < annotations[i].length; j++)
         if (annotations[i][j] instanceof Union)
           paramSchema = getAnnotatedUnion(((Union)annotations[i][j]), names);
-      if (paramSchema == null)
-        paramSchema = getSchema(paramTypes[i], names);
+        else if (annotations[i][j] instanceof Nullable)
+          paramSchema = makeNullable(paramSchema);
       String paramName =  paramNames.length == paramTypes.length
         ? paramNames[i]
         : paramSchema.getName()+i;
@@ -383,6 +390,8 @@
     Schema response = union == null
       ? getSchema(method.getGenericReturnType(), names)
       : getAnnotatedUnion(union, names);
+    if (method.isAnnotationPresent(Nullable.class))          // nullable
+      response = makeNullable(response);
 
     List<Schema> errs = new ArrayList<Schema>();
     errs.add(Protocol.SYSTEM_ERROR);              // every method can throw
@@ -408,5 +417,4 @@
     throw new UnsupportedOperationException();
   }
 
-
 }

Modified: hadoop/avro/trunk/lang/java/src/java/org/apache/avro/specific/SpecificData.java
URL: http://svn.apache.org/viewvc/hadoop/avro/trunk/lang/java/src/java/org/apache/avro/specific/SpecificData.java?rev=918770&r1=918769&r2=918770&view=diff
==============================================================================
--- hadoop/avro/trunk/lang/java/src/java/org/apache/avro/specific/SpecificData.java (original)
+++ hadoop/avro/trunk/lang/java/src/java/org/apache/avro/specific/SpecificData.java Wed Mar  3 23:27:48 2010
@@ -157,7 +157,6 @@
       ParameterizedType ptype = (ParameterizedType)type;
       Class raw = (Class)ptype.getRawType();
       java.lang.reflect.Type[] params = ptype.getActualTypeArguments();
-      for (int i = 0; i < params.length; i++)
       if (GenericArray.class.isAssignableFrom(raw)) { // array
         if (params.length != 1)
           throw new AvroTypeException("No array type specified.");
@@ -168,6 +167,8 @@
         if (!(key == Utf8.class))
           throw new AvroTypeException("Map key class not Utf8: "+key);
         return Schema.createMap(createSchema(value, names));
+      } else {
+        return createSchema(raw, names);
       }
     } else if (type instanceof Class) {               // class
       Class c = (Class)type;

Modified: hadoop/avro/trunk/lang/java/src/test/java/org/apache/avro/TestReflect.java
URL: http://svn.apache.org/viewvc/hadoop/avro/trunk/lang/java/src/test/java/org/apache/avro/TestReflect.java?rev=918770&r1=918769&r2=918770&view=diff
==============================================================================
--- hadoop/avro/trunk/lang/java/src/test/java/org/apache/avro/TestReflect.java (original)
+++ hadoop/avro/trunk/lang/java/src/test/java/org/apache/avro/TestReflect.java Wed Mar  3 23:27:48 2010
@@ -40,6 +40,7 @@
 import org.apache.avro.reflect.ReflectDatumReader;
 import org.apache.avro.reflect.ReflectDatumWriter;
 import org.apache.avro.reflect.Stringable;
+import org.apache.avro.reflect.Nullable;
 import org.apache.avro.reflect.Union;
 import org.junit.Test;
 
@@ -268,6 +269,57 @@
     checkReadWrite(new R10("foo"), r10Schema);
   }
 
+  // test Nullable annotation on field
+  public static class R11 {
+    @Nullable private String text;
+    public boolean equals(Object o) {
+      if (!(o instanceof R11)) return false;
+      R11 that = (R11)o;
+      if (this.text == null) return that.text == null;
+      return this.text.equals(that.text);
+    }
+  }
+  
+  @Test public void testR11() throws Exception {
+    Schema r11Record = ReflectData.get().getSchema(R11.class);
+    assertEquals(Schema.Type.RECORD, r11Record.getType());
+    Schema r11Field = r11Record.getField("text").schema();
+    assertEquals(Schema.Type.UNION, r11Field.getType());
+    assertEquals(Schema.Type.NULL, r11Field.getTypes().get(0).getType());
+    Schema r11String = r11Field.getTypes().get(1);
+    assertEquals(Schema.Type.STRING, r11String.getType());
+    R11 r11 = new R11();
+    checkReadWrite(r11, r11Record);
+    r11.text = "foo";
+    checkReadWrite(r11, r11Record);
+  }
+
+  // test nullable annotation on methods and parameters
+  public static interface P1 {
+    @Nullable String foo(@Nullable String s);
+  }
+
+  @Test public void testP1() throws Exception {
+    Protocol p1 = ReflectData.get().getProtocol(P1.class);
+    Protocol.Message message = p1.getMessages().get("foo");
+    // check response schema is union
+    Schema response = message.getResponse();
+    assertEquals(Schema.Type.UNION, response.getType());
+    assertEquals(Schema.Type.NULL, response.getTypes().get(0).getType());
+    assertEquals(Schema.Type.STRING, response.getTypes().get(1).getType());
+    // check request schema is union
+    Schema request = message.getRequest();
+    Field field = request.getField("s");
+    assertNotNull("field 's' should not be null", field);
+    Schema param = field.schema();
+    assertEquals(Schema.Type.UNION, param.getType());
+    assertEquals(Schema.Type.NULL, param.getTypes().get(0).getType());
+    assertEquals(Schema.Type.STRING, param.getTypes().get(1).getType());
+    // check union erasure
+    assertEquals(String.class, ReflectData.get().getClass(response));
+    assertEquals(String.class, ReflectData.get().getClass(param));
+  }
+
   void checkReadWrite(Object object) throws Exception {
     checkReadWrite(object, ReflectData.get().getSchema(object.getClass()));
   }