/** * 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; using Newtonsoft.Json.Linq; namespace Avro { public class Protocol { /// /// Name of the protocol /// public string Name { get; set; } /// /// Namespace of the protocol /// public string Namespace { get; set; } /// /// Documentation for the protocol /// public string Doc { get; set; } /// /// List of schemas objects representing the different schemas defined under the 'types' attribute /// public IList Types { get; set; } /// /// List of message objects representing the different schemas defined under the 'messages' attribute /// public IDictionary Messages { get; set; } private byte[] md5; public byte[] MD5 { get { try { if (md5 == null) md5 = System.Security.Cryptography.MD5.Create().ComputeHash(Encoding.UTF8.GetBytes(ToString())); } catch (Exception ex) { throw new AvroRuntimeException("MD5 get exception", ex); } return md5; } } /// /// Constructor for Protocol class /// /// required name of protocol /// optional namespace /// optional documentation /// required list of types /// required list of messages public Protocol(string name, string space, string doc, IEnumerable types, IDictionary messages) { if (string.IsNullOrEmpty(name)) throw new ArgumentNullException("name", "name cannot be null."); if (null == types) throw new ArgumentNullException("types", "types cannot be null."); if (null == messages) throw new ArgumentNullException("messages", "messages cannot be null."); this.Name = name; this.Namespace = space; this.Doc = doc; this.Types = new List(types); this.Messages = new Dictionary(messages); } /// /// Parses the given JSON string to create a Protocol object /// /// JSON string /// Protocol object public static Protocol Parse(string jstring) { if (string.IsNullOrEmpty(jstring)) throw new ArgumentNullException("json", "json cannot be null."); JToken jtok = null; try { jtok = JObject.Parse(jstring); } catch (Exception ex) { throw new ProtocolParseException("Invalid JSON format: " + jstring, ex); } return Parse(jtok); } /// /// Parses the given JSON object to create a Protocol object /// /// JSON object /// Protocol object private static Protocol Parse(JToken jtok) { string name = JsonHelper.GetRequiredString(jtok, "protocol"); string space = JsonHelper.GetOptionalString(jtok, "namespace"); string doc = JsonHelper.GetOptionalString(jtok, "doc"); var names = new SchemaNames(); JToken jtypes = jtok["types"]; var types = new List(); if (jtypes is JArray) { foreach (JToken jtype in jtypes) { var schema = Schema.ParseJson(jtype, names, space); types.Add(schema); } } var messages = new Dictionary(); JToken jmessages = jtok["messages"]; if (null != jmessages) { foreach (JProperty jmessage in jmessages) { var message = Message.Parse(jmessage, names, space); messages.Add(message.Name, message); } } return new Protocol(name, space, doc, types, messages); } /// /// Writes Protocol in JSON format /// /// JSON string public override string ToString() { using (System.IO.StringWriter sw = new System.IO.StringWriter()) { using (Newtonsoft.Json.JsonTextWriter writer = new Newtonsoft.Json.JsonTextWriter(sw)) { #if(DEBUG) writer.Formatting = Newtonsoft.Json.Formatting.Indented; #endif WriteJson(writer, new SchemaNames()); writer.Flush(); return sw.ToString(); } } } /// /// Writes Protocol in JSON format /// /// JSON writer /// list of named schemas already written internal void WriteJson(Newtonsoft.Json.JsonTextWriter writer, SchemaNames names) { writer.WriteStartObject(); JsonHelper.writeIfNotNullOrEmpty(writer, "protocol", this.Name); JsonHelper.writeIfNotNullOrEmpty(writer, "namespace", this.Namespace); JsonHelper.writeIfNotNullOrEmpty(writer, "doc", this.Doc); writer.WritePropertyName("types"); writer.WriteStartArray(); foreach (Schema type in this.Types) type.WriteJson(writer, names, this.Namespace); writer.WriteEndArray(); writer.WritePropertyName("messages"); writer.WriteStartObject(); foreach (KeyValuePair message in this.Messages) { writer.WritePropertyName(message.Key); message.Value.writeJson(writer, names, this.Namespace); } writer.WriteEndObject(); writer.WriteEndObject(); } /// /// Tests equality of this protocol object with the passed object /// /// /// public override bool Equals(object obj) { if (obj == this) return true; if (!(obj is Protocol)) return false; Protocol that = obj as Protocol; return this.Name.Equals(that.Name) && this.Namespace.Equals(that.Namespace) && TypesEquals(that.Types) && MessagesEquals(that.Messages); } /// /// Test equality of this protocols Types list with the passed Types list. /// Order of schemas does not matter, as long as all types in this protocol /// are also defined in the passed protocol /// /// /// private bool TypesEquals(IList that) { if (Types.Count != that.Count) return false; foreach (Schema schema in Types) if (!that.Contains(schema)) return false; return true; } /// /// Test equality of this protocols Message map with the passed Message map /// Order of messages does not matter, as long as all messages in this protocol /// are also defined in the passed protocol /// /// /// private bool MessagesEquals(IDictionary that) { if (Messages.Count != that.Count) return false; foreach (KeyValuePair pair in Messages) { if (!that.ContainsKey(pair.Key)) return false; if (!pair.Value.Equals(that[pair.Key])) return false; } return true; } /// /// Returns the hash code of this protocol object /// /// public override int GetHashCode() { return Name.GetHashCode() + Namespace.GetHashCode() + GetTypesHashCode() + GetMessagesHashCode(); } /// /// Returns the hash code of the Types list /// /// private int GetTypesHashCode() { int hash = Types.Count; foreach (Schema schema in Types) hash += schema.GetHashCode(); return hash; } /// /// Returns the hash code of the Messages map /// /// private int GetMessagesHashCode() { int hash = Messages.Count; foreach (KeyValuePair pair in Messages) hash += (pair.Key.GetHashCode() + pair.Value.GetHashCode()); return hash; } } }