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 2011/03/31 23:16:49 UTC

svn commit: r1087439 [4/6] - in /avro/trunk: ./ lang/csharp/ lang/csharp/lib/ lang/csharp/lib/main/ lang/csharp/lib/test/ lang/csharp/src/ lang/csharp/src/apache/ lang/csharp/src/apache/codegen/ lang/csharp/src/apache/codegen/Properties/ lang/csharp/sr...

Added: avro/trunk/lang/csharp/src/apache/main/Schema/NamedSchema.cs
URL: http://svn.apache.org/viewvc/avro/trunk/lang/csharp/src/apache/main/Schema/NamedSchema.cs?rev=1087439&view=auto
==============================================================================
--- avro/trunk/lang/csharp/src/apache/main/Schema/NamedSchema.cs (added)
+++ avro/trunk/lang/csharp/src/apache/main/Schema/NamedSchema.cs Thu Mar 31 21:16:28 2011
@@ -0,0 +1,223 @@
+/**
+ * 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.
+ */
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Newtonsoft.Json.Linq;
+
+
+namespace Avro
+{
+    /// <summary>
+    /// Base class for all named schemas: fixed, enum, record
+    /// </summary>
+    public abstract class NamedSchema : Schema
+    {
+        /// <summary>
+        /// Name of the schema, contains name, namespace and enclosing namespace
+        /// </summary>
+        public SchemaName SchemaName { get; private set; }
+
+        /// <summary>
+        /// Name of the schema
+        /// </summary>
+        public override string Name
+        {
+            get { return SchemaName.Name; }
+        }
+
+        /// <summary>
+        /// Namespace of the schema
+        /// </summary>
+        public string Namespace
+        {
+            get { return SchemaName.Namespace; }
+        }
+
+        /// <summary>
+        /// Namespace.Name of the schema
+        /// </summary>
+        public string Fullname
+        {
+            get { return SchemaName.Fullname; }
+        }
+
+        /// <summary>
+        /// List of aliases for this named schema
+        /// </summary>
+        private readonly IList<SchemaName> aliases;
+
+        /*
+        /// <summary>
+        /// Returns the SchemaName object of the named schema
+        /// </summary>
+        /// <returns></returns>
+        public SchemaName GetName()
+        {
+            return schemaName;
+        }
+         */
+
+        /// <summary>
+        /// Static function to return a new instance of the named schema
+        /// </summary>
+        /// <param name="jo">JSON object of the named schema</param>
+        /// <param name="names">list of named schemas already read</param>
+        /// <param name="encspace">enclosing namespace of the named schema</param>
+        /// <returns></returns>
+        internal static NamedSchema NewInstance(JObject jo, PropertyMap props, SchemaNames names, string encspace)
+        {
+            string type = JsonHelper.GetRequiredString(jo, "type");
+            switch (type)
+            {
+                case "fixed":
+                    return FixedSchema.NewInstance(jo, props, names, encspace);
+                case "enum":
+                    return EnumSchema.NewInstance(jo, props, names, encspace);
+                case "record":
+                    return RecordSchema.NewInstance(Type.Record, jo, props, names, encspace);
+                case "error":
+                    return RecordSchema.NewInstance(Type.Error, jo, props, names, encspace);
+                default:
+                    NamedSchema result;
+                    if (names.TryGetValue(type, null, encspace, out result))
+                        return result;
+                    return null;
+            }
+        }
+
+        /// <summary>
+        /// Constructor for named schema class
+        /// </summary>
+        /// <param name="type">schema type</param>
+        /// <param name="name">name</param>
+        /// <param name="names">list of named schemas already read</param>
+        protected NamedSchema(Type type, SchemaName name, IList<SchemaName> aliases, PropertyMap props, SchemaNames names)
+                                : base(type, props)
+        {
+            this.SchemaName = name;
+            this.aliases = aliases;
+            try
+            {
+                names.Add(name, this);
+            }
+            catch (Exception ex)
+            {
+                throw new AvroException("Could not add " + name.Fullname + ". " + ex.Message);
+            }
+        }
+
+        /// <summary>
+        /// Parses the name and namespace from the given JSON schema object then creates
+        /// SchemaName object including the given enclosing namespace
+        /// </summary>
+        /// <param name="jtok">JSON object to read</param>
+        /// <param name="encspace">enclosing namespace</param>
+        /// <returns>new SchemaName object</returns>
+        protected static SchemaName GetName(JToken jtok, string encspace)
+        {
+            String n = JsonHelper.GetOptionalString(jtok, "name");      // Changed this to optional string for anonymous records in messages 
+            String ns = JsonHelper.GetOptionalString(jtok, "namespace");
+            return new SchemaName(n, ns, encspace);
+        }
+
+        /// <summary>
+        /// Parses the 'aliases' property from the given JSON token
+        /// </summary>
+        /// <param name="jtok">JSON object to read</param>
+        /// <param name="space">namespace of the name this alias is for</param>
+        /// <param name="encspace">enclosing namespace of the name this alias is for</param>
+        /// <returns>List of SchemaName that represents the list of alias. If no 'aliases' specified, then it returns null.</returns>
+        protected static IList<SchemaName> GetAliases(JToken jtok, string space, string encspace)
+        {
+            JToken jaliases = jtok["aliases"];
+            if (null == jaliases)
+                return null;
+
+            if (jaliases.Type != JTokenType.Array)
+                throw new SchemaParseException("Aliases must be of format JSON array of strings");
+
+            var aliases = new List<SchemaName>();
+            foreach (JToken jalias in jaliases)
+            {
+                if (jalias.Type != JTokenType.String)
+                    throw new SchemaParseException("Aliases must be of format JSON array of strings");
+
+                aliases.Add(new SchemaName((string)jalias, space, encspace));
+            }
+            return aliases;
+        }
+
+        protected bool InAliases(SchemaName name)
+        {
+            if (null != aliases)
+            {
+                foreach (SchemaName alias in aliases)
+                    if (name.Equals(alias)) return true;
+            }
+            return false;
+        }
+
+        /// <summary>
+        /// Writes named schema in JSON format
+        /// </summary>
+        /// <param name="writer">JSON writer</param>
+        /// <param name="names">list of named schemas already written</param>
+        /// <param name="encspace">enclosing namespace of the named schema</param>
+        protected internal override void WriteJson(Newtonsoft.Json.JsonTextWriter writer, SchemaNames names, string encspace)
+        {
+            if (!names.Add(this))
+            {
+                // schema is already in the list, write name only
+                SchemaName schemaName = this.SchemaName;
+                string name;
+                if (schemaName.Namespace != encspace)
+                    name = schemaName.Namespace + "." + schemaName.Name;  // we need to add the qualifying namespace of the target schema if it's not the same as current namespace
+                else
+                    name = schemaName.Name;
+                writer.WriteValue(name);
+            }
+            else
+                // schema is not in the list, write full schema definition
+                base.WriteJson(writer, names, encspace);
+        }
+
+        /// <summary>
+        /// Writes named schema in JSON format
+        /// </summary>
+        /// <param name="writer">JSON writer</param>
+        /// <param name="names">list of named schemas already written</param>
+        /// <param name="encspace">enclosing namespace of the named schema</param>
+        protected internal override void WriteJsonFields(Newtonsoft.Json.JsonTextWriter writer, SchemaNames names, string encspace)
+        {
+            this.SchemaName.WriteJson(writer, names, encspace);
+
+            if (null != aliases)
+            {
+                writer.WritePropertyName("aliases");
+                writer.WriteStartArray();
+                foreach (SchemaName name in aliases)
+                {
+                    string fullname = (null != name.Space) ? name.Space + "." + name.Name : name.Name;
+                    writer.WriteValue(fullname);
+                }
+                writer.WriteEndArray();
+            }
+        }
+    }
+}

