| /** |
| * 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 fields defined in a record |
| /// </summary> |
| public class Field |
| { |
| /// <summary> |
| /// Enum for the sorting order of record fields |
| /// </summary> |
| public enum SortOrder |
| { |
| ascending, |
| descending, |
| ignore |
| } |
| |
| /// <summary> |
| /// Name of the field. |
| /// </summary> |
| public readonly string Name; |
| |
| /// <summary> |
| /// List of aliases for the field name |
| /// </summary> |
| public readonly IList<string> aliases; |
| |
| /// <summary> |
| /// Position of the field within its record. |
| /// </summary> |
| public int Pos { get; private set; } |
| |
| /// <summary> |
| /// Documentation for the field, if any. Null if there is no documentation. |
| /// </summary> |
| public string Documentation { get; private set; } |
| |
| /// <summary> |
| /// The default value for the field stored as JSON object, if defined. Otherwise, null. |
| /// </summary> |
| public JToken DefaultValue { get; private set; } |
| |
| /// <summary> |
| /// Order of the field |
| /// </summary> |
| public SortOrder? Ordering { get; private set; } |
| |
| /// <summary> |
| /// Field type's schema |
| /// </summary> |
| public Schema Schema { get; private set; } |
| |
| /// <summary> |
| /// Custom properties for the field. We don't store the fields custom properties in |
| /// the field type's schema because if the field type is only a reference to the schema |
| /// instead of an actual schema definition, then the schema could already have it's own set |
| /// of custom properties when it was previously defined. |
| /// </summary> |
| private readonly PropertyMap Props; |
| |
| /// <summary> |
| /// Static comparer object for JSON objects such as the fields default value |
| /// </summary> |
| internal static JTokenEqualityComparer JtokenEqual = new JTokenEqualityComparer(); |
| |
| /// <summary> |
| /// A flag to indicate if reader schema has a field that is missing from writer schema and has a default value |
| /// This is set in CanRead() which is always be called before deserializing data |
| /// </summary> |
| |
| /// <summary> |
| /// Constructor for the field class |
| /// </summary> |
| /// <param name="schema">schema for the field type</param> |
| /// <param name="name">name of the field</param> |
| /// <param name="aliases">list of aliases for the name of the field</param> |
| /// <param name="pos">position of the field</param> |
| /// <param name="doc">documentation for the field</param> |
| /// <param name="defaultValue">field's default value if it exists</param> |
| /// <param name="sortorder">sort order of the field</param> |
| internal Field(Schema schema, string name, IList<string> aliases, int pos, string doc, |
| JToken defaultValue, SortOrder sortorder, PropertyMap props) |
| { |
| if (string.IsNullOrEmpty(name)) throw new ArgumentNullException("name", "name cannot be null."); |
| if (null == schema) throw new ArgumentNullException("type", "type cannot be null."); |
| this.Schema = schema; |
| this.Name = name; |
| this.aliases = aliases; |
| this.Pos = pos; |
| this.Documentation = doc; |
| this.DefaultValue = defaultValue; |
| this.Ordering = sortorder; |
| this.Props = props; |
| } |
| |
| /// <summary> |
| /// Writes the Field class 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 for the field</param> |
| protected internal void writeJson(JsonTextWriter writer, SchemaNames names, string encspace) |
| { |
| writer.WriteStartObject(); |
| JsonHelper.writeIfNotNullOrEmpty(writer, "name", this.Name); |
| JsonHelper.writeIfNotNullOrEmpty(writer, "doc", this.Documentation); |
| |
| if (null != this.DefaultValue) |
| { |
| writer.WritePropertyName("default"); |
| this.DefaultValue.WriteTo(writer, null); |
| } |
| if (null != this.Schema) |
| { |
| writer.WritePropertyName("type"); |
| Schema.WriteJson(writer, names, encspace); |
| } |
| |
| if (null != this.Props) |
| this.Props.WriteJson(writer); |
| |
| if (null != aliases) |
| { |
| writer.WritePropertyName("aliases"); |
| writer.WriteStartArray(); |
| foreach (string name in aliases) |
| writer.WriteValue(name); |
| writer.WriteEndArray(); |
| } |
| |
| writer.WriteEndObject(); |
| } |
| |
| /// <summary> |
| /// Parses the 'aliases' property from the given JSON token |
| /// </summary> |
| /// <param name="jtok">JSON object to read</param> |
| /// <returns>List of string that represents the list of alias. If no 'aliases' specified, then it returns null.</returns> |
| internal static IList<string> GetAliases(JToken jtok) |
| { |
| 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<string>(); |
| foreach (JToken jalias in jaliases) |
| { |
| if (jalias.Type != JTokenType.String) |
| throw new SchemaParseException("Aliases must be of format JSON array of strings"); |
| |
| aliases.Add((string)jalias); |
| } |
| return aliases; |
| } |
| |
| /// <summary> |
| /// Returns the field'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> |
| /// Compares two field objects |
| /// </summary> |
| /// <param name="obj">field to compare with this field</param> |
| /// <returns>true if two fields are equal, false otherwise</returns> |
| public override bool Equals(object obj) |
| { |
| if (obj == this) return true; |
| if (obj != null && obj is Field) |
| { |
| Field that = obj as Field; |
| return areEqual(that.Name, Name) && that.Pos == Pos && areEqual(that.Documentation, Documentation) |
| && areEqual(that.Ordering, Ordering) && JtokenEqual.Equals(that.DefaultValue, DefaultValue) |
| && that.Schema.Equals(Schema) && areEqual(that.Props, this.Props); |
| } |
| return false; |
| } |
| |
| /// <summary> |
| /// Compares two objects |
| /// </summary> |
| /// <param name="o1">first object</param> |
| /// <param name="o2">second object</param> |
| /// <returns>true if two objects are equal, false otherwise</returns> |
| private static bool areEqual(object o1, object o2) |
| { |
| return o1 == null ? o2 == null : o1.Equals(o2); |
| } |
| |
| /// <summary> |
| /// Hash code function |
| /// </summary> |
| /// <returns></returns> |
| public override int GetHashCode() |
| { |
| return 17 * Name.GetHashCode() + Pos + 19 * getHashCode(Documentation) + |
| 23 * getHashCode(Ordering) + 29 * getHashCode(DefaultValue) + 31 * Schema.GetHashCode() + |
| 37 * getHashCode(Props); |
| } |
| |
| /// <summary> |
| /// Hash code helper function |
| /// </summary> |
| /// <param name="obj"></param> |
| /// <returns></returns> |
| private static int getHashCode(object obj) |
| { |
| return obj == null ? 0 : obj.GetHashCode(); |
| } |
| } |
| } |