237 lines
		
	
	
		
			9.3 KiB
		
	
	
	
		
			C#
		
	
	
		
		
			
		
	
	
			237 lines
		
	
	
		
			9.3 KiB
		
	
	
	
		
			C#
		
	
	
| 
								 | 
							
								using Google.Protobuf.Reflection;
							 | 
						|||
| 
								 | 
							
								using System;
							 | 
						|||
| 
								 | 
							
								using System.Collections.Generic;
							 | 
						|||
| 
								 | 
							
								using System.Text.RegularExpressions;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								namespace ProtoBuf.Reflection
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    internal class ParserException : Exception
							 | 
						|||
| 
								 | 
							
								    {
							 | 
						|||
| 
								 | 
							
								        public int ColumnNumber { get; }
							 | 
						|||
| 
								 | 
							
								        public int LineNumber { get; }
							 | 
						|||
| 
								 | 
							
								        public string File { get; }
							 | 
						|||
| 
								 | 
							
								        public string Text { get; }
							 | 
						|||
| 
								 | 
							
								        public string LineContents { get; }
							 | 
						|||
| 
								 | 
							
								        public bool IsError { get; }
							 | 
						|||
| 
								 | 
							
								        internal ParserException(Token token, string message, bool isError)
							 | 
						|||
| 
								 | 
							
								            : base(message ?? "error")
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            ColumnNumber = token.ColumnNumber;
							 | 
						|||
| 
								 | 
							
								            LineNumber = token.LineNumber;
							 | 
						|||
| 
								 | 
							
								            File = token.File;
							 | 
						|||
| 
								 | 
							
								            LineContents = token.LineContents;
							 | 
						|||
| 
								 | 
							
								            Text = token.Value ?? "";
							 | 
						|||
| 
								 | 
							
								            IsError = isError;
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								    /// <summary>
							 | 
						|||
| 
								 | 
							
								    /// Provides general purpose name suggestions
							 | 
						|||
| 
								 | 
							
								    /// </summary>
							 | 
						|||
| 
								 | 
							
								    public abstract class NameNormalizer
							 | 
						|||
| 
								 | 
							
								    {
							 | 
						|||
| 
								 | 
							
								        private class NullNormalizer : NameNormalizer
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            protected override string GetName(string identifier) => identifier;
							 | 
						|||
| 
								 | 
							
								            /// <summary>
							 | 
						|||
| 
								 | 
							
								            /// Suggest a name with idiomatic pluralization
							 | 
						|||
| 
								 | 
							
								            /// </summary>
							 | 
						|||
| 
								 | 
							
								            public override string Pluralize(string identifier) => identifier;
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								        private class DefaultNormalizer : NameNormalizer
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            protected override string GetName(string identifier) => AutoCapitalize(identifier);
							 | 
						|||
| 
								 | 
							
								            /// <summary>
							 | 
						|||
| 
								 | 
							
								            /// Suggest a name with idiomatic pluralization
							 | 
						|||
| 
								 | 
							
								            /// </summary>
							 | 
						|||
| 
								 | 
							
								            public override string Pluralize(string identifier) => AutoPluralize(identifier);
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Suggest a name with idiomatic name capitalization
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        public static string AutoCapitalize(string identifier)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            if (string.IsNullOrEmpty(identifier)) return identifier;
							 | 
						|||
| 
								 | 
							
								            // if all upper-case, make proper-case
							 | 
						|||
| 
								 | 
							
								            if (Regex.IsMatch(identifier, @"^[_A-Z0-9]*$"))
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                return Regex.Replace(identifier, @"(^|_)([A-Z0-9])([A-Z0-9]*)",
							 | 
						|||
| 
								 | 
							
								                    match => match.Groups[2].Value.ToUpperInvariant() + match.Groups[3].Value.ToLowerInvariant());
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            // if all lower-case, make proper case
							 | 
						|||
| 
								 | 
							
								            if (Regex.IsMatch(identifier, @"^[_a-z0-9]*$"))
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                return Regex.Replace(identifier, @"(^|_)([a-z0-9])([a-z0-9]*)",
							 | 
						|||
| 
								 | 
							
								                    match => match.Groups[2].Value.ToUpperInvariant() + match.Groups[3].Value.ToLowerInvariant());
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            // just remove underscores - leave their chosen casing alone
							 | 
						|||
| 
								 | 
							
								            return identifier.Replace("_", "");
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								        public static string AutoCapitalizeFullName(string fullName)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            var names = fullName.Split('.');
							 | 
						|||
| 
								 | 
							
								            var s = "";
							 | 
						|||
| 
								 | 
							
								            for (int i = 0; i < names.Length; i++)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                if (i == names.Length - 1)
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    s += $"{AutoCapitalize(names[i])}";
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								                else
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    s += $"{AutoCapitalize(names[i])}.";
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            return s;
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Suggest a name with idiomatic pluralization
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        protected static string AutoPluralize(string identifier)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            // horribly Anglo-centric and only covers common cases; but: is swappable
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            if (string.IsNullOrEmpty(identifier) || identifier.Length == 1) return identifier;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            if (identifier.EndsWith("ss") || identifier.EndsWith("o")) return identifier + "es";
							 | 
						|||
| 
								 | 
							
								            if (identifier.EndsWith("is") && identifier.Length > 2) return identifier.Substring(0, identifier.Length - 2) + "es";
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            if (identifier.EndsWith("s")) return identifier; // misses some things (bus => buses), but: might already be pluralized
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            if (identifier.EndsWith("y") && identifier.Length > 2)
							 | 
						|||
| 
								 | 
							
								            {   // identity => identities etc
							 | 
						|||
| 
								 | 
							
								                switch (identifier[identifier.Length - 2])
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    case 'a':
							 | 
						|||
| 
								 | 
							
								                    case 'e':
							 | 
						|||
| 
								 | 
							
								                    case 'i':
							 | 
						|||
| 
								 | 
							
								                    case 'o':
							 | 
						|||
| 
								 | 
							
								                    case 'u':
							 | 
						|||
| 
								 | 
							
								                        break; // only for consonant prefix
							 | 
						|||
| 
								 | 
							
								                    default:
							 | 
						|||
| 
								 | 
							
								                        return identifier.Substring(0, identifier.Length - 1) + "ies";
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            return identifier + "s";
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Name normalizer with default protobuf-net behaviour, using .NET idioms
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        public static NameNormalizer Default { get; } = new DefaultNormalizer();
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Name normalizer that passes through all identifiers without any changes
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        public static NameNormalizer Null { get; } = new NullNormalizer();
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Suggest a normalized identifier
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        protected abstract string GetName(string identifier);
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Suggest a name with idiomatic pluralization
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        public abstract string Pluralize(string identifier);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Suggest a normalized identifier
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        public virtual string GetName(FileDescriptorProto definition)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            var ns = definition?.Options?.GetOptions()?.Namespace;
							 | 
						|||
| 
								 | 
							
								            if (!string.IsNullOrWhiteSpace(ns)) return ns;
							 | 
						|||
| 
								 | 
							
								            ns = definition.Options?.CsharpNamespace;
							 | 
						|||
| 
								 | 
							
								            if (string.IsNullOrWhiteSpace(ns)) ns = GetName(definition.Package);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            return string.IsNullOrWhiteSpace(ns) ? null : ns;
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Suggest a normalized identifier
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        public virtual string GetName(DescriptorProto definition)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            var name = definition?.Options?.GetOptions()?.Name;
							 | 
						|||
| 
								 | 
							
								            if (!string.IsNullOrWhiteSpace(name)) return name;
							 | 
						|||
| 
								 | 
							
								            return GetName(definition.Parent as DescriptorProto, GetName(definition.Name), definition.Name, false);
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Suggest a normalized identifier
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        public virtual string GetName(EnumDescriptorProto definition)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            var name = definition?.Options?.GetOptions()?.Name;
							 | 
						|||
| 
								 | 
							
								            if (!string.IsNullOrWhiteSpace(name)) return name;
							 | 
						|||
| 
								 | 
							
								            return GetName(definition.Parent as DescriptorProto, GetName(definition.Name), definition.Name, false);
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Suggest a normalized identifier
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        public virtual string GetName(EnumValueDescriptorProto definition)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            var name = definition?.Options?.GetOptions()?.Name;
							 | 
						|||
| 
								 | 
							
								            if (!string.IsNullOrWhiteSpace(name)) return name;
							 | 
						|||
| 
								 | 
							
								            return AutoCapitalize(definition.Name);
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Suggest a normalized identifier
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        public virtual string GetName(FieldDescriptorProto definition)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            var name = definition?.Options?.GetOptions()?.Name;
							 | 
						|||
| 
								 | 
							
								            if (!string.IsNullOrWhiteSpace(name)) return name;
							 | 
						|||
| 
								 | 
							
								            var preferred = GetName(definition.Name);
							 | 
						|||
| 
								 | 
							
								            if (definition.label == FieldDescriptorProto.Label.LabelRepeated)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                preferred = Pluralize(preferred);
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            return GetName(definition.Parent as DescriptorProto, preferred, definition.Name, true);
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Obtain a set of all names defined for a message
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        protected HashSet<string> BuildConflicts(DescriptorProto parent, bool includeDescendents)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            var conflicts = new HashSet<string>();
							 | 
						|||
| 
								 | 
							
								            if (parent != null)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                conflicts.Add(GetName(parent));
							 | 
						|||
| 
								 | 
							
								                if (includeDescendents)
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    foreach (var type in parent.NestedTypes)
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        conflicts.Add(GetName(type));
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                    foreach (var type in parent.EnumTypes)
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        conflicts.Add(GetName(type));
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            return conflicts;
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Get the preferred name for an element
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        protected virtual string GetName(DescriptorProto parent, string preferred, string fallback, bool includeDescendents)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            var conflicts = BuildConflicts(parent, includeDescendents);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            if (!conflicts.Contains(preferred)) return preferred;
							 | 
						|||
| 
								 | 
							
								            if (!conflicts.Contains(fallback)) return fallback;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            var attempt = preferred + "Value";
							 | 
						|||
| 
								 | 
							
								            if (!conflicts.Contains(attempt)) return attempt;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            attempt = fallback + "Value";
							 | 
						|||
| 
								 | 
							
								            if (!conflicts.Contains(attempt)) return attempt;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            int i = 1;
							 | 
						|||
| 
								 | 
							
								            while (true)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                attempt = preferred + i.ToString();
							 | 
						|||
| 
								 | 
							
								                if (!conflicts.Contains(attempt)) return attempt;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								}
							 |