Added: avro/trunk/lang/csharp/src/apache/main/Schema/PrimitiveSchema.cs
URL: http://svn.apache.org/viewvc/avro/trunk/lang/csharp/src/apache/main/Schema/PrimitiveSchema.cs?rev=1087439&view=auto
==============================================================================
--- avro/trunk/lang/csharp/src/apache/main/Schema/PrimitiveSchema.cs (added)
+++ avro/trunk/lang/csharp/src/apache/main/Schema/PrimitiveSchema.cs Thu Mar 31 21:16:28 2011
@@ -0,0 +1,130 @@
+/**
+ * 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.
+ */
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Newtonsoft.Json;
+
+namespace Avro
+{
+    /// <summary>
+    /// Class for schemas of primitive types
+    /// </summary>
+    public sealed class PrimitiveSchema : UnnamedSchema
+    {
+        /// <summary>
+        /// Constructor for primitive schema
+        /// </summary>
+        /// <param name="type"></param>
+        private PrimitiveSchema(Type type, PropertyMap props) : base(type, props)
+        {
+        }
+
+        /// <summary>
+        /// Static function to return new instance of primitive schema
+        /// </summary>
+        /// <param name="type">primitive type</param>
+        /// <returns></returns>
+        public static PrimitiveSchema NewInstance(string type, PropertyMap props = null)
+        {
+            const string q = "\"";
+            if (type.StartsWith(q) && type.EndsWith(q)) type = type.Substring(1, type.Length - 2);
+            switch (type)
+            {
+                case "null":
+                    return new PrimitiveSchema(Schema.Type.Null, props);
+                case "boolean":
+                    return new PrimitiveSchema(Schema.Type.Boolean, props);
+                case "int":
+                    return new PrimitiveSchema(Schema.Type.Int, props);
+                case "long":
+                    return new PrimitiveSchema(Schema.Type.Long, props);
+                case "float":
+                    return new PrimitiveSchema(Schema.Type.Float, props);
+                case "double":
+                    return new PrimitiveSchema(Schema.Type.Double, props);
+                case "bytes":
+                    return new PrimitiveSchema(Schema.Type.Bytes, props);
+                case "string":
+                    return new PrimitiveSchema(Schema.Type.String, props);
+                default:
+                    return null;
+            }
+        }
+
+        /// <summary>
+        /// Writes primitive schema in JSON format
+        /// </summary>
+        /// <param name="w"></param>
+        /// <param name="names"></param>
+        /// <param name="encspace"></param>
+        protected internal override void WriteJson(JsonTextWriter w, SchemaNames names, string encspace)
+        {
+            w.WriteValue(Name);
+        }
+
+        /// <summary>
+        /// Checks if this schema can read data written by the given schema. Used for decoding data.
+        /// </summary>
+        /// <param name="writerSchema">writer schema</param>
+        /// <returns>true if this and writer schema are compatible based on the AVRO specification, false otherwise</returns>
+        public override bool CanRead(Schema writerSchema)
+        {
+            if (writerSchema is UnionSchema || Tag == writerSchema.Tag) return true;
+            Type t = writerSchema.Tag;
+            switch (Tag)
+            {
+                case Type.Double:
+                    return t == Type.Int || t == Type.Long || t == Type.Float;
+                case Type.Float:
+                    return t == Type.Int || t == Type.Long;
+                case Type.Long:
+                    return t == Type.Int;
+                default:
+                    return false;
+            }
+        }
+
+        /// <summary>
+        /// Function to compare equality of two primitive schemas
+        /// </summary>
+        /// <param name="obj">other primitive schema</param>
+        /// <returns>true two schemas are equal, false otherwise</returns>
+        public override bool Equals(object obj)
+        {
+            if (this == obj) return true;
+
+            if (obj != null && obj is PrimitiveSchema)
+            {
+                var that = obj as PrimitiveSchema;
+                if (this.Tag == that.Tag)
+                    return areEqual(that.Props, this.Props);
+            }
+            return false;
+        }
+
+        /// <summary>
+        /// Hashcode function
+        /// </summary>
+        /// <returns></returns>
+        public override int GetHashCode()
+        {
+            return 13 * Tag.GetHashCode() + getHashCode(Props);
+        }
+    }
+}

Added: avro/trunk/lang/csharp/src/apache/main/Schema/Property.cs
URL: http://svn.apache.org/viewvc/avro/trunk/lang/csharp/src/apache/main/Schema/Property.cs?rev=1087439&view=auto
==============================================================================
--- avro/trunk/lang/csharp/src/apache/main/Schema/Property.cs (added)
+++ avro/trunk/lang/csharp/src/apache/main/Schema/Property.cs Thu Mar 31 21:16:28 2011
@@ -0,0 +1,124 @@
+/**
+ * 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.
+ */
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Newtonsoft.Json.Linq;
+using Newtonsoft.Json;
+
+namespace Avro
+{
+    public class PropertyMap : Dictionary<string, string>
+    {
+        /// <summary>
+        /// Set of reserved schema property names, any other properties not defined in this set are custom properties and can be added to this map
+        /// </summary>
+        private static readonly HashSet<string> ReservedProps = new HashSet<string>() { "type", "name", "namespace", "fields", "items", "size", "symbols", "values", "aliases", "order", "doc", "default" };
+
+        /// <summary>
+        /// Parses the custom properties from the given JSON object and stores them
+        /// into the schema's list of custom properties
+        /// </summary>
+        /// <param name="jtok">JSON object to prase</param>
+        public void Parse(JToken jtok)
+        {
+            JObject jo = jtok as JObject;
+            foreach (JProperty prop in jo.Properties())
+            {
+                if (ReservedProps.Contains(prop.Name))
+                    continue;
+                if (!ContainsKey(prop.Name))
+                    Add(prop.Name, prop.Value.ToString());
+            }
+        }
+
+        /// <summary>
+        /// Adds a custom property to the schema
+        /// </summary>
+        /// <param name="key">custom property name</param>
+        /// <param name="value">custom property value</param>
+        public void Set(string key, string value)
+        {
+            if (ReservedProps.Contains(key))
+                throw new AvroException("Can't set reserved property: " + key);
+
+            string oldValue;
+            if (TryGetValue(key, out oldValue))
+            {
+                if (!oldValue.Equals(value)) throw new AvroException("Property cannot be overwritten: " + key);
+            }
+            else
+                Add(key, value);
+        }
+
+        /// <summary>
+        /// Writes the schema's custom properties in JSON format
+        /// </summary>
+        /// <param name="writer">JSON writer</param>
+        public void WriteJson(JsonTextWriter writer)
+        {
+            foreach (KeyValuePair<string, string> kp in this)
+            {
+                if (ReservedProps.Contains(kp.Key)) continue;
+
+                writer.WritePropertyName(kp.Key);
+                writer.WriteRawValue(kp.Value);
+            }
+        }
+
+        /// <summary>
+        /// Function to compare equality of two PropertyMaps
+        /// </summary>
+        /// <param name="obj">other PropertyMap</param>
+        /// <returns>true if contents of the two maps are the same, false otherwise</returns>
+        public override bool Equals(object obj)
+        {
+            if (this == obj) return true;
+
+            if (obj != null && obj is PropertyMap)
+            {
+                var that = obj as PropertyMap;
+                if (this.Count != that.Count) 
+                    return false; 
+                foreach (KeyValuePair<string, string> pair in this) 
+                { 
+                    if (!that.ContainsKey(pair.Key))
+                        return false;
+                    if (!pair.Value.Equals(that[pair.Key]))
+                        return false; 
+                } 
+                return true;
+            }
+            return false;
+        }
+
+        /// <summary>
+        /// Hashcode function
+        /// </summary>
+        /// <returns></returns>
+        public override int GetHashCode()
+        {
+            int hash = this.Count;
+            int index = 1;
+            foreach (KeyValuePair<string, string> pair in this)
+                hash += (pair.Key.GetHashCode() + pair.Value.GetHashCode()) * index++;
+            return hash;
+        }
+    }
+}

