From e4b7d1618846e7a951b4cfa9bb7d660569934161 Mon Sep 17 00:00:00 2001 From: TheRandomLabs Date: Sat, 6 Feb 2021 02:09:18 +1100 Subject: [PATCH] Command-line options and Markdown output --- .editorconfig | 29 --- README.md | 8 +- build.gradle | 13 +- .../BasicChangelogGenerator.java | 14 +- .../changeloggenerator/ChangelogEntry.java | 16 +- .../ChangelogGenerator.java | 21 ++- .../ChangelogGeneratorOptions.java | 125 +++++++++++++ .../changeloggenerator/Main.java | 54 ------ .../MarkdownChangelogGenerator.java | 165 ++++++++++++++++++ .../provider/ActuallyAdditionsProvider.java | 2 +- .../provider/BiomesOPlentyProvider.java | 5 +- .../provider/CurseChangelogProvider.java | 2 +- .../provider/MezzProvider.java | 5 +- 13 files changed, 355 insertions(+), 104 deletions(-) create mode 100644 src/main/java/com/therandomlabs/changeloggenerator/ChangelogGeneratorOptions.java delete mode 100644 src/main/java/com/therandomlabs/changeloggenerator/Main.java create mode 100644 src/main/java/com/therandomlabs/changeloggenerator/MarkdownChangelogGenerator.java diff --git a/.editorconfig b/.editorconfig index af1da2a..4cb81f0 100644 --- a/.editorconfig +++ b/.editorconfig @@ -15,7 +15,6 @@ ij_visual_guides = 100 ij_wrap_on_typing = false [*.css] -ij_visual_guides = none ij_css_align_closing_brace_with_properties = false ij_css_blank_lines_around_nested_selector = 1 ij_css_blank_lines_between_blocks = 1 @@ -35,19 +34,15 @@ ij_css_use_double_quotes = true ij_css_value_alignment = do_not_align [*.feature] -ij_visual_guides = none ij_gherkin_keep_indents_on_empty_lines = false [*.gsp] -ij_visual_guides = none ij_gsp_keep_indents_on_empty_lines = false [*.haml] -ij_visual_guides = none ij_haml_keep_indents_on_empty_lines = false [*.java] -ij_visual_guides = none ij_java_align_consecutive_assignments = false ij_java_align_consecutive_variable_declarations = false ij_java_align_group_field_declarations = false @@ -296,7 +291,6 @@ ij_java_wrap_first_method_in_call_chain = false ij_java_wrap_long_lines = false [*.less] -ij_visual_guides = none ij_less_align_closing_brace_with_properties = false ij_less_blank_lines_around_nested_selector = 1 ij_less_blank_lines_between_blocks = 1 @@ -316,10 +310,7 @@ ij_less_use_double_quotes = true ij_less_value_alignment = 0 [*.nbtt] -indent_style = space -max_line_length = 150 ij_continuation_indent_size = 4 -ij_visual_guides = none ij_nbtt_keep_indents_on_empty_lines = false ij_nbtt_space_after_colon = true ij_nbtt_space_after_comma = true @@ -329,7 +320,6 @@ ij_nbtt_spaces_within_brackets = false ij_nbtt_spaces_within_parentheses = false [*.sass] -ij_visual_guides = none ij_sass_align_closing_brace_with_properties = false ij_sass_blank_lines_around_nested_selector = 1 ij_sass_blank_lines_between_blocks = 1 @@ -349,7 +339,6 @@ ij_sass_use_double_quotes = true ij_sass_value_alignment = 0 [*.scss] -ij_visual_guides = none ij_scss_align_closing_brace_with_properties = false ij_scss_blank_lines_around_nested_selector = 1 ij_scss_blank_lines_between_blocks = 1 @@ -369,7 +358,6 @@ ij_scss_use_double_quotes = true ij_scss_value_alignment = 0 [*.styl] -ij_visual_guides = none ij_stylus_align_closing_brace_with_properties = false ij_stylus_blank_lines_around_nested_selector = 1 ij_stylus_blank_lines_between_blocks = 1 @@ -389,7 +377,6 @@ ij_stylus_use_double_quotes = true ij_stylus_value_alignment = 0 [.editorconfig] -ij_visual_guides = none ij_editorconfig_align_group_field_declarations = false ij_editorconfig_space_after_colon = false ij_editorconfig_space_after_comma = true @@ -398,7 +385,6 @@ ij_editorconfig_space_before_comma = false ij_editorconfig_spaces_around_assignment_operators = true [{*.ant,*.fxml,*.jhm,*.jnlp,*.jrxml,*.pom,*.rng,*.tld,*.wadl,*.wsdd,*.wsdl,*.xjb,*.xml,*.xsd,*.xsl,*.xslt,*.xul}] -ij_visual_guides = none ij_xml_align_attributes = true ij_xml_align_text = false ij_xml_attribute_wrap = normal @@ -418,7 +404,6 @@ ij_xml_text_wrap = normal ij_xml_use_custom_settings = false [{*.ats,*.ts}] -ij_visual_guides = none ij_typescript_align_imports = false ij_typescript_align_multiline_array_initializer_expression = false ij_typescript_align_multiline_binary_operation = false @@ -586,7 +571,6 @@ ij_typescript_while_on_new_line = false ij_typescript_wrap_comments = false [{*.bash,*.sh,*.zsh}] -ij_visual_guides = none ij_shell_binary_ops_start_line = false ij_shell_keep_column_alignment_padding = false ij_shell_minify_program = false @@ -595,7 +579,6 @@ ij_shell_switch_cases_indented = false [{*.cjs,*.js}] ij_continuation_indent_size = 4 -ij_visual_guides = none ij_javascript_align_imports = false ij_javascript_align_multiline_array_initializer_expression = false ij_javascript_align_multiline_binary_operation = false @@ -761,7 +744,6 @@ ij_javascript_wrap_comments = false [{*.cjsx,*.coffee}] ij_continuation_indent_size = 4 -ij_visual_guides = none ij_coffeescript_align_function_body = false ij_coffeescript_align_imports = false ij_coffeescript_align_multiline_array_initializer_expression = true @@ -856,11 +838,9 @@ ij_coffeescript_use_semicolon_after_statement = false ij_coffeescript_var_declaration_wrap = normal [{*.ft,*.vm,*.vsl}] -ij_visual_guides = none ij_vtl_keep_indents_on_empty_lines = false [{*.gant,*.gradle,*.groovy,*.gson,*.gy}] -ij_visual_guides = none ij_groovy_align_group_field_declarations = false ij_groovy_align_multiline_array_initializer_expression = false ij_groovy_align_multiline_assignment = false @@ -1040,7 +1020,6 @@ ij_groovy_while_on_new_line = false ij_groovy_wrap_long_lines = false [{*.gradle.kts,*.kt,*.kts,*.main.kts}] -ij_visual_guides = none ij_kotlin_align_in_columns_case_branch = false ij_kotlin_align_multiline_binary_operation = false ij_kotlin_align_multiline_extends_list = false @@ -1122,7 +1101,6 @@ ij_kotlin_wrap_expression_body_functions = 1 ij_kotlin_wrap_first_method_in_call_chain = false [{*.har,*.jsb2,*.jsb3,*.json,*.mcmeta,.babelrc,.eslintrc,.stylelintrc,bowerrc,jest.config,mcmod.info}] -ij_visual_guides = none ij_json_keep_blank_lines_in_code = 0 ij_json_keep_indents_on_empty_lines = false ij_json_keep_line_breaks = true @@ -1135,7 +1113,6 @@ ij_json_spaces_within_brackets = false ij_json_wrap_long_lines = false [{*.htm,*.html,*.ng,*.sht,*.shtm,*.shtml}] -ij_visual_guides = none ij_html_add_new_line_before_tags = body,div,p,form,h1,h2,h3 ij_html_align_attributes = true ij_html_align_text = false @@ -1164,29 +1141,23 @@ ij_html_text_wrap = normal ij_html_uniform_ident = false [{*.jsf,*.jsp,*.jspf,*.tag,*.tagf,*.xjsp}] -ij_visual_guides = none ij_jsp_jsp_prefer_comma_separated_import_list = false ij_jsp_keep_indents_on_empty_lines = false [{*.jspx,*.tagx}] -ij_visual_guides = none ij_jspx_keep_indents_on_empty_lines = false [{*.properties,spring.handlers,spring.schemas}] -ij_visual_guides = none ij_properties_align_group_field_declarations = false ij_properties_keep_blank_lines = false ij_properties_key_value_delimiter = equals ij_properties_spaces_around_key_value_delimiter = false [{*.toml,Cargo.lock,Gopkg.lock,Pipfile}] -indent_style = space -ij_visual_guides = none ij_toml_keep_indents_on_empty_lines = false [{*.yaml,*.yml}] indent_size = 2 -ij_visual_guides = none ij_yaml_keep_indents_on_empty_lines = false ij_yaml_keep_line_breaks = true ij_yaml_space_before_colon = true diff --git a/README.md b/README.md index 2849e90..fe4312c 100644 --- a/README.md +++ b/README.md @@ -15,8 +15,6 @@ Generates changelogs for CurseForge modpacks. All public-facing code is documented with Javadoc and (mostly) tested with JUnit. -## How to use? -1. Put the `manifest.json` from your old version in a folder and rename it to `old.json`. -2. Put the `manifest.json` from your new version in the same folder and rename it to `new.json`. -3. Execute the `ChangelogGenerator.jar` by double-clicking it in the same folder. -4. Wait for the `changelog.txt`. +## Usage + +Run `java -jar ChangelogGenerator-[version].jar --help` for command-line usage instructions. diff --git a/build.gradle b/build.gradle index 187b053..0e6095c 100644 --- a/build.gradle +++ b/build.gradle @@ -1,12 +1,12 @@ group = "com.therandomlabs.changeloggenerator" -version = "2.0.0-pre5" +version = "2.0.0-pre6" ext { commonGradleBranch = "master" - mainClass = "${group}.Main" + mainClass = "${group}.ChangelogGeneratorOptions" jacocoExcludes = [ "com.therandomlabs.changeloggenerator.ChangelogEntry", - "com.therandomlabs.changeloggenerator.Main" + "com.therandomlabs.changeloggenerator.ChangelogGeneratorOptions" ] } @@ -22,7 +22,14 @@ dependencies { api "com.github.TheRandomLabs:CurseAPI:master-SNAPSHOT" api "com.github.TheRandomLabs:CurseAPI-Minecraft:master-SNAPSHOT" + implementation "info.picocli:picocli:4.6.1" + annotationProcessor "info.picocli:picocli-codegen:4.6.1" + implementation "com.atlassian.commonmark:commonmark:0.13.1" testImplementation "com.github.TheRandomLabs:TRLUtils-IO:master-SNAPSHOT" } + +compileJava { + options.compilerArgs += "-Aproject=${project.group}/${project.name}" +} diff --git a/src/main/java/com/therandomlabs/changeloggenerator/BasicChangelogGenerator.java b/src/main/java/com/therandomlabs/changeloggenerator/BasicChangelogGenerator.java index c5f63ba..9db153b 100644 --- a/src/main/java/com/therandomlabs/changeloggenerator/BasicChangelogGenerator.java +++ b/src/main/java/com/therandomlabs/changeloggenerator/BasicChangelogGenerator.java @@ -52,7 +52,11 @@ public class BasicChangelogGenerator extends ChangelogGenerator { */ public static final BasicChangelogGenerator instance = new BasicChangelogGenerator(); - private static final Splitter LINE_SEPARATOR_SPLITTER = Splitter.on(System.lineSeparator()). + /** + * A {@link Splitter} that works on line separators. + * Empty strings are omitted, and results are trimmed. + */ + protected static final Splitter LINE_SEPARATOR_SPLITTER = Splitter.on(System.lineSeparator()). omitEmptyStrings(). trimResults(); @@ -67,7 +71,9 @@ protected BasicChangelogGenerator() { * {@inheritDoc} */ @Override - public String generate(CurseModpack oldModpack, CurseModpack newModpack) throws CurseException { + public String generate( + CurseModpack oldModpack, CurseModpack newModpack, ChangelogGeneratorOptions options + ) throws CurseException { final CurseFilesComparison comparison = CurseFilesComparison.of(oldModpack.files(), newModpack.files()); final StringBuilder builder = new StringBuilder(); @@ -167,7 +173,7 @@ protected void appendFiles( ); for (String projectName : projectNames) { - builder.append(System.lineSeparator()).append("- ").append(projectName); + builder.append(System.lineSeparator()).append("* ").append(projectName); } } @@ -183,7 +189,7 @@ protected void appendFiles( protected void appendChangelogEntries( StringBuilder builder, String title, Set> fileChanges ) throws CurseException { - builder.append(title).append(':'); + appendTitle(builder, title); final Map allEntries = new TreeMap<>(CurseAPI.parallelMap( fileChanges, diff --git a/src/main/java/com/therandomlabs/changeloggenerator/ChangelogEntry.java b/src/main/java/com/therandomlabs/changeloggenerator/ChangelogEntry.java index 5b0d48e..d63e712 100644 --- a/src/main/java/com/therandomlabs/changeloggenerator/ChangelogEntry.java +++ b/src/main/java/com/therandomlabs/changeloggenerator/ChangelogEntry.java @@ -24,6 +24,7 @@ package com.therandomlabs.changeloggenerator; import com.google.common.base.MoreObjects; +import okhttp3.HttpUrl; import org.jsoup.nodes.Element; /** @@ -32,19 +33,22 @@ public final class ChangelogEntry implements Comparable { private final Comparable comparable; private final String title; + private final HttpUrl url; private Element entry; /** - * Constructs a {@link ChangelogEntry} instance with the specified title and entry. + * Constructs a {@link ChangelogEntry} instance with the specified title, entry and URL. * * @param comparable a {@link Comparable} for use in sorting. Changelog entries for newer * files should be sorted first. * @param title a title. + * @param url a URL. * @param entry a changelog entry. */ - public ChangelogEntry(Comparable comparable, String title, Element entry) { + public ChangelogEntry(Comparable comparable, String title, HttpUrl url, Element entry) { this.comparable = comparable; this.title = title; + this.url = url; this.entry = entry; } @@ -94,6 +98,14 @@ public String title() { return title; } + /** + * Returns this {@link ChangelogEntry}'s URL. + * @return this {@link ChangelogEntry}'s URL. + */ + public HttpUrl url() { + return url; + } + /** * Returns this {@link ChangelogEntry}'s changelog entry. * diff --git a/src/main/java/com/therandomlabs/changeloggenerator/ChangelogGenerator.java b/src/main/java/com/therandomlabs/changeloggenerator/ChangelogGenerator.java index 2950206..b78e760 100644 --- a/src/main/java/com/therandomlabs/changeloggenerator/ChangelogGenerator.java +++ b/src/main/java/com/therandomlabs/changeloggenerator/ChangelogGenerator.java @@ -120,8 +120,25 @@ public ChangelogEntries getChangelogEntries( * @return the generated changelog. * @throws CurseException if an error occurs. */ - public abstract String generate(CurseModpack oldModpack, CurseModpack newModpack) - throws CurseException; + public String generate(CurseModpack oldModpack, CurseModpack newModpack) throws CurseException { + return generate(oldModpack, newModpack, new ChangelogGeneratorOptions()); + } + + /** + * Generates a changelog that details changes between an older and newer version of a + * CurseForge modpack. + * + * @param oldModpack a {@link CurseModpack} instance that represents an older version of a + * CurseForge modpack. + * @param newModpack a {@link CurseModpack} instance that represents a newer version of a + * CurseForge modpack. + * @param options ChangelogGenerator options. + * @return the generated changelog. + * @throws CurseException if an error occurs. + */ + public abstract String generate( + CurseModpack oldModpack, CurseModpack newModpack, ChangelogGeneratorOptions options + ) throws CurseException; /** * Registers the specified {@link ChangelogProvider} to this {@link ChangelogGenerator} diff --git a/src/main/java/com/therandomlabs/changeloggenerator/ChangelogGeneratorOptions.java b/src/main/java/com/therandomlabs/changeloggenerator/ChangelogGeneratorOptions.java new file mode 100644 index 0000000..1ec71c1 --- /dev/null +++ b/src/main/java/com/therandomlabs/changeloggenerator/ChangelogGeneratorOptions.java @@ -0,0 +1,125 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019-2020 TheRandomLabs + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.therandomlabs.changeloggenerator; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.concurrent.Callable; + +import com.therandomlabs.curseapi.minecraft.modpack.CurseModpack; +import okio.BufferedSink; +import okio.Okio; +import org.checkerframework.checker.nullness.qual.Nullable; +import picocli.CommandLine; + +/** + * Options for ChangelogGenerator. + * This class also contains the main method for the runnable ChangelogGenerator application. + */ +@CommandLine.Command( + mixinStandardHelpOptions = true, version = ChangelogGenerator.VERSION, + description = "Generates changelogs for CurseForge modpacks." +) +public final class ChangelogGeneratorOptions implements Callable { + @CommandLine.Option( + names = {"-o", "--old-manifest"}, + description = "The old modpack manifest. \"old.json\" by default." + ) + @Nullable + public Path oldManifest; + + @CommandLine.Option( + names = {"-n", "--new-manifest"}, + description = "The new modpack manifest. \"new.json\" by default." + ) + @Nullable + public Path newManifest; + + @CommandLine.Option( + names = {"-O", "--output"}, + description = "The changelog output location. \"changelog.txt\" or " + + "\"changelog.md\" by default." + ) + @Nullable + public Path output; + + @CommandLine.Option( + names = {"-l", "--lines"}, + description = "The maximum number of lines to display in a changelog entry." + ) + public int maxEntryLineCount; + + @CommandLine.Option( + names = {"-m", "--markdown"}, + description = "Generate a Markdown changelog." + ) + public boolean markdown; + + /** + * {@inheritDoc} + */ + @Override + public Integer call() throws Exception { + if (oldManifest == null) { + oldManifest = Paths.get("old.json"); + } + + if (newManifest == null) { + newManifest = Paths.get("new.json"); + } + + if (output == null) { + output = Paths.get(markdown ? "changelog.md" : "changelog.txt"); + } + + if (maxEntryLineCount < 0) { + throw new IllegalArgumentException("Cannot display negative number of lines"); + } + + final CurseModpack oldModpack = CurseModpack.fromJSON(oldManifest); + final CurseModpack newModpack = CurseModpack.fromJSON(newManifest); + final ChangelogGenerator generator = + markdown ? MarkdownChangelogGenerator.instance : BasicChangelogGenerator.instance; + final String changelog = generator.generate(oldModpack, newModpack, this); + + try (BufferedSink sink = Okio.buffer(Okio.sink(output))) { + sink.writeUtf8(changelog).writeUtf8(System.lineSeparator()); + } + + return 0; + } + + /** + * The main method for the runnable ChangelogGenerator application. + * + * @param args the command-line arguments. + */ + public static void main(String[] args) { + System.exit( + new CommandLine(new ChangelogGeneratorOptions()). + registerConverter(Path.class, Paths::get). + execute(args) + ); + } +} diff --git a/src/main/java/com/therandomlabs/changeloggenerator/Main.java b/src/main/java/com/therandomlabs/changeloggenerator/Main.java deleted file mode 100644 index d649e9b..0000000 --- a/src/main/java/com/therandomlabs/changeloggenerator/Main.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2019-2020 TheRandomLabs - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package com.therandomlabs.changeloggenerator; - -import java.nio.file.Paths; - -import com.therandomlabs.curseapi.minecraft.modpack.CurseModpack; -import okio.BufferedSink; -import okio.Okio; - -/** - * The entry point for the runnable ChangelogGenerator application. - */ -public final class Main { - private Main() {} - - /** - * The main method for the runnable ChangelogGenerator application. - * - * @param args the command-line arguments. - * @throws Exception if an error occurs. - */ - public static void main(String[] args) throws Exception { - //TODO - final CurseModpack oldModpack = CurseModpack.fromJSON(Paths.get("old.json")); - final CurseModpack newModpack = CurseModpack.fromJSON(Paths.get("new.json")); - final String changelog = BasicChangelogGenerator.instance.generate(oldModpack, newModpack); - - try (BufferedSink sink = Okio.buffer(Okio.sink(Paths.get("changelog.txt")))) { - sink.writeUtf8(changelog).writeUtf8(System.lineSeparator()); - } - } -} diff --git a/src/main/java/com/therandomlabs/changeloggenerator/MarkdownChangelogGenerator.java b/src/main/java/com/therandomlabs/changeloggenerator/MarkdownChangelogGenerator.java new file mode 100644 index 0000000..60c6115 --- /dev/null +++ b/src/main/java/com/therandomlabs/changeloggenerator/MarkdownChangelogGenerator.java @@ -0,0 +1,165 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019-2020 TheRandomLabs + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.therandomlabs.changeloggenerator; + +import java.util.Collections; +import java.util.Optional; +import java.util.Set; +import java.util.TreeSet; +import java.util.stream.Collectors; + +import com.therandomlabs.curseapi.CurseException; +import com.therandomlabs.curseapi.file.BasicCurseFile; +import com.therandomlabs.curseapi.file.CurseFile; +import com.therandomlabs.curseapi.file.CurseFileChange; +import com.therandomlabs.curseapi.file.CurseFiles; +import com.therandomlabs.curseapi.minecraft.modpack.CurseModpack; +import com.therandomlabs.curseapi.project.CurseProject; +import com.therandomlabs.curseapi.util.JsoupUtils; + +/** + * A variant of {@link BasicChangelogGenerator} that generates a Markdown changelog. + */ +public class MarkdownChangelogGenerator extends BasicChangelogGenerator { + /** + * The singleton instance of {@link MarkdownChangelogGenerator}. + */ + public static final MarkdownChangelogGenerator instance = new MarkdownChangelogGenerator(); + + /** + * Constructs a {@link MarkdownChangelogGenerator}. + */ + protected MarkdownChangelogGenerator() { + //Default constructor. + } + + /** + * {@inheritDoc} + */ + @Override + protected void appendTitle(StringBuilder builder, String title) { + builder.append("## ").append(title).append(System.lineSeparator()); + } + + /** + * {@inheritDoc} + */ + @Override + protected void appendModpackVersions( + StringBuilder builder, CurseModpack oldModpack, CurseModpack newModpack + ) throws CurseException { + builder.append("# ").append(oldModpack.name()).append(' ').append(oldModpack.version()). + append("⟶").append(newModpack.name()).append(' ').append(newModpack.version()); + } + + /** + * {@inheritDoc} + */ + @Override + protected void appendFiles( + StringBuilder builder, String title, CurseFiles files + ) throws CurseException { + appendTitle(builder, title); + + final Set projectNames = files.parallelMap( + file -> Optional.ofNullable(file.project()). + map(project -> "[" + project.name() + "](" + project.url() + ")"). + orElse("Deleted project"), + Collectors.toCollection(TreeSet::new) + ); + + for (String projectName : projectNames) { + builder.append(System.lineSeparator()).append("* ").append(projectName); + } + } + + /** + * {@inheritDoc} + */ + @SuppressWarnings("Duplicates") + @Override + protected void appendChangelogEntries( + StringBuilder builder, String projectName, ChangelogEntries changelogEntries + ) throws CurseException { + final CurseFileChange fileChange = changelogEntries.fileChange(); + + final CurseFile oldFile = fileChange.oldCurseFile(); + final String oldDisplayName; + + if (oldFile == null) { + oldDisplayName = "Archived file"; + } else { + oldDisplayName = "[" + oldFile.displayName() + "](" + oldFile.url() + ")"; + } + + final CurseFile newFile = fileChange.newCurseFile(); + final String newDisplayName; + + if (newFile == null) { + newDisplayName = "Archived file"; + } else { + newDisplayName = "[" + newFile.displayName() + "](" + newFile.url() + ")"; + } + + builder.append("### ").append( + Optional.ofNullable(fileChange.project()). + map(project -> "[" + project.name() + "](" + project.url() + ")"). + orElse("Deleted project") + ).append(" (").append(oldDisplayName).append("⟶").append(newDisplayName).append(")"); + + if (changelogEntries.entries().isEmpty()) { + builder.append(System.lineSeparator()); + return; + } + + for (ChangelogEntry entry : changelogEntries.entries()) { + builder.append(System.lineSeparator()).append(System.lineSeparator()).append("#### ["). + append(entry.title()).append("](").append(entry.url()).append(")"). + append(System.lineSeparator()); + + Iterable entryLines; + + if (JsoupUtils.isEmpty(entry.entry())) { + entryLines = Collections.singleton("No changelog available."); + } else { + entryLines = LINE_SEPARATOR_SPLITTER.split(JsoupUtils.getPlainText(entry.entry())); + } + + for (String line : entryLines) { + builder.append(System.lineSeparator()); + + if (!line.isEmpty()) { + //For consistency, we change "- " and "+ " to "* " at line beginnings. + if ((line.startsWith("- ") || line.startsWith("+ ")) && line.length() > 2) { + builder.append("* ").append(line.substring(2)); + } else { + builder.append(line); + } + } + } + + builder.append(System.lineSeparator()); + } + } +} diff --git a/src/main/java/com/therandomlabs/changeloggenerator/provider/ActuallyAdditionsProvider.java b/src/main/java/com/therandomlabs/changeloggenerator/provider/ActuallyAdditionsProvider.java index 0a62a02..832adc9 100644 --- a/src/main/java/com/therandomlabs/changeloggenerator/provider/ActuallyAdditionsProvider.java +++ b/src/main/java/com/therandomlabs/changeloggenerator/provider/ActuallyAdditionsProvider.java @@ -98,7 +98,7 @@ public SortedSet getChangelog( if (newVersionFound && "ul".equals(element.tagName())) { changelog.add(new ChangelogEntry( - index++, "Actually Additions " + currentVersion, element + index++, "Actually Additions " + currentVersion, CHANGELOG_URL, element )); } } diff --git a/src/main/java/com/therandomlabs/changeloggenerator/provider/BiomesOPlentyProvider.java b/src/main/java/com/therandomlabs/changeloggenerator/provider/BiomesOPlentyProvider.java index 2051884..dd0b01f 100644 --- a/src/main/java/com/therandomlabs/changeloggenerator/provider/BiomesOPlentyProvider.java +++ b/src/main/java/com/therandomlabs/changeloggenerator/provider/BiomesOPlentyProvider.java @@ -63,7 +63,8 @@ public SortedSet getChangelog( } final CurseFiles files = ChangelogProvider.getFilesBetweenInclusive(fileChange); - final String fullChangelog = files.first().changelogPlainText(); + final CurseFile newFile = files.first(); + final String fullChangelog = newFile.changelogPlainText(); if (!fullChangelog.startsWith("Build: ")) { return null; @@ -111,7 +112,7 @@ public SortedSet getChangelog( } changelog.add(new ChangelogEntry( - index++, "Biomes O' Plenty " + currentVersion, currentEntry + index++, "Biomes O' Plenty " + currentVersion, newFile.url(), currentEntry )); currentEntry = new Element("div"); currentList = null; diff --git a/src/main/java/com/therandomlabs/changeloggenerator/provider/CurseChangelogProvider.java b/src/main/java/com/therandomlabs/changeloggenerator/provider/CurseChangelogProvider.java index 3b93c32..3485a27 100644 --- a/src/main/java/com/therandomlabs/changeloggenerator/provider/CurseChangelogProvider.java +++ b/src/main/java/com/therandomlabs/changeloggenerator/provider/CurseChangelogProvider.java @@ -59,7 +59,7 @@ public SortedSet getChangelog( gameVersionGroups(fileChange.get(CurseFile::gameVersionGroups)). apply(files); return files.parallelMap( - file -> new ChangelogEntry(file, file.displayName(), file.changelog()), + file -> new ChangelogEntry(file, file.displayName(), file.url(), file.changelog()), Collectors.toCollection(TreeSet::new) ); } diff --git a/src/main/java/com/therandomlabs/changeloggenerator/provider/MezzProvider.java b/src/main/java/com/therandomlabs/changeloggenerator/provider/MezzProvider.java index eea6f8a..5029c8e 100644 --- a/src/main/java/com/therandomlabs/changeloggenerator/provider/MezzProvider.java +++ b/src/main/java/com/therandomlabs/changeloggenerator/provider/MezzProvider.java @@ -29,6 +29,7 @@ import com.therandomlabs.changeloggenerator.ChangelogEntry; import com.therandomlabs.curseapi.file.BasicCurseFile; import com.therandomlabs.curseapi.file.CurseFileChange; +import okhttp3.HttpUrl; import org.checkerframework.checker.nullness.qual.Nullable; import org.jsoup.nodes.Element; @@ -108,6 +109,8 @@ public SortedSet getChangelog( return changelog; */ - return ImmutableSortedSet.of(new ChangelogEntry(0, "Placeholder", new Element("p"))); + return ImmutableSortedSet.of(new ChangelogEntry( + 0, "Placeholder", HttpUrl.get("https://placeholder.com"), new Element("p") + )); } }