477 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C#
		
	
	
		
		
			
		
	
	
			477 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C#
		
	
	
| 
								 | 
							
								using Google.Protobuf.Reflection;
							 | 
						|||
| 
								 | 
							
								using System;
							 | 
						|||
| 
								 | 
							
								using System.Collections.Generic;
							 | 
						|||
| 
								 | 
							
								using System.IO;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								namespace ProtoBuf.Reflection
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
								    /// <summary>
							 | 
						|||
| 
								 | 
							
								    /// Abstract root for a general purpose code-generator
							 | 
						|||
| 
								 | 
							
								    /// </summary>
							 | 
						|||
| 
								 | 
							
								    public abstract class CodeGenerator
							 | 
						|||
| 
								 | 
							
								    {
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// The logical name of this code generator
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        public abstract string Name { get; }
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Get a string representation of the instance
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        public override string ToString() => Name;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Execute the code generator against a FileDescriptorSet, yielding a sequence of files
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        public abstract IEnumerable<CodeFile> Generate(FileDescriptorSet set, NameNormalizer normalizer = null);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Eexecute this code generator against a code file
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        public CompilerResult Compile(CodeFile file) => Compile(new[] { file });
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Eexecute this code generator against a set of code file
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        public CompilerResult Compile(params CodeFile[] files)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            var set = new FileDescriptorSet();
							 | 
						|||
| 
								 | 
							
								            foreach (var file in files)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                using (var reader = new StringReader(file.Text))
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    Console.WriteLine($"Parsing {file.Name}...");
							 | 
						|||
| 
								 | 
							
								                    set.Add(file.Name, true, reader);
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            set.Process();
							 | 
						|||
| 
								 | 
							
								            var results = new List<CodeFile>();
							 | 
						|||
| 
								 | 
							
								            var newErrors = new List<Error>();
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            try
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                results.AddRange(Generate(set));
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            catch (Exception ex)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                set.Errors.Add(new Error(default(Token), ex.Message, true));
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            var errors = set.GetErrors();
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            return new CompilerResult(errors, results.ToArray());
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								    /// <summary>
							 | 
						|||
| 
								 | 
							
								    /// Abstract base class for a code generator that uses a visitor pattern
							 | 
						|||
| 
								 | 
							
								    /// </summary>
							 | 
						|||
| 
								 | 
							
								    public abstract partial class CommonCodeGenerator : CodeGenerator
							 | 
						|||
| 
								 | 
							
								    {
							 | 
						|||
| 
								 | 
							
								        private Access? GetAccess(IType parent)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            if (parent is DescriptorProto)
							 | 
						|||
| 
								 | 
							
								                return GetAccess((DescriptorProto)parent);
							 | 
						|||
| 
								 | 
							
								            if (parent is EnumDescriptorProto)
							 | 
						|||
| 
								 | 
							
								                return GetAccess((EnumDescriptorProto)parent);
							 | 
						|||
| 
								 | 
							
								            if (parent is FileDescriptorProto)
							 | 
						|||
| 
								 | 
							
								                return GetAccess((FileDescriptorProto)parent);
							 | 
						|||
| 
								 | 
							
								            return null;
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Obtain the access of an item, accounting for the model's hierarchy
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        protected Access GetAccess(FileDescriptorProto obj)
							 | 
						|||
| 
								 | 
							
								            => obj?.Options?.GetOptions()?.Access ?? Access.Public;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        static Access? NullIfInherit(Access? access)
							 | 
						|||
| 
								 | 
							
								            => access == Access.Inherit ? null : access;
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Obtain the access of an item, accounting for the model's hierarchy
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        protected Access GetAccess(DescriptorProto obj)
							 | 
						|||
| 
								 | 
							
								            => NullIfInherit(obj?.Options?.GetOptions()?.Access)
							 | 
						|||
| 
								 | 
							
								            ?? GetAccess(obj?.Parent) ?? Access.Public;
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Obtain the access of an item, accounting for the model's hierarchy
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        protected Access GetAccess(FieldDescriptorProto obj)
							 | 
						|||
| 
								 | 
							
								            => NullIfInherit(obj?.Options?.GetOptions()?.Access)
							 | 
						|||
| 
								 | 
							
								            ?? GetAccess(obj?.Parent as IType) ?? Access.Public;
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Obtain the access of an item, accounting for the model's hierarchy
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        protected Access GetAccess(EnumDescriptorProto obj)
							 | 
						|||
| 
								 | 
							
								            => NullIfInherit(obj?.Options?.GetOptions()?.Access)
							 | 
						|||
| 
								 | 
							
								                ?? GetAccess(obj?.Parent) ?? Access.Public;
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Get the textual name of a given access level
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        public virtual string GetAccess(Access access)
							 | 
						|||
| 
								 | 
							
								            => access.ToString();
							 | 
						|||
| 
								 | 
							
								        
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// The indentation used by this code generator
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        public virtual string Indent => "    ";
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// The file extension of the files generatred by this generator
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        protected abstract string DefaultFileExtension { get; }
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Handle keyword escaping in the language of this code generator
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        /// <param name="identifier"></param>
							 | 
						|||
| 
								 | 
							
								        /// <returns></returns>
							 | 
						|||
| 
								 | 
							
								        protected abstract string Escape(string identifier);
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Execute the code generator against a FileDescriptorSet, yielding a sequence of files
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        public override IEnumerable<CodeFile> Generate(FileDescriptorSet set, NameNormalizer normalizer = null)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            foreach (var file in set.Files)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                if (!file.IncludeInOutput) continue;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                var fileName = Path.ChangeExtension(file.Name, DefaultFileExtension);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                string generated;
							 | 
						|||
| 
								 | 
							
								                using (var buffer = new StringWriter())
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    var ctx = new GeneratorContext(file, normalizer ?? NameNormalizer.Default, buffer, Indent);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                    ctx.BuildTypeIndex(); // populates for TryFind<T>
							 | 
						|||
| 
								 | 
							
								                    WriteFile(ctx, file);
							 | 
						|||
| 
								 | 
							
								                    generated = buffer.ToString();
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								                yield return new CodeFile(fileName, generated);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Emits the code for a file in a descriptor-set
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        protected virtual void WriteFile(GeneratorContext ctx, FileDescriptorProto obj)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            var file = ctx.File;
							 | 
						|||
| 
								 | 
							
								            object state = null;
							 | 
						|||
| 
								 | 
							
								            WriteFileHeader(ctx, obj, ref state);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            foreach (var inner in file.MessageTypes)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                WriteMessage(ctx, inner);
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            foreach (var inner in file.EnumTypes)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                WriteEnum(ctx, inner);
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            foreach (var inner in file.Services)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                WriteService(ctx, inner);
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            if(file.Extensions.Count != 0)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                object extState = null;
							 | 
						|||
| 
								 | 
							
								                WriteExtensionsHeader(ctx, file, ref extState);
							 | 
						|||
| 
								 | 
							
								                foreach(var ext in file.Extensions)
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    WriteExtension(ctx, ext);
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								                WriteExtensionsFooter(ctx, file, ref extState);
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            WriteFileFooter(ctx, obj, ref state);
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Emit code representing an extension field
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        protected virtual void WriteExtension(GeneratorContext ctx, FieldDescriptorProto ext) { }
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Emit code preceeding a set of extension fields
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        protected virtual void WriteExtensionsHeader(GeneratorContext ctx, FileDescriptorProto file, ref object state) { }
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Emit code following a set of extension fields
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        protected virtual void WriteExtensionsFooter(GeneratorContext ctx, FileDescriptorProto file, ref object state) { }
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Emit code preceeding a set of extension fields
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        protected virtual void WriteExtensionsHeader(GeneratorContext ctx, DescriptorProto file, ref object state) { }
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Emit code following a set of extension fields
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        protected virtual void WriteExtensionsFooter(GeneratorContext ctx, DescriptorProto file, ref object state) { }
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Emit code representing a service
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        protected virtual void WriteService(GeneratorContext ctx, ServiceDescriptorProto obj)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            object state = null;
							 | 
						|||
| 
								 | 
							
								            WriteServiceHeader(ctx, obj, ref state);
							 | 
						|||
| 
								 | 
							
								            foreach (var inner in obj.Methods)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                WriteServiceMethod(ctx, inner, ref state);
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            WriteServiceFooter(ctx, obj, ref state);
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Emit code following a set of service methods
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        protected virtual void WriteServiceFooter(GeneratorContext ctx, ServiceDescriptorProto obj, ref object state) { }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Emit code representing a service method
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        protected virtual void WriteServiceMethod(GeneratorContext ctx, MethodDescriptorProto inner, ref object state) { }
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Emit code following preceeding a set of service methods
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        protected virtual void WriteServiceHeader(GeneratorContext ctx, ServiceDescriptorProto obj, ref object state) { }
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Check whether a particular message should be suppressed - for example because it represents a map
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        protected virtual bool ShouldOmitMessage(GeneratorContext ctx, DescriptorProto obj, ref object state)
							 | 
						|||
| 
								 | 
							
								            => obj.Options?.MapEntry ?? false; // don't write this type - use a dictionary instead
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Emit code representing a message type
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        protected virtual void WriteMessage(GeneratorContext ctx, DescriptorProto obj)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            object state = null;
							 | 
						|||
| 
								 | 
							
								            if (ShouldOmitMessage(ctx, obj, ref state)) return;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            WriteMessageHeader(ctx, obj, ref state);
							 | 
						|||
| 
								 | 
							
								            var oneOfs = OneOfStub.Build(ctx, obj);
							 | 
						|||
| 
								 | 
							
								            foreach (var inner in obj.Fields)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                WriteField(ctx, inner, ref state, oneOfs);
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            foreach (var inner in obj.NestedTypes)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                WriteMessage(ctx, inner);
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            foreach (var inner in obj.EnumTypes)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                WriteEnum(ctx, inner);
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            if (obj.Extensions.Count != 0)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                object extState = null;
							 | 
						|||
| 
								 | 
							
								                WriteExtensionsHeader(ctx, obj, ref extState);
							 | 
						|||
| 
								 | 
							
								                foreach (var ext in obj.Extensions)
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    WriteExtension(ctx, ext);
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								                WriteExtensionsFooter(ctx, obj, ref extState);
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            WriteMessageFooter(ctx, obj, ref state);
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Emit code representing a message field
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        protected abstract void WriteField(GeneratorContext ctx, FieldDescriptorProto obj, ref object state, OneOfStub[] oneOfs);
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Emit code following a set of message fields
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        protected abstract void WriteMessageFooter(GeneratorContext ctx, DescriptorProto obj, ref object state);
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Emit code preceeding a set of message fields
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        protected abstract void WriteMessageHeader(GeneratorContext ctx, DescriptorProto obj, ref object state);
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Emit code representing an enum type
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        protected virtual void WriteEnum(GeneratorContext ctx, EnumDescriptorProto obj)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            object state = null;
							 | 
						|||
| 
								 | 
							
								            WriteEnumHeader(ctx, obj, ref state);
							 | 
						|||
| 
								 | 
							
								            foreach (var inner in obj.Values)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                WriteEnumValue(ctx, inner, ref state);
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            WriteEnumFooter(ctx, obj, ref state);
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Emit code preceeding a set of enum values
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        protected abstract void WriteEnumHeader(GeneratorContext ctx, EnumDescriptorProto obj, ref object state);
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Emit code representing an enum value
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        protected abstract void WriteEnumValue(GeneratorContext ctx, EnumValueDescriptorProto obj, ref object state);
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Emit code following a set of enum values
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        protected abstract void WriteEnumFooter(GeneratorContext ctx, EnumDescriptorProto obj, ref object state);
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Emit code at the start of a file
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        protected virtual void WriteFileHeader(GeneratorContext ctx, FileDescriptorProto obj, ref object state) { }
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Emit code at the end of a file
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        protected virtual void WriteFileFooter(GeneratorContext ctx, FileDescriptorProto obj, ref object state) { }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Represents the state of a code-generation invocation
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        protected class GeneratorContext
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            /// <summary>
							 | 
						|||
| 
								 | 
							
								            /// The file being processed
							 | 
						|||
| 
								 | 
							
								            /// </summary>
							 | 
						|||
| 
								 | 
							
								            public FileDescriptorProto File { get; }
							 | 
						|||
| 
								 | 
							
								            /// <summary>
							 | 
						|||
| 
								 | 
							
								            /// The token to use for indentation
							 | 
						|||
| 
								 | 
							
								            /// </summary>
							 | 
						|||
| 
								 | 
							
								            public string IndentToken { get; }
							 | 
						|||
| 
								 | 
							
								            /// <summary>
							 | 
						|||
| 
								 | 
							
								            /// The current indent level
							 | 
						|||
| 
								 | 
							
								            /// </summary>
							 | 
						|||
| 
								 | 
							
								            public int IndentLevel { get; private set; }
							 | 
						|||
| 
								 | 
							
								            /// <summary>
							 | 
						|||
| 
								 | 
							
								            /// The mechanism to use for name normalization
							 | 
						|||
| 
								 | 
							
								            /// </summary>
							 | 
						|||
| 
								 | 
							
								            public NameNormalizer NameNormalizer { get; }
							 | 
						|||
| 
								 | 
							
								            /// <summary>
							 | 
						|||
| 
								 | 
							
								            /// The output for this code generation
							 | 
						|||
| 
								 | 
							
								            /// </summary>
							 | 
						|||
| 
								 | 
							
								            public TextWriter Output { get; }
							 | 
						|||
| 
								 | 
							
								            /// <summary>
							 | 
						|||
| 
								 | 
							
								            /// The effective syntax of this code-generation cycle, defaulting to "proto2" if not explicity specified
							 | 
						|||
| 
								 | 
							
								            /// </summary>
							 | 
						|||
| 
								 | 
							
								            public string Syntax => string.IsNullOrWhiteSpace(File.Syntax) ? FileDescriptorProto.SyntaxProto2 : File.Syntax;
							 | 
						|||
| 
								 | 
							
								            /// <summary>
							 | 
						|||
| 
								 | 
							
								            /// Create a new GeneratorContext instance
							 | 
						|||
| 
								 | 
							
								            /// </summary>
							 | 
						|||
| 
								 | 
							
								            internal GeneratorContext(FileDescriptorProto file, NameNormalizer nameNormalizer, TextWriter output, string indentToken)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                File = file;
							 | 
						|||
| 
								 | 
							
								                NameNormalizer = nameNormalizer;
							 | 
						|||
| 
								 | 
							
								                Output = output;
							 | 
						|||
| 
								 | 
							
								                IndentToken = indentToken;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            /// <summary>
							 | 
						|||
| 
								 | 
							
								            /// Ends the current line
							 | 
						|||
| 
								 | 
							
								            /// </summary>
							 | 
						|||
| 
								 | 
							
								            public GeneratorContext WriteLine()
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                Output.WriteLine();
							 | 
						|||
| 
								 | 
							
								                return this;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            /// <summary>
							 | 
						|||
| 
								 | 
							
								            /// Appends a value and ends the current line
							 | 
						|||
| 
								 | 
							
								            /// </summary>
							 | 
						|||
| 
								 | 
							
								            public GeneratorContext WriteLine(string line)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                var indentLevel = IndentLevel;
							 | 
						|||
| 
								 | 
							
								                var target = Output;
							 | 
						|||
| 
								 | 
							
								                while (indentLevel-- > 0)
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    target.Write(IndentToken);
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								                target.WriteLine(line);
							 | 
						|||
| 
								 | 
							
								                return this;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            /// <summary>
							 | 
						|||
| 
								 | 
							
								            /// Appends a value to the current line
							 | 
						|||
| 
								 | 
							
								            /// </summary>
							 | 
						|||
| 
								 | 
							
								            public TextWriter Write(string value)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                var indentLevel = IndentLevel;
							 | 
						|||
| 
								 | 
							
								                var target = Output;
							 | 
						|||
| 
								 | 
							
								                while (indentLevel-- > 0)
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    target.Write(IndentToken);
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								                target.Write(value);
							 | 
						|||
| 
								 | 
							
								                return target;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            /// <summary>
							 | 
						|||
| 
								 | 
							
								            /// Increases the indentation level
							 | 
						|||
| 
								 | 
							
								            /// </summary>
							 | 
						|||
| 
								 | 
							
								            public GeneratorContext Indent()
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                IndentLevel++;
							 | 
						|||
| 
								 | 
							
								                return this;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            /// <summary>
							 | 
						|||
| 
								 | 
							
								            /// Decreases the indentation level
							 | 
						|||
| 
								 | 
							
								            /// </summary>
							 | 
						|||
| 
								 | 
							
								            public GeneratorContext Outdent()
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                IndentLevel--;
							 | 
						|||
| 
								 | 
							
								                return this;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            /// <summary>
							 | 
						|||
| 
								 | 
							
								            /// Try to find a descriptor of the type specified by T with the given full name
							 | 
						|||
| 
								 | 
							
								            /// </summary>
							 | 
						|||
| 
								 | 
							
								            public T TryFind<T>(string typeName) where T : class
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                object obj;
							 | 
						|||
| 
								 | 
							
								                if (!_knownTypes.TryGetValue(typeName, out obj) || obj == null)
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    return null;
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								                return obj as T;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            private Dictionary<string, object> _knownTypes = new Dictionary<string, object>();
							 | 
						|||
| 
								 | 
							
											void AddMessage(DescriptorProto message)
							 | 
						|||
| 
								 | 
							
											{
							 | 
						|||
| 
								 | 
							
												_knownTypes[message.FullyQualifiedName] = message;
							 | 
						|||
| 
								 | 
							
												foreach (var @enum in message.EnumTypes)
							 | 
						|||
| 
								 | 
							
												{
							 | 
						|||
| 
								 | 
							
													_knownTypes[@enum.FullyQualifiedName] = @enum;
							 | 
						|||
| 
								 | 
							
												}
							 | 
						|||
| 
								 | 
							
												foreach (var msg in message.NestedTypes)
							 | 
						|||
| 
								 | 
							
												{
							 | 
						|||
| 
								 | 
							
													AddMessage(msg);
							 | 
						|||
| 
								 | 
							
												}
							 | 
						|||
| 
								 | 
							
											}
							 | 
						|||
| 
								 | 
							
								            internal void BuildTypeIndex()
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    var processedFiles = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
							 | 
						|||
| 
								 | 
							
								                    var pendingFiles = new Queue<FileDescriptorProto>();
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                    _knownTypes.Clear();
							 | 
						|||
| 
								 | 
							
								                    processedFiles.Add(File.Name);
							 | 
						|||
| 
								 | 
							
								                    pendingFiles.Enqueue(File);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                    while (pendingFiles.Count != 0)
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        var file = pendingFiles.Dequeue();
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                        foreach (var @enum in file.EnumTypes)
							 | 
						|||
| 
								 | 
							
								                        {
							 | 
						|||
| 
								 | 
							
								                            _knownTypes[@enum.FullyQualifiedName] = @enum;
							 | 
						|||
| 
								 | 
							
								                        }
							 | 
						|||
| 
								 | 
							
								                        foreach (var msg in file.MessageTypes)
							 | 
						|||
| 
								 | 
							
								                        {
							 | 
						|||
| 
								 | 
							
								                            AddMessage(msg);
							 | 
						|||
| 
								 | 
							
								                        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                        if (file.HasImports())
							 | 
						|||
| 
								 | 
							
								                        {
							 | 
						|||
| 
								 | 
							
								                            foreach (var import in file.GetImports())
							 | 
						|||
| 
								 | 
							
								                            {
							 | 
						|||
| 
								 | 
							
								                                if (processedFiles.Add(import.Path))
							 | 
						|||
| 
								 | 
							
								                                {
							 | 
						|||
| 
								 | 
							
								                                    var importFile = file.Parent.GetFile(import.Path);
							 | 
						|||
| 
								 | 
							
								                                    if (importFile != null) pendingFiles.Enqueue(importFile);
							 | 
						|||
| 
								 | 
							
								                                }
							 | 
						|||
| 
								 | 
							
								                            }
							 | 
						|||
| 
								 | 
							
								                        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								}
							 |