Added: avro/trunk/lang/csharp/src/apache/main/Schema/RecordSchema.cs
URL: http://svn.apache.org/viewvc/avro/trunk/lang/csharp/src/apache/main/Schema/RecordSchema.cs?rev=1087439&view=auto
==============================================================================
--- avro/trunk/lang/csharp/src/apache/main/Schema/RecordSchema.cs (added)
+++ avro/trunk/lang/csharp/src/apache/main/Schema/RecordSchema.cs Thu Mar 31 21:16:28 2011
@@ -0,0 +1,338 @@
+/**
+ * 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.
+ */
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Newtonsoft.Json.Linq;
+using Newtonsoft.Json;
+
+namespace Avro
+{
+    internal delegate T Function<T>();
+
+    /// <summary>
+    /// Class for record schemas
+    /// </summary>
+    public class RecordSchema : NamedSchema
+    {
+        /// <summary>
+        /// List of fields in the record
+        /// </summary>
+        public List<Field> Fields { get; private set; }
+
+        /// <summary>
+        /// Number of fields in the record
+        /// </summary>
+        public int Count { get { return Fields.Count; } }
+
+        /// <summary>
+        /// Map of field name and Field object for faster field lookups
+        /// </summary>
+        private readonly IDictionary<string, Field> fieldLookup;
+
+        private readonly IDictionary<string, Field> fieldAliasLookup;
+        private bool request;
+
+        /// <summary>
+        /// Static function to return new instance of the record schema
+        /// </summary>
+        /// <param name="type">type of record schema, either record or error</param>
+        /// <param name="jtok">JSON object for the record schema</param>
+        /// <param name="names">list of named schema already read</param>
+        /// <param name="encspace">enclosing namespace of the records schema</param>
+        /// <returns>new RecordSchema object</returns>
+        internal static RecordSchema NewInstance(Type type, JToken jtok, PropertyMap props, SchemaNames names, string encspace)
+        {
+            bool request = false;
+            JToken jfields = jtok["fields"];    // normal record
+            if (null == jfields)
+            {
+                jfields = jtok["request"];      // anonymous record from messages
+                if (null != jfields) request = true;
+            }
+            if (null == jfields)
+                throw new SchemaParseException("'fields' cannot be null for record");
+            if (jfields.Type != JTokenType.Array)
+                throw new SchemaParseException("'fields' not an array for record");
+
+            var name = GetName(jtok, encspace);
+            var aliases = NamedSchema.GetAliases(jtok, name.Space, name.EncSpace);
+            var fields = new List<Field>();
+            var fieldMap = new Dictionary<string, Field>();
+            var fieldAliasMap = new Dictionary<string, Field>();
+            var result = new RecordSchema(type, name, aliases, props, fields, request, fieldMap, fieldAliasMap, names);
+
+            int fieldPos = 0;
+            foreach (JObject jfield in jfields)
+            {
+                string fieldName = JsonHelper.GetRequiredString(jfield, "name");
+                Field field = createField(jfield, fieldPos++, names, name.Namespace);  // add record namespace for field look up
+                fields.Add(field);
+                addToFieldMap(fieldMap, fieldName, field);
+                addToFieldMap(fieldAliasMap, fieldName, field);
+
+                if (null != field.aliases)    // add aliases to field lookup map so reader function will find it when writer field name appears only as an alias on the reader field
+                    foreach (string alias in field.aliases)
+                        addToFieldMap(fieldAliasMap, alias, field);
+            }
+            return result;
+        }
+
+        /// <summary>
+        /// Constructor for the record schema
+        /// </summary>
+        /// <param name="type">type of record schema, either record or error</param>
+        /// <param name="name">name of the record schema</param>
+        /// <param name="aliases">list of aliases for the record name</param>
+        /// <param name="fields">list of fields for the record</param>
+        /// <param name="request">true if this is an anonymous record with 'request' instead of 'fields'</param>
+        /// <param name="fieldMap">map of field names and field objects</param>
+        /// <param name="names">list of named schema already read</param>
+        private RecordSchema(Type type, SchemaName name, IList<SchemaName> aliases,  PropertyMap props, 
+                                List<Field> fields, bool request, IDictionary<string, Field> fieldMap, 
+                                IDictionary<string, Field> fieldAliasMap, SchemaNames names)
+                                : base(type, name, aliases, props, names)
+        {
+            this.Fields = fields;
+            this.request = request;
+            this.fieldLookup = fieldMap;
+            this.fieldAliasLookup = fieldAliasMap;
+        }
+
+        /// <summary>
+        /// Creates a new field for the record
+        /// </summary>
+        /// <param name="jfield">JSON object for the field</param>
+        /// <param name="pos">position number of the field</param>
+        /// <param name="names">list of named schemas already read</param>
+        /// <param name="encspace">enclosing namespace of the records schema</param>
+        /// <returns>new Field object</returns>
+        private static Field createField(JToken jfield, int pos, SchemaNames names, string encspace)
+        {
+            var name = JsonHelper.GetRequiredString(jfield, "name");
+            var doc = JsonHelper.GetOptionalString(jfield, "doc");
+
+            var jorder = JsonHelper.GetOptionalString(jfield, "order");
+            Field.SortOrder sortorder = Field.SortOrder.ignore;
+            if (null != jorder)
+                sortorder = (Field.SortOrder) Enum.Parse(typeof(Field.SortOrder), jorder);
+
+            var aliases = Field.GetAliases(jfield);
+            var props = Schema.GetProperties(jfield);
+            var defaultValue = jfield["default"];
+
+            JToken jtype = jfield["type"];
+            if (null == jtype) 
+                throw new SchemaParseException("'type' was not found for field: " + name);
+            var schema = Schema.ParseJson(jtype, names, encspace);
+
+            return new Field(schema, name, aliases, pos, doc, defaultValue, sortorder, props);
+        }
+
+        private static void addToFieldMap(Dictionary<string, Field> map, string name, Field field)
+        {
+            if (map.ContainsKey(name))
+                throw new SchemaParseException("field or alias " + name + " is a duplicate name");
+            map.Add(name, field);
+        }
+
+        /// <summary>
+        /// Returns the field with the given name.
+        /// </summary>
+        /// <param name="name">field name</param>
+        /// <returns>Field object</returns>
+        public Field this[string name]
+        {
+            get
+            {
+                if (string.IsNullOrEmpty(name)) throw new ArgumentNullException("name");
+                Field field;
+                return (fieldLookup.TryGetValue(name, out field)) ? field : null;
+            }
+        }
+
+        /// <summary>
+        /// Returns true if and only if the record contains a field by the given name.
+        /// </summary>
+        /// <param name="fieldName">The name of the field</param>
+        /// <returns>true if the field exists, false otherwise</returns>
+        public bool Contains(string fieldName)
+        {
+            return fieldLookup.ContainsKey(fieldName);
+        }
+
+        public bool TryGetField(string fieldName, out Field field)
+        {
+            return fieldLookup.TryGetValue(fieldName, out field);
+        }
+        public bool TryGetFieldAlias(string fieldName, out Field field)
+        {
+            return fieldAliasLookup.TryGetValue(fieldName, out field);
+        }
+
+        /// <summary>
+        /// Returns an enumerator which enumerates over the fields of this record schema
+        /// </summary>
+        /// <returns>Enumerator over the field in the order of their definition</returns>
+        public IEnumerator<Field> GetEnumerator()
+        {
+            return Fields.GetEnumerator();
+        }
+
+        /// <summary>
+        /// Writes the records schema in JSON format
+        /// </summary>
+        /// <param name="writer">JSON writer</param>
+        /// <param name="names">list of named schemas already written</param>
+        /// <param name="encspace">enclosing namespace of the record schema</param>
+        protected internal override void WriteJsonFields(Newtonsoft.Json.JsonTextWriter writer, SchemaNames names, string encspace)
+        {
+            base.WriteJsonFields(writer, names, encspace);
+
+            // we allow reading for empty fields, so writing of records with empty fields are allowed as well
+            if (request)
+                writer.WritePropertyName("request");
+            else
+                writer.WritePropertyName("fields");
+            writer.WriteStartArray();
+
+            if (null != this.Fields && this.Fields.Count > 0)
+            {
+                foreach (Field field in this)
+                    field.writeJson(writer, names, this.Namespace); // use the namespace of the record for the fields
+            }
+            writer.WriteEndArray();
+        }
+
+        /// <summary>
+        /// Compares equality of two record schemas
+        /// </summary>
+        /// <param name="obj">record schema to compare against this schema</param>
+        /// <returns>true if the two schemas are equal, false otherwise</returns>
+        public override bool Equals(object obj)
+        {
+            if (obj == this) return true;
+            if (obj != null && obj is RecordSchema)
+            {
+                RecordSchema that = obj as RecordSchema;
+                return protect(() => true, () =>
+                {
+                    if (this.SchemaName.Equals(that.SchemaName) && this.Count == that.Count)
+                    {
+                        for (int i = 0; i < Fields.Count; i++) if (!Fields[i].Equals(that.Fields[i])) return false;
+                        return areEqual(that.Props, this.Props);
+                    }
+                    return false;
+                }, that);
+            }
+            return false;
+        }
+
+        /// <summary>
+        /// Hash code function
+        /// </summary>
+        /// <returns></returns>
+        public override int GetHashCode()
+        {
+            return protect(() =>
+            {
+                int result = SchemaName.GetHashCode();
+                foreach (Field f in Fields) result += 29 * f.GetHashCode();
+                result += getHashCode(Props);
+                return result;
+            }, () => 0, this);
+        }
+
+        /// <summary>
+        /// Checks if this schema can read data written by the given schema. Used for decoding data.
+        /// </summary>
+        /// <param name="writerSchema">writer schema</param>
+        /// <returns>true if this and writer schema are compatible based on the AVRO specification, false otherwise</returns>
+        public override bool CanRead(Schema writerSchema)
+        {
+            if (writerSchema.Tag != Type.Record) return false;
+            RecordSchema that = writerSchema as RecordSchema;
+            return protect(() => true, () =>
+            {
+                if (!that.SchemaName.Equals(SchemaName))
+                    if (!InAliases(that.SchemaName)) 
+                        return false;
+
+                foreach (Field f in this)
+                {
+                    Field f2 = that[f.Name];
+                    if (null == f2) // reader field not in writer field, check aliases of reader field if any match with a writer field
+                        if (null != f.aliases)
+                            foreach (string alias in f.aliases)
+                            {
+                                f2 = that[alias];
+                                if (null != f2) break;
+                            }
+
+                    if (f2 == null && f.DefaultValue != null)
+                        continue;         // Writer field missing, reader has default.
+
+                    if (f2 != null && f.Schema.CanRead(f2.Schema)) continue;    // Both fields exist and are compatible.
+                    return false;
+                }
+                return true;
+            }, that);
+        }
+
+        private class RecordSchemaPair
+        {
+            public readonly RecordSchema first;
+            public readonly RecordSchema second;
+
+            public RecordSchemaPair(RecordSchema first, RecordSchema second)
+            {
+                this.first = first;
+                this.second = second;
+            }
+        }
+
+        [ThreadStatic]
+        private static List<RecordSchemaPair> seen;
+
+        /**
+         * We want to protect against infinite recursion when the schema is recursive. We look into a thread local
+         * to see if we have been into this if so, we execute the bypass function otherwise we execute the main function.
+         * Before executing the main function, we ensure that we create a marker so that if we come back here recursively
+         * we can detect it.
+         * 
+         * The infinite loop happens in ToString(), Equals() and GetHashCode() methods.
+         * Though it does not happen for CanRead() because of the current implemenation of UnionSchema's can read,
+         * it could potenitally happen.
+         * We do a linear seach for the marker as we don't expect the list to be very long.
+         */
+        private T protect<T>(Function<T> bypass, Function<T> main, RecordSchema that)
+        {
+            if (seen == null) 
+                seen = new List<RecordSchemaPair>();
+
+            else if (seen.Find((RecordSchemaPair rs) => rs.first == this && rs.second == that) != null) 
+                return bypass();
+
+            RecordSchemaPair p = new RecordSchemaPair(this, that);
+            seen.Add(p);
+            try { return main(); }
+            finally { seen.Remove(p); }
+        }
+
+    }
+}

