Kinetica   C#   API  Version 7.2.3.0
RecordSchema.cs
Go to the documentation of this file.
1 
18 using System;
19 using System.Collections.Generic;
20 using System.Text;
21 using Newtonsoft.Json.Linq;
22 using Newtonsoft.Json;
23 
24 namespace Avro
25 {
26  internal delegate T Function<T>();
27 
31  public class RecordSchema : NamedSchema
32  {
36  public List<Field> Fields { get; private set; }
37 
41  public int Count { get { return Fields.Count; } }
42 
46  private readonly IDictionary<string, Field> fieldLookup;
47 
48  private readonly IDictionary<string, Field> fieldAliasLookup;
49  private bool request;
50 
59  internal static RecordSchema NewInstance(Type type, JToken jtok, PropertyMap props, SchemaNames names, string encspace)
60  {
61  bool request = false;
62  JToken jfields = jtok["fields"]; // normal record
63  if (null == jfields)
64  {
65  jfields = jtok["request"]; // anonymous record from messages
66  if (null != jfields) request = true;
67  }
68  if (null == jfields)
69  throw new SchemaParseException("'fields' cannot be null for record");
70  if (jfields.Type != JTokenType.Array)
71  throw new SchemaParseException("'fields' not an array for record");
72 
73  var name = GetName(jtok, encspace);
74  var aliases = NamedSchema.GetAliases(jtok, name.Space, name.EncSpace);
75  var fields = new List<Field>();
76  var fieldMap = new Dictionary<string, Field>();
77  var fieldAliasMap = new Dictionary<string, Field>();
78  var result = new RecordSchema(type, name, aliases, props, fields, request, fieldMap, fieldAliasMap, names);
79 
80  int fieldPos = 0;
81  foreach (JObject jfield in jfields)
82  {
83  string fieldName = JsonHelper.GetRequiredString(jfield, "name");
84  Field field = createField(jfield, fieldPos++, names, name.Namespace); // add record namespace for field look up
85  fields.Add(field);
86  addToFieldMap(fieldMap, fieldName, field);
87  addToFieldMap(fieldAliasMap, fieldName, field);
88 
89  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
90  foreach (string alias in field.aliases)
91  addToFieldMap(fieldAliasMap, alias, field);
92  }
93  return result;
94  }
95 
106  private RecordSchema(Type type, SchemaName name, IList<SchemaName> aliases, PropertyMap props,
107  List<Field> fields, bool request, IDictionary<string, Field> fieldMap,
108  IDictionary<string, Field> fieldAliasMap, SchemaNames names)
109  : base(type, name, aliases, props, names)
110  {
111  if (!request && null == name.Name) throw new SchemaParseException("name cannot be null for record schema.");
112  this.Fields = fields;
113  this.request = request;
114  this.fieldLookup = fieldMap;
115  this.fieldAliasLookup = fieldAliasMap;
116  }
117 
126  private static Field createField(JToken jfield, int pos, SchemaNames names, string encspace)
127  {
128  var name = JsonHelper.GetRequiredString(jfield, "name");
129  var doc = JsonHelper.GetOptionalString(jfield, "doc");
130 
131  var jorder = JsonHelper.GetOptionalString(jfield, "order");
132  Field.SortOrder sortorder = Field.SortOrder.ignore;
133  if (null != jorder)
134  sortorder = (Field.SortOrder) Enum.Parse(typeof(Field.SortOrder), jorder);
135 
136  var aliases = Field.GetAliases(jfield);
137  var props = Schema.GetProperties(jfield);
138  var defaultValue = jfield["default"];
139 
140  JToken jtype = jfield["type"];
141  if (null == jtype)
142  throw new SchemaParseException("'type' was not found for field: " + name);
143  var schema = Schema.ParseJson(jtype, names, encspace);
144 
145  return new Field(schema, name, aliases, pos, doc, defaultValue, sortorder, props);
146  }
147 
148  private static void addToFieldMap(Dictionary<string, Field> map, string name, Field field)
149  {
150  if (map.ContainsKey(name))
151  throw new SchemaParseException("field or alias " + name + " is a duplicate name");
152  map.Add(name, field);
153  }
154 
160  public Field this[string name]
161  {
162  get
163  {
164  if (string.IsNullOrEmpty(name)) throw new ArgumentNullException("name");
165  Field field;
166  return (fieldLookup.TryGetValue(name, out field)) ? field : null;
167  }
168  }
169 
175  public bool Contains(string fieldName)
176  {
177  return fieldLookup.ContainsKey(fieldName);
178  }
179 
180  public bool TryGetField(string fieldName, out Field field)
181  {
182  return fieldLookup.TryGetValue(fieldName, out field);
183  }
184  public bool TryGetFieldAlias(string fieldName, out Field field)
185  {
186  return fieldAliasLookup.TryGetValue(fieldName, out field);
187  }
188 
193  public IEnumerator<Field> GetEnumerator()
194  {
195  return Fields.GetEnumerator();
196  }
197 
204  protected internal override void WriteJsonFields(Newtonsoft.Json.JsonTextWriter writer, SchemaNames names, string encspace)
205  {
206  base.WriteJsonFields(writer, names, encspace);
207 
208  // we allow reading for empty fields, so writing of records with empty fields are allowed as well
209  if (request)
210  writer.WritePropertyName("request");
211  else
212  writer.WritePropertyName("fields");
213  writer.WriteStartArray();
214 
215  if (null != this.Fields && this.Fields.Count > 0)
216  {
217  foreach (Field field in this)
218  field.writeJson(writer, names, this.Namespace); // use the namespace of the record for the fields
219  }
220  writer.WriteEndArray();
221  }
222 
228  public override bool Equals(object obj)
229  {
230  if (obj == this) return true;
231  if (obj != null && obj is RecordSchema)
232  {
233  RecordSchema that = obj as RecordSchema;
234  return protect(() => true, () =>
235  {
236  if (this.SchemaName.Equals(that.SchemaName) && this.Count == that.Count)
237  {
238  for (int i = 0; i < Fields.Count; i++) if (!Fields[i].Equals(that.Fields[i])) return false;
239  return areEqual(that.Props, this.Props);
240  }
241  return false;
242  }, that);
243  }
244  return false;
245  }
246 
251  public override int GetHashCode()
252  {
253  return protect(() => 0, () =>
254  {
255  int result = SchemaName.GetHashCode();
256  foreach (Field f in Fields) result += 29 * f.GetHashCode();
257  result += getHashCode(Props);
258  return result;
259  }, this);
260  }
261 
267  public override bool CanRead(Schema writerSchema)
268  {
269  if ((writerSchema.Tag != Type.Record) && (writerSchema.Tag != Type.Error)) return false;
270 
271  RecordSchema that = writerSchema as RecordSchema;
272  return protect(() => true, () =>
273  {
274  if (!that.SchemaName.Equals(SchemaName))
275  if (!InAliases(that.SchemaName))
276  return false;
277 
278  foreach (Field f in this)
279  {
280  Field f2 = that[f.Name];
281  if (null == f2) // reader field not in writer field, check aliases of reader field if any match with a writer field
282  if (null != f.aliases)
283  foreach (string alias in f.aliases)
284  {
285  f2 = that[alias];
286  if (null != f2) break;
287  }
288 
289  if (f2 == null && f.DefaultValue != null)
290  continue; // Writer field missing, reader has default.
291 
292  if (f2 != null && f.Schema.CanRead(f2.Schema)) continue; // Both fields exist and are compatible.
293  return false;
294  }
295  return true;
296  }, that);
297  }
298 
299  private class RecordSchemaPair
300  {
301  public readonly RecordSchema first;
302  public readonly RecordSchema second;
303 
304  public RecordSchemaPair(RecordSchema first, RecordSchema second)
305  {
306  this.first = first;
307  this.second = second;
308  }
309  }
310 
311  [ThreadStatic]
312  private static List<RecordSchemaPair> seen;
313 
325  private T protect<T>(Function<T> bypass, Function<T> main, RecordSchema that)
326  {
327  if (seen == null)
328  seen = new List<RecordSchemaPair>();
329 
330  else if (seen.Find((RecordSchemaPair rs) => rs.first == this && rs.second == that) != null)
331  return bypass();
332 
333  RecordSchemaPair p = new RecordSchemaPair(this, that);
334  seen.Add(p);
335  try { return main(); }
336  finally { seen.Remove(p); }
337  }
338 
339  }
340 }
static SchemaName GetName(JToken jtok, string encspace)
Parses the name and namespace from the given JSON schema object then creates SchemaName object includ...
Definition: NamedSchema.cs:116
override bool Equals(object obj)
Compares equality of two record schemas
Class for record schemas
Definition: RecordSchema.cs:31
internal override void WriteJsonFields(Newtonsoft.Json.JsonTextWriter writer, SchemaNames names, string encspace)
Writes the records schema in JSON format
static int getHashCode(object obj)
Hash code helper function
Definition: Schema.cs:301
Schema Schema
Field type's schema
Definition: Field.cs:75
Class for fields defined in a record
Definition: Field.cs:30
bool InAliases(SchemaName name)
Definition: NamedSchema.cs:150
Base class for all schema types
Definition: Schema.cs:29
IEnumerator< Field > GetEnumerator()
Returns an enumerator which enumerates over the fields of this record schema
readonly string Name
Name of the field.
Definition: Field.cs:45
virtual bool CanRead(Schema writerSchema)
Returns true if and only if data written using writerSchema can be read using the current schema acco...
Definition: Schema.cs:283
NamedSchema(Type type, SchemaName name, IList< SchemaName > aliases, PropertyMap props, SchemaNames names)
Constructor for named schema class
Definition: NamedSchema.cs:99
Type
Enum for schema types
Definition: Schema.cs:34
Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.
readonly IList< string > aliases
List of aliases for the field name
Definition: Field.cs:50
internal delegate T Function< T >()
Class to store schema name, namespace and enclosing namespace
Definition: SchemaName.cs:27
override int GetHashCode()
Hash code function
Definition: Field.cs:228
override int GetHashCode()
Hash code function
bool TryGetField(string fieldName, out Field field)
string Namespace
Namespace of the schema
Definition: NamedSchema.cs:48
A class that contains a list of named schemas.
Definition: SchemaName.cs:146
override int GetHashCode()
Definition: SchemaName.cs:136
int Count
Number of fields in the record
Definition: RecordSchema.cs:41
override bool CanRead(Schema writerSchema)
Checks if this schema can read data written by the given schema.
JToken DefaultValue
The default value for the field stored as JSON object, if defined.
Definition: Field.cs:65
override bool Equals(Object obj)
Compares two schema names
Definition: SchemaName.cs:114
Schema(Type type, PropertyMap props)
Constructor for schema class
Definition: Schema.cs:67
static bool areEqual(object o1, object o2)
Compares two objects, null is equal to null
Definition: Schema.cs:291
internal void writeJson(JsonTextWriter writer, SchemaNames names, string encspace)
Writes the Field class in JSON format
Definition: Field.cs:126
Type Tag
Schema type property
Definition: Schema.cs:56
List< Field > Fields
List of fields in the record
Definition: RecordSchema.cs:36
Base class for all named schemas: fixed, enum, record
Definition: NamedSchema.cs:29
bool TryGetFieldAlias(string fieldName, out Field field)
bool Contains(string fieldName)
Returns true if and only if the record contains a field by the given name.
SchemaName SchemaName
Name of the schema, contains name, namespace and enclosing namespace
Definition: NamedSchema.cs:34