Skip to content

Commit d17582d

Browse files
committed
add package-info.java files for all CLI packages with detailed documentation
1 parent 51c2f81 commit d17582d

18 files changed

Lines changed: 1679 additions & 115 deletions

aether-datafixers-cli/src/main/java/de/splatgames/aether/datafixers/cli/AetherCli.java

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,28 @@
7676
public class AetherCli implements Callable<Integer> {
7777

7878
/**
79-
* Main entry point for the CLI.
79+
* Main entry point for the Aether Datafixers CLI application.
8080
*
81-
* @param args command-line arguments
81+
* <p>This method initializes the picocli {@link CommandLine} parser with the
82+
* root {@link AetherCli} command and executes it with the provided arguments.
83+
* The process exit code is set based on the command execution result.</p>
84+
*
85+
* <h3>Exit Codes</h3>
86+
* <ul>
87+
* <li>{@code 0} - Success (or help displayed)</li>
88+
* <li>{@code 1} - Error occurred during command execution</li>
89+
* <li>{@code 2} - Validation found files needing migration (validate command only)</li>
90+
* </ul>
91+
*
92+
* <h3>Configuration</h3>
93+
* <p>The CommandLine instance is configured with:</p>
94+
* <ul>
95+
* <li>Case-insensitive enum value parsing enabled</li>
96+
* </ul>
97+
*
98+
* @param args command-line arguments passed from the shell; may be empty
99+
* but should not be {@code null}
100+
* @see CommandLine#execute(String...)
82101
*/
83102
public static void main(final String[] args) {
84103
final int exitCode = new CommandLine(new AetherCli())
@@ -87,6 +106,20 @@ public static void main(final String[] args) {
87106
System.exit(exitCode);
88107
}
89108

109+
/**
110+
* Executes the root command when invoked without a subcommand.
111+
*
112+
* <p>When the CLI is invoked without specifying a subcommand (e.g., just
113+
* {@code aether-cli}), this method is called. It displays the help message
114+
* showing available commands and options.</p>
115+
*
116+
* <p>This behavior provides a user-friendly experience where running the
117+
* CLI without arguments shows how to use it, rather than doing nothing
118+
* or showing an error.</p>
119+
*
120+
* @return {@code 0} indicating successful execution (help was displayed)
121+
* @see CommandLine#usage(Object, java.io.PrintStream)
122+
*/
90123
@Override
91124
public Integer call() {
92125
// Print help when no subcommand is specified

aether-datafixers-cli/src/main/java/de/splatgames/aether/datafixers/cli/bootstrap/BootstrapLoader.java

Lines changed: 97 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,45 +22,95 @@
2222

2323
package de.splatgames.aether.datafixers.cli.bootstrap;
2424

25+
import com.google.common.base.Preconditions;
2526
import de.splatgames.aether.datafixers.api.bootstrap.DataFixerBootstrap;
2627
import org.jetbrains.annotations.NotNull;
2728

2829
import java.lang.reflect.Constructor;
2930
import java.util.ServiceLoader;
3031

3132
/**
32-
* Loads {@link DataFixerBootstrap} implementations.
33+
* Utility class for loading {@link DataFixerBootstrap} implementations at runtime.
34+
*
35+
* <p>This class provides multiple methods for discovering and instantiating
36+
* bootstrap implementations:</p>
3337
*
3438
* <h2>Discovery Methods</h2>
3539
* <ol>
36-
* <li>Explicit class name via {@code --bootstrap} option</li>
37-
* <li>ServiceLoader discovery via {@code META-INF/services}</li>
40+
* <li><b>Explicit class name</b> via {@link #load(String)} - Used when the CLI
41+
* receives the {@code --bootstrap} option with a fully qualified class name</li>
42+
* <li><b>ServiceLoader discovery</b> via {@link #discover()} or {@link #loadFirst()} -
43+
* Automatically discovers implementations registered in
44+
* {@code META-INF/services/de.splatgames.aether.datafixers.api.bootstrap.DataFixerBootstrap}</li>
3845
* </ol>
3946
*
47+
* <h2>Thread Safety</h2>
48+
* <p>All methods are thread-safe as they do not maintain any mutable state.</p>
49+
*
50+
* <h2>Example Usage</h2>
51+
* <pre>{@code
52+
* // Load by explicit class name
53+
* DataFixerBootstrap bootstrap = BootstrapLoader.load("com.example.MyBootstrap");
54+
*
55+
* // Auto-discover via ServiceLoader
56+
* DataFixerBootstrap bootstrap = BootstrapLoader.loadFirst();
57+
*
58+
* // Iterate over all discovered bootstraps
59+
* for (DataFixerBootstrap bootstrap : BootstrapLoader.discover()) {
60+
* System.out.println("Found: " + bootstrap.getClass().getName());
61+
* }
62+
* }</pre>
63+
*
4064
* @author Erik Pfoertner
65+
* @see DataFixerBootstrap
66+
* @see BootstrapLoadException
67+
* @see java.util.ServiceLoader
4168
* @since 0.3.0
4269
*/
4370
public final class BootstrapLoader {
4471

72+
/**
73+
* Private constructor to prevent instantiation.
74+
*
75+
* <p>This is a utility class with only static methods and should not be instantiated.</p>
76+
*/
4577
private BootstrapLoader() {
4678
// Utility class
4779
}
4880

4981
/**
50-
* Loads a bootstrap by its fully qualified class name.
82+
* Loads a bootstrap implementation by its fully qualified class name.
5183
*
52-
* <p>The class must:</p>
84+
* <p>This method uses reflection to load and instantiate the specified class.
85+
* The class must satisfy the following requirements:</p>
5386
* <ul>
54-
* <li>Implement {@link DataFixerBootstrap}</li>
55-
* <li>Have a public no-argument constructor</li>
87+
* <li>Must implement {@link DataFixerBootstrap}</li>
88+
* <li>Must have a public no-argument constructor</li>
89+
* <li>Must be accessible on the current thread's context class loader</li>
5690
* </ul>
5791
*
58-
* @param className the fully qualified class name
59-
* @return the bootstrap instance
60-
* @throws BootstrapLoadException if loading fails
92+
* <h3>Error Handling</h3>
93+
* <p>All reflection-related exceptions are wrapped in {@link BootstrapLoadException}
94+
* with descriptive error messages to aid debugging:</p>
95+
* <ul>
96+
* <li>{@code ClassNotFoundException} - Class not found on classpath</li>
97+
* <li>{@code NoSuchMethodException} - Missing public no-arg constructor</li>
98+
* <li>{@code ReflectiveOperationException} - Instantiation failure</li>
99+
* </ul>
100+
*
101+
* @param className the fully qualified class name (e.g., "com.example.MyBootstrap"),
102+
* must not be {@code null}
103+
* @return the instantiated bootstrap instance, never {@code null}
104+
* @throws BootstrapLoadException if the class cannot be found, does not implement
105+
* DataFixerBootstrap, lacks a no-arg constructor,
106+
* or instantiation fails
107+
* @see #discover()
108+
* @see #loadFirst()
61109
*/
62110
@NotNull
63111
public static DataFixerBootstrap load(@NotNull final String className) {
112+
Preconditions.checkNotNull(className, "className must not be null");
113+
64114
try {
65115
final Class<?> clazz = Class.forName(className);
66116

@@ -87,20 +137,52 @@ public static DataFixerBootstrap load(@NotNull final String className) {
87137
}
88138

89139
/**
90-
* Discovers bootstraps via ServiceLoader.
140+
* Discovers all bootstrap implementations via Java's {@link ServiceLoader} mechanism.
141+
*
142+
* <p>This method searches for implementations registered in:</p>
143+
* <pre>
144+
* META-INF/services/de.splatgames.aether.datafixers.api.bootstrap.DataFixerBootstrap
145+
* </pre>
91146
*
92-
* @return iterable of discovered bootstraps
147+
* <p>The returned iterable is lazy - implementations are instantiated on demand
148+
* as the iterator is consumed. Each call to this method creates a fresh
149+
* ServiceLoader instance.</p>
150+
*
151+
* <h3>Registration</h3>
152+
* <p>To register a bootstrap for discovery, create a file at:</p>
153+
* <pre>
154+
* src/main/resources/META-INF/services/de.splatgames.aether.datafixers.api.bootstrap.DataFixerBootstrap
155+
* </pre>
156+
* <p>containing the fully qualified class name of your implementation.</p>
157+
*
158+
* @return an iterable over all discovered bootstrap implementations;
159+
* may be empty if none are registered
160+
* @see ServiceLoader#load(Class)
161+
* @see #loadFirst()
162+
* @see #load(String)
93163
*/
94164
@NotNull
95165
public static Iterable<DataFixerBootstrap> discover() {
96166
return ServiceLoader.load(DataFixerBootstrap.class);
97167
}
98168

99169
/**
100-
* Loads the first available bootstrap from ServiceLoader.
170+
* Loads the first available bootstrap from the ServiceLoader.
171+
*
172+
* <p>This is a convenience method that returns the first bootstrap found
173+
* via {@link #discover()}. Useful when only one bootstrap is expected to
174+
* be registered in the application.</p>
175+
*
176+
* <p>The order in which bootstraps are discovered is not guaranteed and
177+
* depends on the classpath order. If multiple bootstraps are registered,
178+
* consider using {@link #discover()} to iterate over all of them or
179+
* {@link #load(String)} to specify an exact implementation.</p>
101180
*
102-
* @return the first bootstrap found
103-
* @throws BootstrapLoadException if no bootstrap is found
181+
* @return the first discovered bootstrap implementation, never {@code null}
182+
* @throws BootstrapLoadException if no bootstrap implementations are found
183+
* via ServiceLoader
184+
* @see #discover()
185+
* @see #load(String)
104186
*/
105187
@NotNull
106188
public static DataFixerBootstrap loadFirst() {
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright (c) 2025 Splatgames.de Software and Contributors
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a copy
5+
* of this software and associated documentation files (the "Software"), to deal
6+
* in the Software without restriction, including without limitation the rights
7+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+
* copies of the Software, and to permit persons to whom the Software is
9+
* furnished to do so, subject to the following conditions:
10+
*
11+
* The above copyright notice and this permission notice shall be included in all
12+
* copies or substantial portions of the Software.
13+
*
14+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20+
* SOFTWARE.
21+
*/
22+
23+
/**
24+
* Bootstrap loading utilities for the CLI.
25+
*
26+
* <p>This package provides utilities for loading
27+
* {@link de.splatgames.aether.datafixers.api.bootstrap.DataFixerBootstrap}
28+
* implementations at runtime via class name or ServiceLoader discovery.</p>
29+
*
30+
* <h2>Key Classes</h2>
31+
* <ul>
32+
* <li>{@link de.splatgames.aether.datafixers.cli.bootstrap.BootstrapLoader}
33+
* - Loads bootstraps by class name or via ServiceLoader</li>
34+
* <li>{@link de.splatgames.aether.datafixers.cli.bootstrap.BootstrapLoadException}
35+
* - Exception thrown when bootstrap loading fails</li>
36+
* </ul>
37+
*
38+
* <h2>Usage</h2>
39+
* <pre>{@code
40+
* // Load by fully qualified class name
41+
* DataFixerBootstrap bootstrap = BootstrapLoader.load("com.example.MyBootstrap");
42+
*
43+
* // Discover via ServiceLoader
44+
* DataFixerBootstrap bootstrap = BootstrapLoader.loadFirst();
45+
* }</pre>
46+
*
47+
* @see de.splatgames.aether.datafixers.cli.bootstrap.BootstrapLoader
48+
* @see de.splatgames.aether.datafixers.api.bootstrap.DataFixerBootstrap
49+
* @since 0.3.0
50+
*/
51+
package de.splatgames.aether.datafixers.cli.bootstrap;

0 commit comments

Comments
 (0)