Added: avro/trunk/lang/csharp/src/apache/main/Schema/Schema.cs
URL: http://svn.apache.org/viewvc/avro/trunk/lang/csharp/src/apache/main/Schema/Schema.cs?rev=1087439&view=auto
==============================================================================
--- avro/trunk/lang/csharp/src/apache/main/Schema/Schema.cs (added)
+++ avro/trunk/lang/csharp/src/apache/main/Schema/Schema.cs Thu Mar 31 21:16:28 2011
@@ -0,0 +1,306 @@
+/**
+ * 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.
+ */
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Newtonsoft.Json.Linq;
+using Newtonsoft.Json;
+
+namespace Avro
+{
+    /// <summary>
+    /// Base class for all schema types
+    /// </summary>
+    public abstract class Schema
+    {
+        /// <summary>
+        /// Enum for schema types
+        /// </summary>
+        public enum Type
+        {
+            Null,
+            Boolean,
+            Int,
+            Long,
+            Float,
+            Double,
+            Bytes,
+            String,
+            Record,
+            Enumeration,
+            Array,
+            Map,
+            Union,
+            Fixed,
+            Error
+        }
+
+        /// <summary>
+        /// Schema type property
+        /// </summary>
+        public Type Tag { get; private set; }
+
+        /// <summary>
+        /// Additional JSON attributes apart from those defined in the AVRO spec
+        /// </summary>
+        internal PropertyMap Props { get; private set; }
+
+        /// <summary>
+        /// Constructor for schema class
+        /// </summary>
+        /// <param name="type"></param>
+        protected Schema(Type type, PropertyMap props)
+        {
+            this.Tag = type;
+            this.Props = props;
+        }
+
+        /// <summary>
+        /// The name of this schema. If this is a named schema such as an enum, it returns the fully qualified
+        /// name for the schema. For other schemas, it returns the type of the schema.
+        /// </summary>
+        public abstract string Name { get; }
+
+        /// <summary>
+        /// Static class to return new instance of schema object
+        /// </summary>
+        /// <param name="jtok">JSON object</param>
+        /// <param name="names">list of named schemas already read</param>
+        /// <param name="encspace">enclosing namespace of the schema</param>
+        /// <returns>new Schema object</returns>
+        internal static Schema ParseJson(JToken jtok, SchemaNames names, string encspace)
+        {
+            if (null == jtok) throw new ArgumentNullException("j", "j cannot be null.");
+            
+            if (jtok.Type == JTokenType.String) // primitive schema with no 'type' property or primitive or named type of a record field
+            {
+                string value = (string)jtok;
+
+                PrimitiveSchema ps = PrimitiveSchema.NewInstance(value);
+                if (null != ps) return ps;
+
+                NamedSchema schema = null;
+                if (names.TryGetValue(value, null, encspace, out schema)) return schema;
+
+                throw new SchemaParseException("Undefined name: " + value);
+            }
+
+            if (jtok is JArray) // union schema with no 'type' property or union type for a record field
+                return UnionSchema.NewInstance(jtok as JArray, null, names, encspace);
+
+            if (jtok is JObject) // JSON object with open/close parenthesis, it must have a 'type' property
+            {
+                JObject jo = jtok as JObject;
+
+                JToken jtype = jo["type"];
+                if (null == jtype)
+                    throw new SchemaParseException("Property type is required");
+
+                var props = Schema.GetProperties(jtok);
+
+                if (jtype.Type == JTokenType.String)
+                {
+                    string type = (string)jtype;
+
+                    if (type.Equals("array")) 
+                        return ArraySchema.NewInstance(jtok, props, names, encspace);
+                    if (type.Equals("map"))
+                        return MapSchema.NewInstance(jtok, props, names, encspace);
+                    
+                    Schema schema = PrimitiveSchema.NewInstance((string)type, props);
+                    if (null != schema) return schema;
+
+                    return NamedSchema.NewInstance(jo, props, names, encspace);
+                }
+                else if (jtype.Type == JTokenType.Array)
+                    return UnionSchema.NewInstance(jtype as JArray, props, names, encspace);
+            }
+            throw new AvroTypeException("Invalid JSON for schema: " + jtok);
+        }
+
+        /// <summary>
+        /// Parses a given JSON string to create a new schema object
+        /// </summary>
+        /// <param name="json">JSON string</param>
+        /// <returns>new Schema object</returns>
+        public static Schema Parse(string json)
+        {
+            if (string.IsNullOrEmpty(json)) throw new ArgumentNullException("json", "json cannot be null.");
+            return Parse(json.Trim(), new SchemaNames(), null); // standalone schema, so no enclosing namespace
+        }
+
+        /// <summary>
+        /// Parses a JSON string to create a new schema object
+        /// </summary>
+        /// <param name="json">JSON string</param>
+        /// <param name="names">list of named schemas already read</param>
+        /// <param name="encspace">enclosing namespace of the schema</param>
+        /// <returns>new Schema object</returns>
+        internal static Schema Parse(string json, SchemaNames names, string encspace)
+        {
+            Schema sc = PrimitiveSchema.NewInstance(json);
+            if (null != sc) return sc;
+
+            try
+            {
+                bool IsArray = json.StartsWith("[") && json.EndsWith("]");
+                JContainer j = IsArray ? (JContainer)JArray.Parse(json) : (JContainer)JObject.Parse(json);
+
+                return ParseJson(j, names, encspace);
+            }
+            catch (Newtonsoft.Json.JsonSerializationException ex)
+            {
+                throw new SchemaParseException("Could not parse. " + ex.Message + Environment.NewLine + json);
+            }
+        }
+
+        /// <summary>
+        /// Static function to parse custom properties (not defined in the Avro spec) from the given JSON object
+        /// </summary>
+        /// <param name="jtok">JSON object to parse</param>
+        /// <returns>Property map if custom properties were found, null if no custom properties found</returns>
+        internal static PropertyMap GetProperties(JToken jtok)
+        {
+            var props = new PropertyMap();
+            props.Parse(jtok);
+            if (props.Count > 0)
+                return props;
+            else
+                return null;
+        }
+
+        /// <summary>
+        /// Returns the canonical JSON representation of this schema.
+        /// </summary>
+        /// <returns>The canonical JSON representation of this schema.</returns>
+        public override string ToString()
+        {
+            System.IO.StringWriter sw = new System.IO.StringWriter();
+            Newtonsoft.Json.JsonTextWriter writer = new Newtonsoft.Json.JsonTextWriter(sw);
+
+            if (this is PrimitiveSchema || this is UnionSchema)
+            {
+                writer.WriteStartObject();
+                writer.WritePropertyName("type");
+            }
+
+            WriteJson(writer, new SchemaNames(), null); // stand alone schema, so no enclosing name space
+
+            if (this is PrimitiveSchema || this is UnionSchema)
+                writer.WriteEndObject();
+
+            return sw.ToString();
+        }
+
+        /// <summary>
+        /// Writes opening { and 'type' property 
+        /// </summary>
+        /// <param name="writer">JSON writer</param>
+        private void writeStartObject(JsonTextWriter writer)
+        {
+            writer.WriteStartObject();
+            writer.WritePropertyName("type");
+            writer.WriteValue(getTypeString(this.Tag));
+        }
+
+        /// <summary>
+        /// Returns symbol name for the given schema type
+        /// </summary>
+        /// <param name="type">schema type</param>
+        /// <returns>symbol name</returns>
+        protected static string getTypeString(Type type)
+        {
+            if (type != Type.Enumeration) return type.ToString().ToLower();
+            return "enum";
+        }
+
+        /// <summary>
+        /// Default implementation for writing schema properties in JSON format
+        /// </summary>
+        /// <param name="writer">JSON writer</param>
+        /// <param name="names">list of named schemas already written</param>
+        /// <param name="encspace">enclosing namespace of the schema</param>
+        protected internal virtual void WriteJsonFields(JsonTextWriter writer, SchemaNames names, string encspace)
+        {
+        }
+
+        /// <summary>
+        /// Writes schema object in JSON format
+        /// </summary>
+        /// <param name="writer">JSON writer</param>
+        /// <param name="names">list of named schemas already written</param>
+        /// <param name="encspace">enclosing namespace of the schema</param>
+        protected internal virtual void WriteJson(JsonTextWriter writer, SchemaNames names, string encspace)
+        {
+            writeStartObject(writer);
+            WriteJsonFields(writer, names, encspace);
+            if (null != this.Props) Props.WriteJson(writer);
+            writer.WriteEndObject();
+        }
+
+        /// <summary>
+        /// Returns the schema's custom property value given the property name
+        /// </summary>
+        /// <param name="key">custom property name</param>
+        /// <returns>custom property value</returns>
+        public string GetProperty(string key)
+        {
+            if (null == this.Props) return null;
+            string v;
+            return (this.Props.TryGetValue(key, out v)) ? v : null;
+        }
+
+        /// <summary>
+        /// Hash code function
+        /// </summary>
+        /// <returns></returns>
+        public override int GetHashCode()
+        {
+            return Tag.GetHashCode() + getHashCode(Props);
+        }
+
+        /// <summary>
+        /// Returns true if and only if data written using writerSchema can be read using the current schema
+        /// according to the Avro resolution rules.
+        /// </summary>
+        /// <param name="writerSchema">The writer's schema to match against.</param>
+        /// <returns>True if and only if the current schema matches the writer's.</returns>
+        public virtual bool CanRead(Schema writerSchema) { return Tag == writerSchema.Tag; }
+
+        /// <summary>
+        /// Compares two objects, null is equal to null
+        /// </summary>
+        /// <param name="o1">first object</param>
+        /// <param name="o2">second object</param>
+        /// <returns>true if two objects are equal, false otherwise</returns>
+        protected static bool areEqual(object o1, object o2)
+        {
+            return o1 == null ? o2 == null : o1.Equals(o2);
+        }
+
+        /// <summary>
+        /// Hash code helper function
+        /// </summary>
+        /// <param name="obj"></param>
+        /// <returns></returns>
+        protected static int getHashCode(object obj)
+        {
+            return obj == null ? 0 : obj.GetHashCode();
+        }
+    }
+}

