diff --git a/CREDITS.md b/CREDITS.md index 8008497dc..53be5097f 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -19,4 +19,8 @@ Nether Chest code for extended slot sizes used with permission by the author, Ma Ender Core code for range particles used with permission (from two contributors) but also with license compatibility (EnderCore & Ender IO are both Public Domain): - [EnderCore](https://github.com/SleepyTrousers/EnderCore) -- [EnderIO](https://github.com/SleepyTrousers/EnderIO/) \ No newline at end of file +- [EnderIO](https://github.com/SleepyTrousers/EnderIO/) + +# CodeTaylor's ZenScript Documentation + +Used with compatible Apache License and permission from CodeTaylor. \ No newline at end of file diff --git a/src/main/java/com/aranaira/arcanearchives/ExportDocumentation.java b/src/main/java/com/aranaira/arcanearchives/ExportDocumentation.java new file mode 100644 index 000000000..49af94569 --- /dev/null +++ b/src/main/java/com/aranaira/arcanearchives/ExportDocumentation.java @@ -0,0 +1,31 @@ +package com.aranaira.arcanearchives; + +import com.aranaira.arcanearchives.integration.crafttweaker.GCTTweaker; +import epicsquid.roots.util.zen.ZenDocExporter; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +public class ExportDocumentation { + + public static void main (String[] args) { + + String targetPath = "docs/zs/"; + Class[] classes = { + GCTTweaker.class + }; + + ZenDocExporter export = new ZenDocExporter(); + Path path = Paths.get(targetPath); + + try { + Files.createDirectories(path); + export.export(path, classes); + + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/src/main/java/com/aranaira/arcanearchives/util/StringHelper.java b/src/main/java/com/aranaira/arcanearchives/util/StringHelper.java new file mode 100644 index 000000000..f4ee7558d --- /dev/null +++ b/src/main/java/com/aranaira/arcanearchives/util/StringHelper.java @@ -0,0 +1,37 @@ +package epicsquid.roots.util; + +// Copied with permission from Athenaeum by CodeTaylor, Apache License +// https://github.com/codetaylor/athenaeum/blob/master/src/main/java/com/codetaylor/mc/athenaeum/util/StringHelper.java + +public class StringHelper { + + public static String capitalizeFirstLetter(String input) { + + return input.substring(0, 1).toUpperCase() + input.substring(1); + } + + public static String lowercaseFirstLetter(String input) { + + return input.substring(0, 1).toLowerCase() + input.substring(1); + } + + public static String ticksToHMS(int ticks) { + + int totalSecs = ticks / 20; + int hours = totalSecs / 3600; + int minutes = (totalSecs % 3600) / 60; + int seconds = totalSecs % 60; + + if (hours > 0) { + return String.format("%02d:%02d:%02d", hours, minutes, seconds); + + } else { + return String.format("%02d:%02d", minutes, seconds); + } + } + + private StringHelper() { + // + } + +} diff --git a/src/main/java/com/aranaira/arcanearchives/util/zen/ZenDocAppend.java b/src/main/java/com/aranaira/arcanearchives/util/zen/ZenDocAppend.java new file mode 100644 index 000000000..7a5de75df --- /dev/null +++ b/src/main/java/com/aranaira/arcanearchives/util/zen/ZenDocAppend.java @@ -0,0 +1,17 @@ +package com.aranaira.arcanearchives.util.zen; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +// This file and all others in this directory taken from Athenaeeum by CodeTaylor +// Licensed with the Apache License and used with permission +// https://github.com/codetaylor/athenaeum/tree/master/src/main/java/com/codetaylor/mc/athenaeum/tools + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface ZenDocAppend { + + String[] value (); +} diff --git a/src/main/java/com/aranaira/arcanearchives/util/zen/ZenDocArg.java b/src/main/java/com/aranaira/arcanearchives/util/zen/ZenDocArg.java new file mode 100644 index 000000000..3b306086c --- /dev/null +++ b/src/main/java/com/aranaira/arcanearchives/util/zen/ZenDocArg.java @@ -0,0 +1,16 @@ +package com.aranaira.arcanearchives.util.zen; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface ZenDocArg { + + String arg (); + + String info () default ""; + +} diff --git a/src/main/java/com/aranaira/arcanearchives/util/zen/ZenDocClass.java b/src/main/java/com/aranaira/arcanearchives/util/zen/ZenDocClass.java new file mode 100644 index 000000000..3748b8fa4 --- /dev/null +++ b/src/main/java/com/aranaira/arcanearchives/util/zen/ZenDocClass.java @@ -0,0 +1,15 @@ +package com.aranaira.arcanearchives.util.zen; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface ZenDocClass { + + String value (); + + String[] description () default {}; +} diff --git a/src/main/java/com/aranaira/arcanearchives/util/zen/ZenDocExporter.java b/src/main/java/com/aranaira/arcanearchives/util/zen/ZenDocExporter.java new file mode 100644 index 000000000..eae3b4969 --- /dev/null +++ b/src/main/java/com/aranaira/arcanearchives/util/zen/ZenDocExporter.java @@ -0,0 +1,288 @@ +package com.aranaira.arcanearchives.util.zen; + +import org.apache.commons.lang3.StringUtils; +import stanhebben.zenscript.annotations.Optional; + +import java.io.IOException; +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.stream.Collectors; + +// This file and all others in this directory taken from Athenaeeum by CodeTaylor +// Licensed with the Apache License and used with permission +// https://github.com/codetaylor/athenaeum/tree/master/src/main/java/com/codetaylor/mc/athenaeum/tools + +public class ZenDocExporter { + + public void export (Path path, Class[] classes) { + + for (int i = 0; i < classes.length; i++) { + StringBuilder out = new StringBuilder(); + + ZenDocClass zenClass = (ZenDocClass) classes[i].getDeclaredAnnotation(ZenDocClass.class); + ZenDocAppend zenDocAppend = (ZenDocAppend) classes[i].getDeclaredAnnotation(ZenDocAppend.class); + + if (zenClass == null) { + continue; + } + + if (i > 0) { + out.append("\n"); + } + + // --- Header + + String[] h3 = zenClass.value().split("\\."); + String zenClassName = h3[h3.length - 1]; + out.append("### Class\n"); + out.append("\n"); + + // --- Import + + out.append("```java").append("\n"); + out.append("import ").append(zenClass.value()).append(";").append("\n"); + out.append("```").append("\n"); + out.append("\n"); + + // --- Class Description + + String[] description = zenClass.description(); + + if (description.length > 0) { + + for (String line : description) { + out.append(this.parse(line)).append("\n"); + } + out.append("\n"); + } + + // --- Methods + + out.append("#### Methods\n"); + out.append("\n"); + + Method[] methods = classes[i].getDeclaredMethods(); + List methodList = this.getSortedMethodList(methods); + + // Add static methods to new list. + List staticMethodList = methodList.stream().filter(pair -> Modifier.isStatic(pair.method.getModifiers())).collect(Collectors.toList()); + + // Remove static methods from main list. + methodList = methodList.stream().filter(pair -> !Modifier.isStatic(pair.method.getModifiers())).collect(Collectors.toList()); + + // --- Static Methods + + if (!staticMethodList.isEmpty()) { + this.writeMethodList(out, staticMethodList); + } + + // --- Methods + + if (!methodList.isEmpty()) { + this.writeMethodList(out, methodList); + } + + // --- Append + + if (zenDocAppend != null) { + String[] toAppend = zenDocAppend.value(); + + out.append("\n"); + + for (String s : toAppend) { + Path p = Paths.get(s); + + try { + List lines = Files.readAllLines(p); + + for (String line : lines) { + out.append(line).append("\n"); + } + + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + // --- Output + + try { + Files.write(path.resolve(zenClassName.toLowerCase() + ".md"), out.toString().getBytes()); + + } catch (IOException e) { + e.printStackTrace(); + } + } + + } + + private void writeMethodList (StringBuilder out, List staticMethodList) { + + for (int j = 0; j < staticMethodList.size(); j++) { + + if (j > 0) { + out.append("\n"); + } + + this.writeMethod(out, staticMethodList.get(j).method, staticMethodList.get(j).annotation); + } + } + + private void writeMethod (StringBuilder out, Method method, ZenDocMethod annotation) { + + String methodName = method.getName(); + Class returnType = method.getReturnType(); + String returnTypeString = this.getSimpleTypeString(returnType); + + out.append("```java").append("\n"); + + if (Modifier.isStatic(method.getModifiers())) { + out.append("static "); + } + + // Method return type and name + out.append(returnTypeString).append(" ").append(methodName).append("("); + + Annotation[][] parameterAnnotations = method.getParameterAnnotations(); + Class[] types = method.getParameterTypes(); + ZenDocArg[] args = annotation.args(); + + if (types.length != args.length) { + throw new IllegalStateException("Wrong number of parameter names found for method: " + methodName); + } + + if (args.length > 0) { + out.append("\n"); + } + + int largest = 0; + String[] parameterStrings = new String[types.length]; + + // find the largest string + for (int k = 0; k < types.length; k++) { + + boolean optional = false; + boolean nullable = false; + + for (Annotation parameterAnnotation : parameterAnnotations[k]) { + + if (parameterAnnotation instanceof Optional) { + optional = true; + } + + if (parameterAnnotation instanceof ZenDocNullable) { + nullable = true; + } + } + + String optionalString = optional ? "@Optional " : ""; + String nullableString = nullable ? "@Nullable " : ""; + String typeString = this.getSimpleTypeString(types[k]); + String nameString = args[k].arg(); + + if (k < types.length - 1) { + parameterStrings[k] = " " + optionalString + nullableString + typeString + " " + nameString + ","; + + } else { + parameterStrings[k] = " " + optionalString + typeString + " " + nameString; + } + + if (parameterStrings[k].length() > largest) { + largest = parameterStrings[k].length(); + } + } + + for (int k = 0; k < parameterStrings.length; k++) { + parameterStrings[k] = StringUtils.rightPad(parameterStrings[k], largest); + out.append(parameterStrings[k]); + + if (!args[k].info().isEmpty()) { + out.append(" // ").append(args[k].info()); + } + + out.append("\n"); + } + + out.append(");\n"); + + out.append("```").append("\n\n"); + + String[] description = annotation.description(); + + if (description.length > 0) { + + for (String line : description) { + out.append(this.parse(line)); + } + } + + out.append("\n---\n\n"); + } + + private String parse (String line) { + + if (line.startsWith("@see")) { + String[] links = line.substring(4).trim().split(" "); + + StringBuilder sb = new StringBuilder("For more information, see:\n"); + + for (String link : links) { + sb.append(" * [").append(link).append("](").append(link).append(")\n"); + } + + return sb.toString(); + } + + return line + "\n"; + } + + private List getSortedMethodList (Method[] methods) { + + List methodList = new ArrayList<>(); + + for (int j = 0; j < methods.length; j++) { + ZenDocMethod annotation = methods[j].getDeclaredAnnotation(ZenDocMethod.class); + + if (annotation != null) { + methodList.add(new MethodAnnotationPair(methods[j], annotation)); + } + } + + methodList.sort(Comparator.comparingInt(o -> o.annotation.order())); + return methodList; + } + + private String getSimpleTypeString (Class type) { + + String result = type.getSimpleName(); + + if (result.startsWith("Zen")) { + result = result.substring(3); + + } else if (result.startsWith("String")) { + result = epicsquid.roots.util.StringHelper.lowercaseFirstLetter(result); + } + return result; + } + + private static class MethodAnnotationPair { + + public final Method method; + public final ZenDocMethod annotation; + + private MethodAnnotationPair (Method method, ZenDocMethod annotation) { + + this.method = method; + this.annotation = annotation; + } + } + +} diff --git a/src/main/java/com/aranaira/arcanearchives/util/zen/ZenDocMethod.java b/src/main/java/com/aranaira/arcanearchives/util/zen/ZenDocMethod.java new file mode 100644 index 000000000..93403940c --- /dev/null +++ b/src/main/java/com/aranaira/arcanearchives/util/zen/ZenDocMethod.java @@ -0,0 +1,17 @@ +package com.aranaira.arcanearchives.util.zen; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface ZenDocMethod { + + int order () default 0; + + String[] description () default {}; + + ZenDocArg[] args () default {}; +} diff --git a/src/main/java/com/aranaira/arcanearchives/util/zen/ZenDocNullable.java b/src/main/java/com/aranaira/arcanearchives/util/zen/ZenDocNullable.java new file mode 100644 index 000000000..e0f26727b --- /dev/null +++ b/src/main/java/com/aranaira/arcanearchives/util/zen/ZenDocNullable.java @@ -0,0 +1,12 @@ +package com.aranaira.arcanearchives.util.zen; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.PARAMETER) +@Retention(RetentionPolicy.RUNTIME) +public @interface ZenDocNullable { + // +} \ No newline at end of file