using System.Text; namespace Json2CSharpCodeGenerator.Lib.CodeWriters; public class JavaCodeWriter : ICodeBuilder { public string FileExtension => ".java"; public string DisplayName => "Java"; private static readonly HashSet _ReservedKeywords = new(comparer: StringComparer.Ordinal) { // https://en.wikipedia.org/wiki/List_of_Java_keywords // `Array.from( document.querySelectorAll('dl > dt > code') ).map( e => '"' + e.textContent + '"' ).sort().join( ", " )` "abstract", "assert", "boolean", "break", "byte", "case", "catch", "char", "class", "const", "const", "continue", "default", "do", "double", "else", "enum", "extends", "false", "final", "finally", "float", "for", "goto", "goto", "if", "implements", "import", "instanceof", "int", "interface", "long", "native", "new", "non-sealed", "null", "open", "opens", "package", "permits", "private", "protected", "provides", "public", "record", "return", "sealed", "short", "static", "strictfp", "super", "switch", "synchronized", "this", "throw", "throws", "to", "transient", "transitive", "true", "try", "uses", "var", "void", "volatile", "while", "with", "yield" }; IReadOnlyCollection ICodeBuilder.ReservedKeywords => _ReservedKeywords; public bool IsReservedKeyword(string word) => _ReservedKeywords.Contains(word ?? string.Empty); public string GetTypeName(JsonType type, IJsonClassGeneratorConfig config) { return type.Type switch { JsonTypeEnum.Anything => "Object", JsonTypeEnum.Array => GetCollectionTypeName(elementTypeName: GetTypeName(type.InternalType, config), config.CollectionType), JsonTypeEnum.Dictionary => "HashMap", JsonTypeEnum.Boolean => "boolean", JsonTypeEnum.Float => "double", JsonTypeEnum.Integer => "int", JsonTypeEnum.NonConstrained => "Object", JsonTypeEnum.NullableBoolean => "boolean", JsonTypeEnum.NullableFloat => "double", JsonTypeEnum.NullableInteger => "int", JsonTypeEnum.NullableLong => "long", JsonTypeEnum.NullableDate => "Date", JsonTypeEnum.Long => "long", JsonTypeEnum.Date => "Date", JsonTypeEnum.NullableSomething => "Object", JsonTypeEnum.Object => type.NewAssignedName, JsonTypeEnum.String => "String", _ => throw new NotSupportedException("Unsupported json type"), }; } private static string GetCollectionTypeName(string elementTypeName, OutputCollectionType type) { return type switch { OutputCollectionType.Array => elementTypeName + "[]", OutputCollectionType.MutableList => "ArrayList<" + elementTypeName + ">", OutputCollectionType.IReadOnlyList or OutputCollectionType.ImmutableArray => throw new NotSupportedException("Java does not have in-box interfaces for read-only and immutable collections."), _ => throw new ArgumentOutOfRangeException(paramName: nameof(type), actualValue: type, message: "Invalid " + nameof(OutputCollectionType) + " enum value."), }; } public void WriteClass(IJsonClassGeneratorConfig config, StringBuilder sw, JsonType type) { string visibility = config.InternalVisibility ? "" : "public"; _ = sw.AppendFormat("{0} class {1}", visibility, type.AssignedName); _ = sw.AppendLine("{"); string prefix = config.UseNestedClasses && !type.IsRoot ? "" : " "; WriteClassMembers(config, sw, type, prefix); if (config.UseNestedClasses && !type.IsRoot) _ = sw.AppendLine(" }"); if (!config.UseNestedClasses) _ = sw.AppendLine("}"); _ = sw.AppendLine(); } public void WriteClassMembers(IJsonClassGeneratorConfig config, StringBuilder sw, JsonType type, string prefix) { foreach (FieldInfo field in type.Fields) { if (config.UsePascalCase || config.ExamplesInDocumentation) _ = sw.AppendLine(); // Check if property name starts with number string memberName = field.MemberName; if (!string.IsNullOrEmpty(field.MemberName) && char.IsDigit(field.MemberName[0])) memberName = "_" + memberName; if (IsReservedKeyword(memberName)) memberName = "my" + memberName; if (config.MutableClasses.Members == OutputMembers.AsProperties) { _ = sw.AppendFormat(prefix + "@JsonProperty" + "(\"{0}\") {1}", field.JsonMemberName, Environment.NewLine); _ = sw.AppendFormat(prefix + "public {0} get{1}() {{ \r\t\t return this.{2}; }} {3}", field.Type.GetTypeName(), ChangeFirstChar(memberName), ChangeFirstChar(memberName, false), Environment.NewLine); _ = sw.AppendFormat(prefix + "public void set{1}({0} {2}) {{ \r\t\t this.{2} = {2}; }} {3}", field.Type.GetTypeName(), ChangeFirstChar(memberName), ChangeFirstChar(memberName, false), Environment.NewLine); _ = sw.AppendFormat(prefix + "{0} {1};", field.Type.GetTypeName(), ChangeFirstChar(memberName, false)); _ = sw.AppendLine(); } else if (config.MutableClasses.Members == OutputMembers.AsPublicFields) { memberName = ChangeFirstChar(memberName, false); if (field.JsonMemberName != memberName) { _ = sw.AppendFormat(prefix + "@JsonProperty" + "(\"{0}\") {1}", field.JsonMemberName, Environment.NewLine); } _ = sw.AppendFormat(prefix + "public {0} {1};{2}", field.Type.GetTypeName(), memberName, Environment.NewLine); } else { throw new InvalidOperationException("Unsupported " + nameof(OutputMembers) + " value: " + config.MutableClasses.Members); } } } private static string ChangeFirstChar(string value, bool toCaptial = true) { if (value == null) throw new ArgumentNullException("value"); if (value.Length == 0) return value; StringBuilder sb = new(); _ = sb.Append(toCaptial ? char.ToUpper(value[0]) : char.ToLower(value[0])); _ = sb.Append(value.Substring(1)); return sb.ToString(); } public void WriteFileStart(IJsonClassGeneratorConfig config, StringBuilder sw) { // foreach (var line in JsonClassGenerator.FileHeader) // { // sw.WriteLine("// " + line); // } } public void WriteFileEnd(IJsonClassGeneratorConfig config, StringBuilder sw) { if (config.UseNestedClasses) { _ = sw.AppendLine(" }"); } } public void WriteNamespaceStart(IJsonClassGeneratorConfig config, StringBuilder sw, bool root) { _ = sw.AppendLine(); _ = sw.AppendFormat("package {0};", root && !config.UseNestedClasses ? config.Namespace : (config.SecondaryNamespace ?? config.Namespace)); _ = sw.AppendLine(); } public void WriteNamespaceEnd(IJsonClassGeneratorConfig config, StringBuilder sw, bool root) => sw.AppendLine("}"); public void WriteDeserializationComment(IJsonClassGeneratorConfig config, StringBuilder sw, bool rootIsArray = false) { _ = sw.AppendLine("// import com.fasterxml.jackson.databind.ObjectMapper; // version 2.11.1"); _ = sw.AppendLine("// import com.fasterxml.jackson.annotation.JsonProperty; // version 2.11.1"); _ = sw.AppendLine("/* ObjectMapper om = new ObjectMapper();"); if (rootIsArray) { _ = sw.AppendLine("Root[] root = om.readValue(myJsonString, Root[].class); */"); } else { _ = sw.AppendLine("Root root = om.readValue(myJsonString, Root.class); */"); } } }