Added: avro/trunk/lang/csharp/src/apache/main/Schema/SchemaName.cs
URL: http://svn.apache.org/viewvc/avro/trunk/lang/csharp/src/apache/main/Schema/SchemaName.cs?rev=1087439&view=auto
==============================================================================
--- avro/trunk/lang/csharp/src/apache/main/Schema/SchemaName.cs (added)
+++ avro/trunk/lang/csharp/src/apache/main/Schema/SchemaName.cs Thu Mar 31 21:16:28 2011
@@ -0,0 +1,222 @@
+/**
+ * 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.
+ */
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Avro
+{
+    /// <summary>
+    /// Class to store schema name, namespace and enclosing namespace
+    /// </summary>
+    public class SchemaName
+    {
+        /// <summary>
+        /// Name of the schema
+        /// </summary>
+        public String Name { get; private set; }
+
+        /// <summary>
+        /// Namespace specified within the schema
+        /// </summary>
+        public String Space { get; private set; }
+
+        /// <summary>
+        /// Namespace from the most tightly enclosing schema
+        /// </summary>
+        public String EncSpace { get; private set; }
+
+        /// <summary>
+        /// Namespace.Name of the schema
+        /// </summary>
+        public String Fullname { get { return string.IsNullOrEmpty(Namespace) ? this.Name : Namespace + "." + this.Name; } }
+
+        /// <summary>
+        /// Namespace of the schema
+        /// </summary>
+        public String Namespace { get { return string.IsNullOrEmpty(this.Space) ? this.EncSpace : this.Space; } } 
+
+        /// <summary>
+        /// Constructor for SchemaName
+        /// </summary>
+        /// <param name="name">name of the schema</param>
+        /// <param name="space">namespace of the schema</param>
+        /// <param name="encspace">enclosing namespace of the schema</param>
+        public SchemaName(String name, String space, String encspace)
+        {
+            if (name == null)
+            {                         // anonymous
+                this.Name = this.Space = null;
+                this.EncSpace = encspace;   // need to save enclosing namespace for anonymous types, so named types within the anonymous type can be resolved
+            }
+            else if (!name.Contains("."))
+            {                          // unqualified name
+                this.Space = space;    // use default space
+                this.Name = name;
+                this.EncSpace = encspace;
+            }
+            else
+            {
+                string[] parts = name.Split('.');
+                this.Space = string.Join(".", parts, 0, parts.Length - 1);
+                this.Name = parts[parts.Length - 1];
+                this.EncSpace = encspace;
+            }
+        }
+
+        /// <summary>
+        /// Returns the full name of the schema
+        /// </summary>
+        /// <returns></returns>
+        public override string ToString()
+        {
+            return Fullname;
+        }
+
+        /// <summary>
+        /// Writes the schema name in JSON format
+        /// </summary>
+        /// <param name="writer">JSON writer</param>
+        /// <param name="names">list of named schemas already written</param>
+        /// <param name="encspace">enclosing namespace of the schema</param>
+        internal void WriteJson(Newtonsoft.Json.JsonTextWriter writer, SchemaNames names, string encspace)
+        {
+            if (null != this.Name)  // write only if not anonymous
+            {
+                JsonHelper.writeIfNotNullOrEmpty(writer, "name", this.Name);
+                if (!String.IsNullOrEmpty(this.Space))
+                    JsonHelper.writeIfNotNullOrEmpty(writer, "namespace", this.Space);
+                else if (!String.IsNullOrEmpty(this.EncSpace)) // need to put enclosing name space for code generated classes
+                    JsonHelper.writeIfNotNullOrEmpty(writer, "namespace", this.EncSpace);
+            }
+        }
+
+        /// <summary>
+        /// Compares two schema names
+        /// </summary>
+        /// <param name="obj">SchameName object to compare against this object</param>
+        /// <returns>true or false</returns>
+        public override bool Equals(Object obj)
+        {
+            if (obj == this) return true;
+            if (obj != null && obj is SchemaName)
+            {
+                SchemaName that = (SchemaName)obj;
+                return areEqual(that.Name, Name) && areEqual(that.Namespace, Namespace);
+            }
+            return false;
+        }
+
+        /// <summary>
+        /// Compares two objects
+        /// </summary>
+        /// <param name="obj1">first object</param>
+        /// <param name="obj2">second object</param>
+        /// <returns>true or false</returns>
+        private static bool areEqual(object obj1, object obj2)
+        {
+            return obj1 == null ? obj2 == null : obj1.Equals(obj2);
+        }
+    
+        public override int GetHashCode()
+        {
+            return string.IsNullOrEmpty(Fullname) ? 0 : 29 * Fullname.GetHashCode();
+        }
+    }
+
+    /// <summary>
+    /// A class that contains a list of named schemas. This is used when reading or writing a schema/protocol.
+    /// This prevents reading and writing of duplicate schema definitions within a protocol or schema file
+    /// </summary>
+    public class SchemaNames
+    {
+        /// <summary>
+        /// Map of schema name and named schema objects
+        /// </summary>
+        public IDictionary<SchemaName, NamedSchema> Names { get; private set; }
+
+        /// <summary>
+        /// Constructor
+        /// </summary>
+        public SchemaNames()
+        {
+            Names = new Dictionary<SchemaName, NamedSchema>();
+        }
+
+        /// <summary>
+        /// Checks if given name is in the map
+        /// </summary>
+        /// <param name="name">schema name</param>
+        /// <returns>true or false</returns>
+        public bool Contains(SchemaName name)
+        {
+            if (Names.ContainsKey(name))
+                return true;
+            return false;
+        }
+
+        /// <summary>
+        /// Adds a schema name to the map if it doesn't exist yet
+        /// </summary>
+        /// <param name="name">schema name</param>
+        /// <param name="schema">schema object</param>
+        /// <returns>true if schema was added to the list, false if schema is already in the list</returns>
+        public bool Add(SchemaName name, NamedSchema schema)
+        {
+            if (Names.ContainsKey(name))
+                return false;
+
+            Names.Add(name, schema);
+            return true;
+        }
+
+        /// <summary>
+        /// Adds a named schema to the list
+        /// </summary>
+        /// <param name="schema">schema object</param>
+        /// <returns>true if schema was added to the list, false if schema is already in the list</returns>
+        public bool Add(NamedSchema schema)
+        {
+            SchemaName name = schema.SchemaName;
+            return Add(name, schema);
+        }
+
+        /// <summary>
+        /// Tries to get the value for the given name fields
+        /// </summary>
+        /// <param name="name">name of the schema</param>
+        /// <param name="space">namespace of the schema</param>
+        /// <param name="encspace">enclosing namespace of the schema</param>
+        /// <param name="schema">schema object found</param>
+        /// <returns>true if name is found in the map, false otherwise</returns>
+        public bool TryGetValue(string name, string space, string encspace, out NamedSchema schema)
+        {
+            SchemaName schemaname = new SchemaName(name, space, encspace);
+            return Names.TryGetValue(schemaname, out schema);
+        }
+
+        /// <summary>
+        /// Returns the enumerator for the map
+        /// </summary>
+        /// <returns></returns>
+        public IEnumerator<KeyValuePair<SchemaName, NamedSchema>> GetEnumerator()
+        {
+            return Names.GetEnumerator();
+        }
+    }
+}

