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/08/04 00:48:57 UTC

svn commit: r982058 - in /avro/trunk: CHANGES.txt lang/java/src/java/org/apache/avro/Schema.java lang/java/src/test/java/org/apache/avro/TestSchema.java

Author: cutting
Date: Tue Aug  3 22:48:57 2010
New Revision: 982058

URL: http://svn.apache.org/viewvc?rev=982058&view=rev
Log:
AVRO-601.  Java: Add per-field property support.

Modified:
    avro/trunk/CHANGES.txt
    avro/trunk/lang/java/src/java/org/apache/avro/Schema.java
    avro/trunk/lang/java/src/test/java/org/apache/avro/TestSchema.java

Modified: avro/trunk/CHANGES.txt
URL: http://svn.apache.org/viewvc/avro/trunk/CHANGES.txt?rev=982058&r1=982057&r2=982058&view=diff
==============================================================================
--- avro/trunk/CHANGES.txt (original)
+++ avro/trunk/CHANGES.txt Tue Aug  3 22:48:57 2010
@@ -83,6 +83,8 @@ Avro 1.4.0 (unreleased)
     AVRO-582. Java: Add comment to generated code indicating that
     set() and get() are not for use by applications.  (cutting)
 
+    AVRO-601. Java: Add per-field property support. (cutting)
+
   BUG FIXES
 
     AVRO-502. Memory leak from parsing JSON schema.

Modified: avro/trunk/lang/java/src/java/org/apache/avro/Schema.java
URL: http://svn.apache.org/viewvc/avro/trunk/lang/java/src/java/org/apache/avro/Schema.java?rev=982058&r1=982057&r2=982058&view=diff
==============================================================================
--- avro/trunk/lang/java/src/java/org/apache/avro/Schema.java (original)
+++ avro/trunk/lang/java/src/java/org/apache/avro/Schema.java Tue Aug  3 22:48:57 2010
@@ -91,7 +91,6 @@ public abstract class Schema {
   };
 
   private final Type type;
-  Map<String,String> props = new HashMap<String,String>(1);
 
   Schema(Type type) { this.type = type; }
 
@@ -110,13 +109,41 @@ public abstract class Schema {
     }
   }
 
-  private static final Set<String> RESERVED_PROPS = new HashSet<String>();
+  private static final class Props extends HashMap<String,String> {
+    private Set<String> reserved;
+    public Props(Set<String> reserved) {
+      super(1);
+      this.reserved = reserved;
+    }
+    public void add(String name, String value) {
+      if (reserved.contains(name))
+        throw new AvroRuntimeException("Can't set reserved property: " + name);
+      
+      if (value == null)
+        throw new AvroRuntimeException("Can't set a property to null: " + name);
+    
+      String old = get(name);
+      if (old == null)
+        put(name, value);
+      else if (!old.equals(value))
+        throw new AvroRuntimeException("Can't overwrite property: " + name);
+    }
+
+    public void write(JsonGenerator gen) throws IOException {
+      for (Map.Entry<String,String> e : entrySet())
+        gen.writeStringField(e.getKey(), e.getValue());
+    }
+  }
+
+  private static final Set<String> SCHEMA_RESERVED = new HashSet<String>();
   static {
-    Collections.addAll(RESERVED_PROPS,
-                       "fields", "items", "name", "namespace",
+    Collections.addAll(SCHEMA_RESERVED,
+                       "doc", "fields", "items", "name", "namespace",
                        "size", "symbols", "values", "type");
   }
 
+  Props props = new Props(SCHEMA_RESERVED);
+
   /**
    * Returns the value of the named property in this schema.
    * Returns <tt>null</tt> if there is no property with that name.
@@ -135,23 +162,7 @@ public abstract class Schema {
    * @param value The value for the property to add
    */
   public synchronized void addProp(String name, String value) {
-    if (RESERVED_PROPS.contains(name)) {
-      throw new AvroRuntimeException("Can't set a reserved property: " + name);
-    }
-    
-    if (value == null) {
-      throw new AvroRuntimeException(
-          "Can't set a null value for property: " + name);
-    }
-    
-    String v = props.get(name);
-    if (v != null) {
-      if (! v.equals(value)) {
-        throw new AvroRuntimeException("Can't overwrite property: " + name);
-      }
-    } else {
-      props.put(name, value);
-    }
+    props.add(name, value);
   }
 
   /** Create an anonymous record schema. */
@@ -310,16 +321,11 @@ public abstract class Schema {
     } else {
       gen.writeStartObject();
       gen.writeStringField("type", getName());
-      writeProps(gen);
+      props.write(gen);
       gen.writeEndObject();
     }
   }
 
-  void writeProps(JsonGenerator gen) throws IOException {
-    for (Map.Entry<String,String> e : props.entrySet())
-      gen.writeStringField(e.getKey(), e.getValue());
-  }
-
   void fieldsToJson(Names names, JsonGenerator gen) throws IOException {
     throw new AvroRuntimeException("Not a record: "+this);
   }
@@ -333,6 +339,11 @@ public abstract class Schema {
   }
   public int hashCode() { return getType().hashCode() + props.hashCode(); }
 
