From c77baa48e306371c5a188c49b553003539132eb5 Mon Sep 17 00:00:00 2001 From: Andrea Cavalli Date: Sun, 7 May 2023 14:44:39 +0200 Subject: [PATCH] Improve documentation, add modern switch-cases --- headers.txt | 63 +++++++++++++-- transform.java | 211 ++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 224 insertions(+), 50 deletions(-) diff --git a/headers.txt b/headers.txt index a7a02e8..b1b79ce 100644 --- a/headers.txt +++ b/headers.txt @@ -1,22 +1,52 @@ import java.io.ByteArrayOutputStream; import java.io.DataOutput; import java.io.DataOutputStream; -import java.io.InputStream; import java.nio.charset.StandardCharsets; -import java.lang.IllegalStateException; import java.io.IOException; import java.io.DataInput; import java.util.Arrays; import java.util.Objects; +/** + * This class contains as static nested classes all other TDLib interface + * type-classes and function-classes. + *

+ * It has no inner classes, functions or public members. + */ public final class TdApi { + + private TdApi() {} + + /** + * This class is a base class for all TDLib interface classes. + */ public abstract static class Object { + + /** + * Returns a string representation of the object. + * + * @return a string representation of the object. + */ public native String toString(); + /** + * Returns an identifier uniquely determining type of the object. + * + * @return a unique identifier of the object type. + */ public abstract int getConstructor(); + /** + * Default Object constructor. + */ private Object() {} + /** + * Serialize the object. + * + * @return the current object serialized into a byte[]. + * @throws IOException the serialization failed + */ public byte[] serialize() throws IOException { try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { try (DataOutputStream out = new DataOutputStream(baos)) { @@ -26,12 +56,33 @@ public final class TdApi { } } + /** + * Serialize the object. + * + * @param out the output in which the object is serialized. + * @throws IOException the serialization failed + */ public abstract void serialize(DataOutput out) throws IOException; + } + /** + * This class is a base class for all TDLib interface function-classes. + * @param The function return type + */ public abstract static class Function extends TdApi.Object { - private Function() {} - - public native String toString(); - } + + /** + * Default Function constructor. + */ + private Function() {} + + /** + * Returns a string representation of the object. + * + * @return a string representation of the object. + */ + public native String toString(); + + } diff --git a/transform.java b/transform.java index 0c07847..54fd3fa 100644 --- a/transform.java +++ b/transform.java @@ -16,6 +16,7 @@ import java.util.stream.Stream; public class transform { static List NATIVES = List.of("int", "long", "boolean", "String", "double", "byte[]", "byte"); + static List PRIMITIVES = List.of("int", "long", "boolean", "double", "byte"); static Map NATIVE_TO_OBJ = Map.of("boolean", "Boolean", "byte[]", "Byte", "byte", "Byte", @@ -270,31 +271,47 @@ public class transform { } else { openFlags.add(StandardOpenOption.CREATE_NEW); } - try (var w = new JavaWriter(Files.newBufferedWriter(outputPath, StandardCharsets.UTF_8, openFlags.toArray(OpenOption[]::new)), 1)) { + try (var w = new JavaWriter(Files.newBufferedWriter(outputPath, StandardCharsets.UTF_8, openFlags.toArray(OpenOption[]::new)), 1, java17)) { // Write package w.write(package_).writeNewLine().writeNewLine(); // Write header w.write(headers); - w.writeIndent().writeOpenCustomBlock("public static class Deserializer").writeNewLine(); + w.writeIndent().writeOpenDocs().writeNewLine(); + w.writeIndent().writeDocs("This class deserializes TDLib classes").writeNewLine(); + w.writeIndent().writeCloseDocs().writeNewLine(); + + w.writeIndent().writeOpenCustomBlock("public static class Deserializer").writeNewLine().writeNewLine(); + + w.writeIndent().writeOpenDocs().writeNewLine(); + w.writeIndent().writeDocs("The default constructor").writeNewLine(); + w.writeIndent().writeCloseDocs().writeNewLine(); + w.writeIndent().writeOpenConstructorFunction("Deserializer", List.of(), null).writeNewLine(); + w.writeCloseBlock(true).writeNewLine(); + w.writeNewLine(); + + + w.writeIndent().writeOpenDocs().writeNewLine(); + w.writeIndent().writeDocs("Deserialize the TDLib class").writeNewLine(); + w.writeIndent().writeDocs("@param input stream that contain the serialized TDLib class to deserialize").writeNewLine(); + w.writeIndent().writeDocs("@return the deserialized TDLib class").writeNewLine(); + w.writeIndent().writeDocs("@throws IOException the deserialization failed").writeNewLine(); + w.writeIndent().writeCloseDocs().writeNewLine(); w.writeIndent().writeOpenFunction("deserialize", List.of(Map.entry("DataInput", "input")), "static Object", "IOException").writeNewLine(); - w.writeIndent().writeOpenSwitch("input.readInt()").writeNewLine(); + w.writeIndent().writeOpenSwitchExpression(null, null, false, "input.readInt()").writeNewLine(); allClasses.forEach((className, x) -> { - w.writeIndent().writeOpenSwitch(className + ".CONSTRUCTOR").writeNewLine(); - w.writeIndent().writeReturn("new " + className + "(input)").writeNewLine(); - - w.decrementIndentation(); + w.writeIndent().writeSwitchCaseAndYield(className + ".CONSTRUCTOR", "new " + className + "(input)").writeNewLine(); }); w.writeIndent().writeOpenSwitchDefault().writeNewLine(); - w.writeIndent().writeException("UnsupportedOperationException").writeNewLine(); - w.decrementIndentation(); + w.writeIndent().writeYieldException("UnsupportedOperationException").writeNewLine(); + w.writeIndent().writeCloseCase().writeNewLine(); + w.writeCloseSwitchExpression().writeNewLine(); - w.writeCloseBlock(true).writeNewLine(); w.writeCloseBlock(true).writeNewLine(); w.writeCloseBlock(true).writeNewLine(); @@ -328,12 +345,19 @@ public class transform { w.write(lines.get(i)); } w.decrementIndentation(); - w.writeOpenCustomBlock(""); + w.writeOpenCustomBlock("").writeNewLine(); } else { - w.writeOpenCustomBlock("public abstract static class " + containerClassName + " extends Object"); + w.writeOpenCustomBlock("public abstract static class " + containerClassName + " extends Object").writeNewLine(); } - w.writeCloseBlock().writeNewLine().writeNewLine(); + w.writeNewLine(); + + w.writeIndent().writeOpenDocs().writeNewLine(); + w.writeIndent().writeDocs("Default class constructor.").writeNewLine(); + w.writeIndent().writeCloseDocs().writeNewLine(); + w.writeIndent().writeOpenConstructorFunction(containerClassName, List.of(), null).writeCloseBlock().writeNewLine().writeNewLine(); + + w.writeCloseBlock(true).writeNewLine().writeNewLine(); }); allClasses.forEach((className, classMeta) -> { @@ -347,7 +371,8 @@ public class transform { } else { classGenerics = ""; } - w.writeOpenCustomBlock("public static final class", className, "extends", classMeta.containerName + classGenerics); + w.writeOpenCustomBlock("public static final class", className, "extends", classMeta.containerName + classGenerics).writeNewLine(); + w.writeNewLine(); classMeta.arguments.forEach(argument -> { w.writeNewLine().writeIndent().writeOpenDocs().writeNewLine().writeIndent(); @@ -367,6 +392,7 @@ public class transform { w.writeNewLine().writeIndent(); w.writeOpenConstructorFunction(className, List.of(), null); w.writeCloseBlock().writeNewLine(); + w.writeNewLine(); w.writeIndent().writeOpenDocs(); @@ -376,21 +402,25 @@ public class transform { classMeta.arguments.forEach(arg -> { var docs = arg.docs; w.writeNewLine().writeIndent(); - w.writeDocs("@param " + arg.type + " " + arg.name + " " + docs.get(0)); + w.writeDocs("@param " + arg.name + " " + docs.get(0)); }); classMeta.docs.subList(1, classMeta.docs.size()).forEach(doc -> w.writeNewLine().writeIndent().writeDocs(doc)); w.writeNewLine().writeIndent().writeCloseDocs(); w.writeNewLine(); + if (!classMeta.arguments.isEmpty()) { - w.writeIndent().writeOpenConstructorFunction(className, classMeta.arguments.stream().map(a -> Map.entry(a.name, a.type)).collect(Collectors.toList()), null); + w.writeIndent().writeOpenConstructorFunction(className, classMeta.arguments.stream().map(a -> Map.entry(a.type, a.name)).collect(Collectors.toList()), null); w.writeNewLine(); classMeta.arguments.forEach(arg -> w.writeIndent().writeClassAssign(arg.name, arg.name).writeNewLine()); w.writeCloseBlock(true).writeNewLine(); - w.writeNewLine().writeIndent().writeOpenDocs(); - classMeta.docs.forEach(doc -> w.writeNewLine().writeIndent().writeDocs(doc)); - w.writeNewLine().writeIndent().writeCloseDocs().writeNewLine(); } + w.writeNewLine().writeIndent().writeOpenDocs().writeNewLine(); + classMeta.docs.forEach(doc -> w.writeIndent().writeDocs(doc).writeNewLine()); + w.writeIndent().writeDocs("").writeNewLine(); + w.writeIndent().writeDocs("@param input Serialized input").writeNewLine(); + w.writeIndent().writeDocs("@throws IOException the deserialization failed").writeNewLine(); + w.writeIndent().writeCloseDocs().writeNewLine(); w.writeIndent().writeOpenConstructorFunction(className, List.of(Map.entry("DataInput", "input")), "IOException").writeNewLine(); classMeta.arguments.forEach(arg -> { @@ -439,11 +469,18 @@ public class transform { w.writeIndent().writeDocs("@return this.CONSTRUCTOR").writeNewLine(); w.writeIndent().writeCloseDocs().writeNewLine(); w.writeIndent().writeOpenFunction("getConstructor", List.of(), "int", null).writeNewLine(); - w.writeIndent().writeReturn("CONSTRUCTOR").writeNewLine(); + w.writeIndent().writeReturn(className + ".CONSTRUCTOR").writeNewLine(); w.writeCloseBlock(true).writeNewLine(); w.writeNewLine(); + + w.writeIndent().writeOpenDocs().writeNewLine(); + w.writeIndent().writeDocs("Serialize the TDLib class").writeNewLine(); + w.writeIndent().writeDocs("@param output output data stream").writeNewLine(); + w.writeIndent().writeDocs("@throws IOException the serialization failed").writeNewLine(); + w.writeIndent().writeCloseDocs().writeNewLine(); + w.writeIndent().writeOpenFunction("serialize", List.of(Map.entry("DataOutput", "output")), "void", "IOException").writeNewLine(); - w.writeIndent().writeCall("output.writeInt", "CONSTRUCTOR").writeNewLine(); + w.writeIndent().writeCall("output.writeInt", className + ".CONSTRUCTOR").writeNewLine(); classMeta.arguments.forEach(arg -> { if (NATIVES.contains(arg.type)) { serializeNative(w, arg.name, arg.type); @@ -458,7 +495,7 @@ public class transform { w.writeIndent().writeCall("output.writeInt", "this." + arg.name + ".length").writeNewLine(); w.writeIndent().writeOpenFor("int i = 0", "i < this." + arg.name + ".length", "i++").writeNewLine(); if (NATIVES.contains(baseArgType)) { - serializeNative(w, baseArgType, arg.name + "[i]", false); + serializeNative(w, arg.name + "[i]", baseArgType, false); } else { serializeTdApi(w, arg.name + "[i]", false); } @@ -475,7 +512,7 @@ public class transform { w.writeIndent().writeCall("output.writeInt", "this." + arg.name + "[i].length").writeNewLine(); w.writeIndent().writeOpenFor("int j = 0", "j < this." + arg.name + "[i].length", "j++").writeNewLine(); if (NATIVES.contains(baseArgType)) { - serializeNative(w, baseArgType, arg.name + "[i][j]", false); + serializeNative(w, arg.name + "[i][j]", baseArgType, false); } else { serializeTdApi(w, arg.name + "[i][j]", false); } @@ -505,7 +542,7 @@ public class transform { } else if (!arg.type.endsWith("[]")) { w.writeOpenIf("!Objects.equals(this." + arg.name + ", " + otherClass + "." + arg.name + ")"); } else if (arg.type.endsWith("[][]")) { - w.writeOpenIf("!Arrays.deepEquals(this." + arg.type + ", " + otherClass + "." + arg.name + ")"); + w.writeOpenIf("!Arrays.deepEquals(this." + arg.name + ", " + otherClass + "." + arg.name + ")"); } else { w.writeOpenIf("!Arrays.equals(this." + arg.name + ", " + otherClass + "." + arg.name + ")"); } @@ -518,14 +555,14 @@ public class transform { w.writeIndent().writeOpenFunction("hashCode", List.of(), "int", null).writeNewLine(); w.writeIndent(); if (!classMeta.arguments.isEmpty()) { - var primitives = classMeta.arguments.stream().filter(n -> NATIVES.contains(n.type)).collect(Collectors.toList()); + var primitives = classMeta.arguments.stream().filter(n -> PRIMITIVES.contains(n.type)).collect(Collectors.toList()); if (!primitives.isEmpty() && classMeta.arguments.size() == 1) { w.writeReturn(NATIVE_TO_OBJ.get(primitives.get(0).type) + ".hashCode(this." + primitives.get(0).name + ")"); } else if (!primitives.isEmpty()) { w.writeLocalAssign("int", "result", NATIVE_TO_OBJ.get(primitives.get(0).type) + ".hashCode(this." + primitives.get(0).name + ")").writeNewLine(); w.writeIndent(); } - var tdapi = classMeta.arguments.stream().filter(n -> !NATIVES.contains(n.type)).collect(Collectors.toList()); + var tdapi = classMeta.arguments.stream().filter(n -> !PRIMITIVES.contains(n.type)).collect(Collectors.toList()); int start; if (!tdapi.isEmpty() && classMeta.arguments.size() == 1) { w.writeReturn(hashObject("this." + tdapi.get(0).name, tdapi.get(0).type)); @@ -549,7 +586,7 @@ public class transform { w.writeReturn("result"); } } else { - w.writeReturn("CONSTRUCTOR"); + w.writeReturn(className + ".CONSTRUCTOR"); } w.writeNewLine(); @@ -628,7 +665,7 @@ public class transform { w.writeIndent().writeOpenIfElse(true).writeNewLine(); w.writeIndent().writeCall("output.writeBoolean", "true").writeNewLine(); } - var tmpName = argName.split("[", 2)[0] + "Tmp"; + var tmpName = argName.split("\\[", 2)[0] + "Tmp"; w.writeIndent().writeLocalAssign("byte[]", tmpName, "this." + argName + ".getBytes(StandardCharsets.UTF_8)").writeNewLine(); w.writeIndent().writeCall("output.writeInt", tmpName + ".length").writeNewLine(); w.writeIndent().writeCall("output.write", tmpName).writeNewLine(); @@ -636,6 +673,7 @@ public class transform { w.writeCloseBlock(true).writeNewLine(); } break; + default: throw new UnsupportedOperationException(argName + ":" + argType); } } @@ -649,19 +687,18 @@ public class transform { } w.writeIndent(); if (cont.containsKey(argType)) { - w.writeOpenSwitch("input.readInt()").writeNewLine(); + w.writeOpenSwitchExpression(null, "this." + argName, true, "input.readInt()").writeNewLine(); classes.forEach((className, classMeta) -> { if (classMeta.containerName.equals(argType) && !className.equals(argType)) { - w.writeIndent().writeOpenSwitchCase(className + ".CONSTRUCTOR"); - w.writeNewLine().writeIndent().writeClassAssign(argName, "new " + className + "(input)"); - w.writeNewLine().writeIndent().writeSwitchBreak().writeNewLine(); + w.writeIndent().writeSwitchCaseAndYield(className + ".CONSTRUCTOR", "new " + className + "(input)").writeNewLine(); } }); w.writeIndent().writeOpenSwitchDefault().writeNewLine(); - w.writeIndent().writeException("UnsupportedOperationException").writeNewLine().decrementIndentation(); - w.writeCloseBlock(true).writeNewLine(); + w.writeIndent().writeYieldException("UnsupportedOperationException").writeNewLine(); + w.writeIndent().writeCloseCase().writeNewLine(); + w.writeCloseSwitchExpression().writeNewLine(); } else { w.writeOpenIf(argType + ".CONSTRUCTOR != input.readInt()"); w.writeNewLine().writeIndent().writeException("UnsupportedOperationException"); @@ -713,7 +750,7 @@ public class transform { if (nullCheck) { w.writeIndent().writeOpenIf("input.readBoolean()").writeNewLine(); } - var tmpName = name.split("\\[", 1)[0] + "Tmp"; + var tmpName = name.split("\\[", 2)[0] + "Tmp"; w.writeIndent().writeLocalAssign("byte[]", tmpName, "new byte[input.readInt()]").writeNewLine(); w.writeIndent().writeCall("input.readFully", tmpName).writeNewLine(); w.writeIndent().writeClassAssign(name, "new String(" + tmpName + ", StandardCharsets.UTF_8)").writeNewLine(); @@ -830,11 +867,15 @@ public class transform { @SuppressWarnings({"UnusedReturnValue", "unused", "resource"}) public static class JavaWriter implements Closeable { private final Writer os; + private final boolean java17; private int depth; + private boolean insideSwitchExpression; + private String switchExpressionVarName; - public JavaWriter(Writer os, int depth) { + public JavaWriter(Writer os, int depth, boolean java17) { this.os = os; this.depth = depth; + this.java17 = java17; } public JavaWriter write(String data) { @@ -915,27 +956,109 @@ public class transform { return this; } + public JavaWriter writeOpenSwitchExpression(String varType, String varName, boolean alreadyExistingVar, String data) { + if (insideSwitchExpression) throw new UnsupportedOperationException(); + insideSwitchExpression = true; + this.switchExpressionVarName = varName; + if (java17) { + if (switchExpressionVarName == null) { + write("return switch (" + data + ") {"); + } else if (alreadyExistingVar) { + write(varName + " = switch (" + data + ") {"); + } else { + write((varType == null ? "var" : varType) + " " + varName + " = switch (" + data + ") {"); + } + } else { + if (!alreadyExistingVar && varName != null) { + write((varType == null ? "Object" : varType) + " " + varName + ";"); + writeNewLine(); + writeIndent(); + } + write("switch(" + data + ") {"); + } + incrementIndentation(); + return this; + } + public JavaWriter writeSwitchBreak() { + if (insideSwitchExpression) throw new UnsupportedOperationException(); decrementIndentation(); write("break;"); return this; } + public JavaWriter writeYield(String expression) { + if (!insideSwitchExpression) throw new UnsupportedOperationException(); + if (java17) { + write("yield " + expression + ";"); + } else if (this.switchExpressionVarName == null) { + write("return " + expression + ";"); + } else { + write(this.switchExpressionVarName + " = " + expression + "; break;"); + } + decrementIndentation(); + return this; + } + + public JavaWriter writeYieldException(String expression) { + if (!insideSwitchExpression) throw new UnsupportedOperationException(); + write("throw new " + expression + "();"); + decrementIndentation(); + return this; + } + + public JavaWriter writeCloseCase() { + write("}"); + return this; + } + + public JavaWriter writeCloseSwitchExpression() { + decrementIndentation(); + writeIndent(); + if (!insideSwitchExpression) throw new UnsupportedOperationException(); + insideSwitchExpression = false; + if (java17) { + write("};"); + } else { + write("}"); + } + return this; + } + public JavaWriter writeOpenSwitchCase(String data) { + if (java17 && insideSwitchExpression) { + write("case " + data + " -> {"); + } else { + write("case " + data + ":"); + } incrementIndentation(); - write("case " + data + ":"); + return this; + } + + public JavaWriter writeSwitchCaseAndYield(String caseExpression, String yieldExpression) { + if (java17 && insideSwitchExpression) { + write("case " + caseExpression + " -> "); + } else { + write("case " + caseExpression + ": "); + } + if (!insideSwitchExpression) throw new UnsupportedOperationException(); + if (java17) { + write(yieldExpression + ";"); + } else if (this.switchExpressionVarName == null) { + write("return " + yieldExpression + ";"); + } else { + write(this.switchExpressionVarName + " = " + yieldExpression + "; break;"); + } return this; } public JavaWriter writeOpenSwitchDefault() { + if (java17 && insideSwitchExpression) { + write("default -> {"); + } else { + write("default: {"); + } incrementIndentation(); - write("default:"); - return this; - } - - public JavaWriter writeSwitchDefault() { - incrementIndentation(); - write("default:"); return this; }