Added: avro/trunk/lang/csharp/src/apache/main/Schema/SchemaParseException.cs
URL: http://svn.apache.org/viewvc/avro/trunk/lang/csharp/src/apache/main/Schema/SchemaParseException.cs?rev=1087439&view=auto
==============================================================================
--- avro/trunk/lang/csharp/src/apache/main/Schema/SchemaParseException.cs (added)
+++ avro/trunk/lang/csharp/src/apache/main/Schema/SchemaParseException.cs Thu Mar 31 21:16:28 2011
@@ -0,0 +1,31 @@
+/**
+ * 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.
+ */
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Avro
+{
+    public class SchemaParseException:AvroException
+    {
+        public SchemaParseException(string s)
+            : base(s)
+        {
+        }
+    }
+}

Added: avro/trunk/lang/csharp/src/apache/main/Schema/UnionSchema.cs
URL: http://svn.apache.org/viewvc/avro/trunk/lang/csharp/src/apache/main/Schema/UnionSchema.cs?rev=1087439&view=auto
==============================================================================
--- avro/trunk/lang/csharp/src/apache/main/Schema/UnionSchema.cs (added)
+++ avro/trunk/lang/csharp/src/apache/main/Schema/UnionSchema.cs Thu Mar 31 21:16:28 2011
@@ -0,0 +1,164 @@
+/**
+ * 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.
+ */
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Newtonsoft.Json.Linq;
+using Newtonsoft.Json;
+
+namespace Avro
+{
+    /// <summary>
+    /// Class for union schemas
+    /// </summary>
+    public class UnionSchema : UnnamedSchema
+    {
+        /// <summary>
+        /// List of schemas in the union
+        /// </summary>
+        public IList<Schema> Schemas { get; private set; }
+
+        /// <summary>
+        /// Count of schemas in the union
+        /// </summary>
+        public int Count { get { return Schemas.Count; } }
+
+        /// <summary>
+        /// Static function to return instance of the union schema
+        /// </summary>
+        /// <param name="jarr">JSON object for the union schema</param>
+        /// <param name="names">list of named schemas already read</param>
+        /// <param name="encspace">enclosing namespace of the schema</param>
+        /// <returns>new UnionSchema object</returns>
+        internal static UnionSchema NewInstance(JArray jarr, PropertyMap props, SchemaNames names, string encspace)
+        {
+            List<Schema> schemas = new List<Schema>();
+            IDictionary<string, string> uniqueSchemas = new Dictionary<string, string>();
+
+            foreach (JToken jvalue in jarr)
+            {
+                Schema unionType = Schema.ParseJson(jvalue, names, encspace);
+                if (null == unionType)
+                    throw new SchemaParseException("Invalid JSON in union" + jvalue.ToString());
+
+                string name = unionType.Name;
+                if (uniqueSchemas.ContainsKey(name))
+                    throw new SchemaParseException("Duplicate type in union: " + name);
+
+                uniqueSchemas.Add(name, name);
+                schemas.Add(unionType);
+            }
+
+            return new UnionSchema(schemas, props);
+        }
+
+        /// <summary>
+        /// Contructor for union schema
+        /// </summary>
+        /// <param name="schemas"></param>
+        private UnionSchema(List<Schema> schemas, PropertyMap props) : base(Type.Union, props)
+        {
+            if (schemas.Count == 0)
+                throw new ArgumentNullException("schemas");
+            this.Schemas = schemas;
+        }
+
+        /// <summary>
+        /// Returns the schema at the given branch.
+        /// </summary>
+        /// <param name="index">Index to the branch, starting with 0.</param>
+        /// <returns>The branch corresponding to the given index.</returns>
+        public Schema this[int index]
+        {
+            get
+            {
+                return Schemas[index];
+            }
+        }
+
+        /// <summary>
+        /// Writes union schema in JSON format
+        /// </summary>
+        /// <param name="writer">JSON writer</param>
+        /// <param name="names">list of named schemas already written</param>
+        /// <param name="encspace">enclosing namespace of the schema</param>
+        protected internal override void WriteJson(Newtonsoft.Json.JsonTextWriter writer, SchemaNames names, string encspace)
+        {
+            writer.WriteStartArray();
+            foreach (Schema schema in this.Schemas)
+                schema.WriteJson(writer, names, encspace);
+            writer.WriteEndArray();
+        }
+
+        /// <summary>
+        /// Returns the index of a branch that can read the data written by the given schema s.
+        /// </summary>
+        /// <param name="s">The schema to match the branches against.</param>
+        /// <returns>The index of the matching branch. If non matches a -1 is returned.</returns>
+        public int MatchingBranch(Schema s)
+        {
+            if (s is UnionSchema) throw new AvroException("Cannot find a match against union schema");
+            // Try exact match.
+            //for (int i = 0; i < Count; i++) if (Schemas[i].Equals(s)) return i; // removed this for performance's sake
+            for (int i = 0; i < Count; i++) if (Schemas[i].CanRead(s)) return i;
+            return -1;
+        }
+
+        /// <summary>
+        /// Checks if this schema can read data written by the given schema. Used for decoding data.
+        /// </summary>
+        /// <param name="writerSchema">writer schema</param>
+        /// <returns>true if this and writer schema are compatible based on the AVRO specification, false otherwise</returns>
+        public override bool CanRead(Schema writerSchema)
+        {
+            return writerSchema.Tag == Schema.Type.Union || MatchingBranch(writerSchema) >= 0;
+        }
+
+        /// <summary>
+        /// Compares two union schema objects
+        /// </summary>
+        /// <param name="obj">union schema object to compare against this schema</param>
+        /// <returns>true if objects are equal, false otherwise</returns>
+        public override bool Equals(object obj)
+        {
+            if (obj == this) return true;
+            if (obj != null && obj is UnionSchema)
+            {
+                UnionSchema that = obj as UnionSchema;
+                if (that.Count == Count)
+                {
+                    for (int i = 0; i < Count; i++) if (!that[i].Equals(this[i])) return false;
+                    return areEqual(that.Props, this.Props);
+                }
+            }
+            return false;
+        }
+
+        /// <summary>
+        /// Hash code function
+        /// </summary>
+        /// <returns></returns>
+        public override int GetHashCode()
+        {
+            int result = 53;
+            foreach (Schema schema in Schemas) result += 89 * schema.GetHashCode();
+            result += getHashCode(Props);
+            return result;
+        }
+    }
+}

Added: avro/trunk/lang/csharp/src/apache/main/Schema/UnnamedSchema.cs
URL: http://svn.apache.org/viewvc/avro/trunk/lang/csharp/src/apache/main/Schema/UnnamedSchema.cs?rev=1087439&view=auto
==============================================================================
--- avro/trunk/lang/csharp/src/apache/main/Schema/UnnamedSchema.cs (added)
+++ avro/trunk/lang/csharp/src/apache/main/Schema/UnnamedSchema.cs Thu Mar 31 21:16:28 2011
@@ -0,0 +1,39 @@
+/**
+ * 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.
+ */
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Avro
+{
+    /// <summary>
+    /// Base class for all unnamed schemas
+    /// </summary>
+    public abstract class UnnamedSchema : Schema
+    {
+        protected UnnamedSchema(Type type, PropertyMap props) : base(type, props)
+        {
+        }
+
+        public override string Name
+        {
+            get { return Tag.ToString().ToLower(); }
+        }
+    }
+}