+  private static final Set<String> FIELD_RESERVED = new HashSet<String>();
+  static {
+    Collections.addAll(FIELD_RESERVED, "default","doc","name","order","type");
+  }
+
   /** A field within a record. */
   public static class Field {
 
@@ -344,11 +355,12 @@ public abstract class Schema {
     };
 
     private final String name;    // name of the field.
-    private int position = -1;
+    private transient int position = -1;
     private final Schema schema;
     private final String doc;
     private final JsonNode defaultValue;
     private final Order order;
+    private final Props props = new Props(FIELD_RESERVED);
 
     public Field(String name, Schema schema, String doc,
         JsonNode defaultValue) {
@@ -371,17 +383,25 @@ public abstract class Schema {
     public String doc() { return doc; }
     public JsonNode defaultValue() { return defaultValue; }
     public Order order() { return order; }
+    /** Return the value of the named property in this field or null. */
+    public synchronized String getProp(String name) { return props.get(name); }
+    /** Add a property with the given name to this field. */
+    public synchronized void addProp(String name, String value) {
+      props.add(name, value);
+    }
     public boolean equals(Object other) {
       if (other == this) return true;
       if (!(other instanceof Field)) return false;
       Field that = (Field) other;
-      return (position == that.position) &&
+      return (name.equals(that.name)) &&
         (schema.equals(that.schema)) &&
         (defaultValue == null
          ? that.defaultValue == null
-         : (defaultValue.equals(that.defaultValue)));
+         : (defaultValue.equals(that.defaultValue))) &&
+        (order.equals(that.order)) &&
+        props.equals(that.props);
     }
-    public int hashCode() { return schema.hashCode(); }
+    public int hashCode() { return name.hashCode() + schema.hashCode(); }
   }
 
   private static class Name {
@@ -552,7 +572,7 @@ public abstract class Schema {
       writeName(names, gen);
       gen.writeFieldName("fields");
       fieldsToJson(names, gen);
-      writeProps(gen);
+      props.write(gen);
       gen.writeEndObject();
     }
 
@@ -569,6 +589,7 @@ public abstract class Schema {
         }
         if (f.order() != Field.Order.ASCENDING)
           gen.writeStringField("order", f.order().name);
+        f.props.write(gen);
         gen.writeEndObject();
       }
       gen.writeEndArray();
@@ -610,7 +631,7 @@ public abstract class Schema {
       for (String symbol : symbols)
         gen.writeString(symbol);
       gen.writeEndArray();
-      writeProps(gen);
+      props.write(gen);
       gen.writeEndObject();
     }
   }
@@ -636,7 +657,7 @@ public abstract class Schema {
       gen.writeStringField("type", "array");
       gen.writeFieldName("items");
       elementType.toJson(names, gen);
-      writeProps(gen);
+      props.write(gen);
       gen.writeEndObject();
     }
   }
@@ -662,7 +683,7 @@ public abstract class Schema {
       gen.writeStringField("type", "map");
       gen.writeFieldName("values");
       valueType.toJson(names, gen);
-      writeProps(gen);
+      props.write(gen);
       gen.writeEndObject();
     }
   }
@@ -745,7 +766,7 @@ public abstract class Schema {
       gen.writeStringField("type", "fixed");
       writeName(names, gen);
       gen.writeNumberField("size", size);
-      writeProps(gen);
+      props.write(gen);
       gen.writeEndObject();
     }
   }
@@ -914,8 +935,16 @@ public abstract class Schema {
           JsonNode orderNode = field.get("order");
           if (orderNode != null)
             order = Field.Order.valueOf(orderNode.getTextValue().toUpperCase());
-          fields.add(new Field(fieldName, fieldSchema,
-              fieldDoc, field.get("default"), order));
+          Field f = new Field(fieldName, fieldSchema,
+                              fieldDoc, field.get("default"), order);
+          Iterator<String> i = field.getFieldNames();
+          while (i.hasNext()) {                       // add field props
+            String prop = i.next();
+            String value = field.get(prop).getTextValue();
+            if (!FIELD_RESERVED.contains(prop) && value != null)
+              f.addProp(prop, value);
+          }
+          fields.add(f);
         }
         result.setFields(fields);
       } else if (type.equals("enum")) {           // enum
@@ -949,7 +978,7 @@ public abstract class Schema {
       while (i.hasNext()) {                       // add properties
         String prop = i.next();
         String value = schema.get(prop).getTextValue();
-        if (!RESERVED_PROPS.contains(prop) && value != null) // ignore reserved
+        if (!SCHEMA_RESERVED.contains(prop) && value != null) // ignore reserved
           result.addProp(prop, value);
       }
       if (savedSpace != null)

Modified: avro/trunk/lang/java/src/test/java/org/apache/avro/TestSchema.java
URL: http://svn.apache.org/viewvc/avro/trunk/lang/java/src/test/java/org/apache/avro/TestSchema.java?rev=982058&r1=982057&r2=982058&view=diff
==============================================================================
--- avro/trunk/lang/java/src/test/java/org/apache/avro/TestSchema.java (original)
+++ avro/trunk/lang/java/src/test/java/org/apache/avro/TestSchema.java Tue Aug  3 22:48:57 2010
@@ -158,8 +158,16 @@ public class TestSchema {
   @Test
   public void testRecord() throws Exception {
     String recordJson = "{\"type\":\"record\", \"name\":\"Test\", \"fields\":"
-      +"[{\"name\":\"f\", \"type\":\"long\"}]}";
+      +"[{\"name\":\"f\", \"type\":\"long\", \"foo\":\"bar\"}]}";
     Schema schema = Schema.parse(recordJson);
+
+    // test field props
+    assertEquals("bar", schema.getField("f").getProp("foo"));
+    assertEquals("bar", Schema.parse(schema.toString())
+                 .getField("f").getProp("foo"));
+    schema.getField("f").addProp("baz", "boo");
+    assertEquals("boo", schema.getField("f").getProp("baz"));
+
     GenericData.Record record = new GenericData.Record(schema);
     record.put("f", 11L);
     check(recordJson, "{\"f\":11}", record, false);