/* * 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 * * https://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 Newtonsoft.Json.Linq; using Newtonsoft.Json; namespace Avro { /// /// Class for fields defined in a record /// public class Field { /// /// Enum for the sorting order of record fields /// public enum SortOrder { /// /// Ascending order. /// ascending, /// /// Descending order. /// descending, /// /// Ignore sort order. /// ignore } /// /// Name of the field. /// public readonly string Name; /// /// List of aliases for the field name /// [Obsolete("Use Aliases instead. This will be removed from the public API in a future version.")] public readonly IList aliases; #pragma warning disable CS0618 // Type or member is obsolete /// /// List of aliases for the field name. /// public IList Aliases => aliases; #pragma warning restore CS0618 // Type or member is obsolete /// /// Position of the field within its record. /// public int Pos { get; private set; } /// /// Documentation for the field, if any. Null if there is no documentation. /// public string Documentation { get; private set; } /// /// The default value for the field stored as JSON object, if defined. Otherwise, null. /// public JToken DefaultValue { get; private set; } /// /// Order of the field /// public SortOrder? Ordering { get; private set; } /// /// Field type's schema /// public Schema Schema { get; private set; } /// /// 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. /// private readonly PropertyMap Props; /// /// Static comparer object for JSON objects such as the fields default value /// internal static JTokenEqualityComparer JtokenEqual = new JTokenEqualityComparer(); /// /// 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 /// /// /// Constructor for the field class /// /// schema for the field type /// name of the field /// list of aliases for the name of the field /// position of the field /// documentation for the field /// field's default value if it exists /// sort order of the field /// dictionary that provides access to custom properties internal Field(Schema schema, string name, IList aliases, int pos, string doc, JToken defaultValue, SortOrder sortorder, PropertyMap props) { if (string.IsNullOrEmpty(name)) throw new ArgumentNullException(nameof(name), "name cannot be null."); if (null == schema) throw new ArgumentNullException("type", "type cannot be null."); this.Schema = schema; this.Name = name; #pragma warning disable CS0618 // Type or member is obsolete this.aliases = aliases; #pragma warning restore CS0618 // Type or member is obsolete this.Pos = pos; this.Documentation = doc; this.DefaultValue = defaultValue; this.Ordering = sortorder; this.Props = props; } /// /// Writes the Field class in JSON format /// /// JSON writer /// list of named schemas already written /// enclosing namespace for the field 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(); } /// /// Parses the 'aliases' property from the given JSON token /// /// JSON object to read /// List of string that represents the list of alias. If no 'aliases' specified, then it returns null. internal static IList 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 at '{jtok.Path}'"); var aliases = new List(); foreach (JToken jalias in jaliases) { if (jalias.Type != JTokenType.String) throw new SchemaParseException($"Aliases must be of format JSON array of strings at '{jtok.Path}'"); aliases.Add((string)jalias); } return aliases; } /// /// Returns the field's custom property value given the property name /// /// custom property name /// custom property value public string GetProperty(string key) { if (null == this.Props) return null; string v; return this.Props.TryGetValue(key, out v) ? v : null; } /// /// Compares two field objects /// /// field to compare with this field /// true if two fields are equal, false otherwise 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; } /// /// Compares two objects /// /// first object /// second object /// true if two objects are equal, false otherwise private static bool areEqual(object o1, object o2) { return o1 == null ? o2 == null : o1.Equals(o2); } /// /// Hash code function /// /// public override int GetHashCode() { #pragma warning disable CA1307 // Specify StringComparison return 17 * Name.GetHashCode() + Pos + 19 * getHashCode(Documentation) + #pragma warning restore CA1307 // Specify StringComparison 23 * getHashCode(Ordering) + 29 * getHashCode(DefaultValue) + 31 * Schema.GetHashCode() + 37 * getHashCode(Props); } /// /// Hash code helper function /// /// /// private static int getHashCode(object obj) { return obj == null ? 0 : obj.GetHashCode(); } } }