Added: avro/trunk/lang/csharp/src/apache/main/Specific/ObjectCreator.cs
URL: http://svn.apache.org/viewvc/avro/trunk/lang/csharp/src/apache/main/Specific/ObjectCreator.cs?rev=1087439&view=auto
==============================================================================
--- avro/trunk/lang/csharp/src/apache/main/Specific/ObjectCreator.cs (added)
+++ avro/trunk/lang/csharp/src/apache/main/Specific/ObjectCreator.cs Thu Mar 31 21:16:28 2011
@@ -0,0 +1,165 @@
+/**
+ * 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.
+ */
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Reflection;
+using System.Reflection.Emit;
+
+namespace Avro.Specific
+{
+
+    public sealed class ObjectCreator
+    {
+        private static readonly ObjectCreator instance = new ObjectCreator();
+        public static ObjectCreator Instance { get { return instance; } }
+
+        /// <summary>
+        /// Static generic dictionary type used for creating new dictionary instances 
+        /// </summary>
+        private Type GenericMapType = typeof(Dictionary<,>);
+
+        /// <summary>
+        /// Static generic list type used for creating new array instances
+        /// </summary>
+        private Type GenericListType = typeof(List<>);
+
+        private readonly Assembly execAssembly;
+        private readonly Assembly entryAssembly;
+        private readonly bool diffAssembly;
+        private readonly Type[] margs;
+        private readonly Type[] largs;
+
+        private delegate object CtorDelegate();
+        private Type ctorType = typeof(CtorDelegate);
+        Dictionary<NameCtorKey, CtorDelegate> ctors;
+
+        private ObjectCreator()
+        {
+            execAssembly = System.Reflection.Assembly.GetExecutingAssembly();
+            entryAssembly = System.Reflection.Assembly.GetEntryAssembly();
+            if (entryAssembly != null && execAssembly != entryAssembly) // entryAssembly returns null when running from NUnit
+                diffAssembly = true;
+
+            GenericMapType = typeof(Dictionary<,>);
+            GenericListType = typeof(List<>);
+            margs = new Type[2] { typeof(string), null };
+            largs = new Type[1] { null };
+
+            ctors = new Dictionary<NameCtorKey, CtorDelegate>();
+        }
+
+        public struct NameCtorKey : IEquatable<NameCtorKey>
+        {
+            public string name { get; private set; }
+            public Schema.Type type { get; private set; }
+            public NameCtorKey(string value1, Schema.Type value2)
+                : this()
+            {
+                name = value1;
+                type = value2;
+            }
+            public bool Equals(NameCtorKey other)
+            {
+                return Equals(other.name, name) && other.type == type;
+            }
+            public override bool Equals(object obj)
+            {
+                if (ReferenceEquals(null, obj))
+                    return false;
+                if (obj.GetType() != typeof(NameCtorKey))
+                    return false;
+                return Equals((NameCtorKey)obj);
+            }
+            public override int GetHashCode()
+            {
+                unchecked
+                {
+                    return ((name != null ? name.GetHashCode() : 0) * 397) ^ type.GetHashCode();
+                }
+            }
+            public static bool operator ==(NameCtorKey left, NameCtorKey right)
+            {
+                return left.Equals(right);
+            }
+            public static bool operator !=(NameCtorKey left, NameCtorKey right)
+            {
+                return !left.Equals(right);
+            }
+        }
+
+        /// <summary>
+        /// Creates new instance of the given type
+        /// </summary>
+        /// <param name="name">fully qualified name of the type</param>
+        /// <param name="schemaType">type of schema</param>
+        /// <returns>new object of the given type</returns>
+        public object New(string name, Schema.Type schemaType)
+        {
+            NameCtorKey key = new NameCtorKey(name, schemaType);
+            CtorDelegate ctor;
+            if (!ctors.TryGetValue(key, out ctor))
+            {
+                Type type;
+                if (diffAssembly)
+                {
+                    // entry assembly different from current assembly, try entry assembly first
+                    type = entryAssembly.GetType(name);
+                    if (type == null)   // now try current assembly and mscorlib
+                        type = Type.GetType(name);
+                }
+                else
+                    type = Type.GetType(name);
+
+                if (type == null) // type is still not found, need to loop through all loaded assemblies
+                {
+                    Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
+                    foreach (Assembly assembly in assemblies)
+                    {
+                        type = assembly.GetType(name);
+                        if (type != null)
+                            break;
+                    }
+                }
+                if (type == null)
+                    throw new AvroException("Unable to find type " + name + " in all loaded assemblies");
+
+                if (schemaType == Schema.Type.Map)
+                {
+                    margs[1] = type;
+                    type = GenericMapType.MakeGenericType(margs);
+                }
+                else if (schemaType == Schema.Type.Array)
+                {
+                    largs[0] = type;
+                    type = GenericListType.MakeGenericType(largs);
+                }
+
+                DynamicMethod dynMethod = new DynamicMethod("DM$OBJ_FACTORY_" + name, typeof(object), null, type, true); 
+                ILGenerator ilGen = dynMethod.GetILGenerator(); 
+                ilGen.Emit(OpCodes.Nop);
+                ilGen.Emit(OpCodes.Newobj, type.GetConstructor(Type.EmptyTypes));
+                ilGen.Emit(OpCodes.Ret);
+
+                ctor = (CtorDelegate)dynMethod.CreateDelegate(ctorType);
+                ctors.Add(key, ctor);
+            }
+            return ctor();
+        }
+    }
+}

Added: avro/trunk/lang/csharp/src/apache/main/Specific/SpecificFixed.cs
URL: http://svn.apache.org/viewvc/avro/trunk/lang/csharp/src/apache/main/Specific/SpecificFixed.cs?rev=1087439&view=auto
==============================================================================
--- avro/trunk/lang/csharp/src/apache/main/Specific/SpecificFixed.cs (added)
+++ avro/trunk/lang/csharp/src/apache/main/Specific/SpecificFixed.cs Thu Mar 31 21:16:28 2011
@@ -0,0 +1,34 @@
+/**
+ * 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.
+ */
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Avro.Generic;
+
+namespace Avro.Specific
+{
+    /// <summary>
+    /// Base class for all generated classes 
+    /// </summary>
+    public abstract class SpecificFixed : GenericFixed
+    {
+        public SpecificFixed(uint size) : base(size) { }
+        public abstract new Schema Schema { get; }
+    }
+}

Added: avro/trunk/lang/csharp/src/apache/main/Specific/SpecificReader.cs
URL: http://svn.apache.org/viewvc/avro/trunk/lang/csharp/src/apache/main/Specific/SpecificReader.cs?rev=1087439&view=auto
==============================================================================
--- avro/trunk/lang/csharp/src/apache/main/Specific/SpecificReader.cs (added)
+++ avro/trunk/lang/csharp/src/apache/main/Specific/SpecificReader.cs Thu Mar 31 21:16:28 2011
@@ -0,0 +1,272 @@
+/**
+ * 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.
+ */
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.IO;
+using Avro;
+using Avro.IO;
+using Avro.Generic;
+
+namespace Avro.Specific
+{
+    /// <summary>
+    /// Reader wrapper class for reading data and storing into specific classes
+    /// </summary>
+    /// <typeparam name="T">Specific class type</typeparam>
+    public class SpecificReader<T> : DatumReader<T>
+    {
+        /// <summary>
+        /// Reader class for reading data and storing into specific classes
+        /// </summary>
+        private readonly SpecificDefaultReader reader;
+
+        /// <summary>
+        /// Constructs a generic reader for the given schemas using the DefaultReader. If the
+        /// reader's and writer's schemas are different this class performs the resolution.
+        /// </summary>
+        /// <param name="writerSchema">The schema used while generating the data</param>
+        /// <param name="readerSchema">The schema desired by the reader</param>
+        public SpecificReader(Schema writerSchema, Schema readerSchema)
+        {
+            reader = new SpecificDefaultReader(writerSchema, readerSchema);
+        }
+
+        /// <summary>
+        /// Schema for the writer class
+        /// </summary>
+        public Schema WriterSchema { get { return reader.WriterSchema; } }
+
+        /// <summary>
+        /// Schema for the reader class
+        /// </summary>
+        public Schema ReaderSchema { get { return reader.ReaderSchema; } }
+
+        /// <summary>
+        /// Generic read function
+        /// </summary>
+        /// <param name="reuse">object to store data read</param>
+        /// <param name="dec">decorder to use for reading data</param>
+        /// <returns></returns>
+        public T Read(T reuse, Decoder dec)
+        {
+            return reader.Read(reuse, dec);
+        }
+    }
+
+    /// <summary>
+    /// Reader class for reading data and storing into specific classes
+    /// </summary>
+    public class SpecificDefaultReader : DefaultReader
+    {
+        /// <summary>
+        /// Static dictionary of type names and its corresponding assembly type. 
+        /// This is used to prevent multiple reflection for the same type name.
+        /// </summary>
+        private static IDictionary<string, Type> TypeName = new Dictionary<string, Type>();
+
+        /// <summary>
+        /// Constructor
+        /// </summary>
+        /// <param name="writerSchema">schema of the object that wrote the data</param>
+        /// <param name="readerSchema">schema of the object that will store the data</param>
+        public SpecificDefaultReader(Schema writerSchema, Schema readerSchema) : base(writerSchema,readerSchema) 
+        {
+        }
+
+        /// <summary>
+        /// Deserializes a record from the stream.
+        /// </summary>
+        /// <param name="reuse">If not null, a record object that could be reused for returning the result</param>
+        /// <param name="writerSchema">The writer's RecordSchema</param>
+        /// <param name="readerSchema">The reader's schema, must be RecordSchema too.</param>
+        /// <param name="dec">The decoder for deserialization</param>
+        /// <returns>The record object just read</returns>
+        protected override object ReadRecord(object reuse, RecordSchema writerSchema, Schema readerSchema, Decoder dec)
+        {
+            RecordSchema rs = (RecordSchema)readerSchema;
+
+            SpecificRecord rec = (reuse != null ? reuse : ObjectCreator.Instance.New(rs.Fullname, Schema.Type.Record)) as SpecificRecord;
+            object obj;
+            foreach (Field wf in writerSchema)
+            {
+                try
+                {
+                    Field rf;
+                    if (rs.TryGetField(wf.Name, out rf))
+                    {
+                        obj = rec.Get(rf.Pos);
+                        rec.Put(rf.Pos, Read(obj, wf.Schema, rf.Schema, dec));
+                    }
+                    else
+                        Skip(wf.Schema, dec);
+                }
+                catch (Exception ex)
+                {
+                    throw new AvroException(ex.Message + " in field " + wf.Name);
+                }
+            }
+            
+            var defaultStream = new MemoryStream();
+            var defaultEncoder = new BinaryEncoder(defaultStream);
+            var defaultDecoder = new BinaryDecoder(defaultStream);
+            foreach (Field rf in rs)
+            {
+                if (writerSchema.Contains(rf.Name)) continue;
+
+                defaultStream.Position = 0; // reset for writing
+                Resolver.EncodeDefaultValue(defaultEncoder, rf.Schema, rf.DefaultValue);
+                defaultStream.Flush();
+                defaultStream.Position = 0; // reset for reading
+
+                obj = rec.Get(rf.Pos);
+                rec.Put(rf.Pos, Read(obj, rf.Schema, rf.Schema, defaultDecoder));
+            }
+            
+            return rec;
+        }
+
+        /// <summary>
+        /// Deserializes a fixed object and returns the object. The default implementation uses CreateFixed()
+        /// and GetFixedBuffer() and returns what CreateFixed() returned.
+        /// </summary>
+        /// <param name="reuse">If appropriate, uses this object instead of creating a new one.</param>
+        /// <param name="writerSchema">The FixedSchema the writer used during serialization.</param>
+        /// <param name="readerSchema">The schema that the readr uses. Must be a FixedSchema with the same
+        /// size as the writerSchema.</param>
+        /// <param name="d">The decoder for deserialization.</param>
+        /// <returns>The deserilized object.</returns>
+        protected override object ReadFixed(object reuse, FixedSchema writerSchema, Schema readerSchema, Decoder d)
+        {
+            FixedSchema rs = readerSchema as FixedSchema;
+            if (rs.Size != writerSchema.Size)
+            {
+                throw new AvroException("Size mismatch between reader and writer fixed schemas. Writer: " + writerSchema +
+                    ", reader: " + readerSchema);
+            }
+
+            SpecificFixed fixedrec = (reuse != null ? reuse : ObjectCreator.Instance.New(rs.Fullname, Schema.Type.Fixed)) as SpecificFixed;
+            d.ReadFixed(fixedrec.Value);
+            return fixedrec;
+        }
+
+        /// <summary>
+        /// Reads an enum from the given decoder
+        /// </summary>
+        /// <param name="reuse">object to store data read</param>
+        /// <param name="writerSchema">schema of the object that wrote the data</param>
+        /// <param name="readerSchema">schema of the object that will store the data</param>
+        /// <param name="dec">decoder object that contains the data to be read</param>
+        /// <returns>enum value</returns>
+        protected override object ReadEnum(object reuse, EnumSchema writerSchema, Schema readerSchema, Decoder dec)
+        {
+            return dec.ReadEnum();
+        }
+
+        /// <summary>
+        /// Reads an array from the given decoder
+        /// </summary>
+        /// <param name="reuse">object to store data read</param>
+        /// <param name="writerSchema">schema of the object that wrote the data</param>
+        /// <param name="readerSchema">schema of the object that will store the data</param>
+        /// <param name="dec">decoder object that contains the data to be read</param>
+        /// <returns>array</returns>
+        protected override object ReadArray(object reuse, ArraySchema writerSchema, Schema readerSchema, Decoder dec)
+        {
+            ArraySchema rs = readerSchema as ArraySchema;
+            System.Collections.IList array;
+            if (reuse != null)
+            {
+                array = reuse as System.Collections.IList;
+                if (array == null)
+                    throw new AvroException("array object does not implement non-generic IList");
+
+                array.Clear();
+            }
+            else
+                array = ObjectCreator.Instance.New(getTargetType(readerSchema), Schema.Type.Array) as System.Collections.IList;
+            
+            int i = 0;
+            for (int n = (int)dec.ReadArrayStart(); n != 0; n = (int)dec.ReadArrayNext())
+            {
+                for (int j = 0; j < n; j++, i++)
+                    array.Add(Read(null, writerSchema.ItemSchema, rs.ItemSchema, dec));
+            }
+            return array;
+        }
+
+        /// <summary>
+        /// Deserialized an avro map. The default implemenation creats a new map using CreateMap() and then
+        /// adds elements to the map using AddMapEntry().
+        /// </summary>
+        /// <param name="reuse">If appropriate, use this instead of creating a new map object.</param>
+        /// <param name="writerSchema">The schema the writer used to write the map.</param>
+        /// <param name="readerSchema">The schema the reader is using.</param>
+        /// <param name="d">The decoder for serialization.</param>
+        /// <returns>The deserialized map object.</returns>
+        protected override object ReadMap(object reuse, MapSchema writerSchema, Schema readerSchema, Decoder d)
+        {
+            MapSchema rs = readerSchema as MapSchema;
+            System.Collections.IDictionary map;
+            if (reuse != null)
+            {
+                map = reuse as System.Collections.IDictionary;
+                if (map == null)
+                    throw new AvroException("map object does not implement non-generic IList");
+
+                map.Clear();
+            }
+            else
+                map = ObjectCreator.Instance.New(getTargetType(readerSchema), Schema.Type.Map) as System.Collections.IDictionary;
+
+            for (int n = (int)d.ReadMapStart(); n != 0; n = (int)d.ReadMapNext())
+            {
+                for (int j = 0; j < n; j++)
+                {
+                    string k = d.ReadString();
+                    map[k] = Read(null, writerSchema.ValueSchema, rs.ValueSchema, d);   // always create new map item
+                }
+            }
+            return map;
+        }
+
+        /// <summary>
+        /// Gets the target type name in the given schema
+        /// </summary>
+        /// <param name="schema">schema containing the type to be determined</param>
+        /// <param name="nullible">used for union schema</param>
+        /// <returns></returns>
+        private string getTargetType(Schema schema)
+        {
+            bool nEnum = false;
+            string type = Avro.CodeGen.getType(schema, false, ref nEnum);
+            if (schema.Tag == Schema.Type.Array)
+            {
+                type = type.Remove(0, 6);              // remove IList<
+                type = type.Remove(type.Length - 1);   // remove >
+            }
+            else if (schema.Tag == Schema.Type.Map)
+            {
+                type = type.Remove(0, 19);             // remove IDictionary<string,
+                type = type.Remove(type.Length - 1);   // remove >
+            }
+            return type;
+        }
+    }
+}

Added: avro/trunk/lang/csharp/src/apache/main/Specific/SpecificRecord.cs
URL: http://svn.apache.org/viewvc/avro/trunk/lang/csharp/src/apache/main/Specific/SpecificRecord.cs?rev=1087439&view=auto
==============================================================================
--- avro/trunk/lang/csharp/src/apache/main/Specific/SpecificRecord.cs (added)
+++ avro/trunk/lang/csharp/src/apache/main/Specific/SpecificRecord.cs Thu Mar 31 21:16:28 2011
@@ -0,0 +1,34 @@
+/**
+ * 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.
+ */
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Avro.Specific
+{
+    /// <summary>
+    /// Base class for generated classes
+    /// </summary>
+    public abstract class SpecificRecord
+    {
+        public abstract Schema Schema { get; }
+        public abstract object Get(int fieldPos);
+        public abstract void Put(int fieldPos, object fieldValue);
+    }
+}