docs: add Javadoc for core classes

- Added Javadoc comments to `Site`, `Server`, `Feed`, `BlogBuilder`, `TemplateRenderer`, and `Post` classes.
- Documented constructors, methods, and class-level descriptions.
- Enhanced clarity around parameters, return values, and exceptions.

Signed-off-by: Minecon724 <git@m724.eu>
This commit is contained in:
Minecon724 2025-02-08 15:50:32 +01:00
parent d5f6eaf487
commit b339d6d239
Signed by: Minecon724
GPG key ID: 3CCC4D267742C8E8
6 changed files with 148 additions and 37 deletions

View file

@ -15,6 +15,11 @@ import java.util.ArrayList;
import java.util.Comparator; import java.util.Comparator;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/**
* The {@code BlogBuilder} class facilitates building a static blog by managing templates,
* assets, posts, and rendering output files. It uses a Git repository as the
* source for the blog's content and configuration.
*/
public class BlogBuilder { public class BlogBuilder {
private final Git git; private final Git git;
private final Path workingDirectory; private final Path workingDirectory;
@ -26,6 +31,11 @@ public class BlogBuilder {
private Path outputDirectory; private Path outputDirectory;
private boolean renderDrafts = false; private boolean renderDrafts = false;
/**
* Constructs a {@link BlogBuilder} instance using the provided Git repository.
*
* @param git the Git repository to be used for the blog.
*/
public BlogBuilder(Git git) { public BlogBuilder(Git git) {
this.git = git; this.git = git;
@ -34,6 +44,14 @@ public class BlogBuilder {
this.outputDirectory = workingDirectory.resolve("generated_out"); this.outputDirectory = workingDirectory.resolve("generated_out");
} }
/**
* Creates a new {@link BlogBuilder} instance for the specified working directory.
* The directory is expected to be a Git repository.
*
* @param workingDirectory the root path of the blog, which must contain a Git repository.
* @return a {@link BlogBuilder} instance
* @throws IOException if there is an error accessing the Git repository
*/
public static BlogBuilder fromPath(Path workingDirectory) throws IOException { public static BlogBuilder fromPath(Path workingDirectory) throws IOException {
var repository = new RepositoryBuilder() var repository = new RepositoryBuilder()
.setGitDir(workingDirectory.resolve(".git").toFile()) .setGitDir(workingDirectory.resolve(".git").toFile())
@ -45,16 +63,34 @@ public class BlogBuilder {
return new BlogBuilder(git); return new BlogBuilder(git);
} }
/**
* Sets the directory to be used for templates in the blog build process.
*
* @param templateDirectory the path to the template directory
* @return the current instance of {@link BlogBuilder}
*/
public BlogBuilder templateDirectory(Path templateDirectory) { public BlogBuilder templateDirectory(Path templateDirectory) {
this.templateDirectory = templateDirectory; this.templateDirectory = templateDirectory;
return this; return this;
} }
/**
* Sets the directory where the output files will be saved during the blog build process.
*
* @param outputDirectory the path representing the directory for output files
* @return the current instance of {@link BlogBuilder}
*/
public BlogBuilder outputDirectory(Path outputDirectory) { public BlogBuilder outputDirectory(Path outputDirectory) {
this.outputDirectory = outputDirectory; this.outputDirectory = outputDirectory;
return this; return this;
} }
/**
* Configures whether drafts should be rendered in the blog build process.
*
* @param renderDrafts a boolean flag indicating whether to include drafts in the rendering process.
* @return the current instance of {@link BlogBuilder}
*/
public BlogBuilder renderDrafts(boolean renderDrafts) { public BlogBuilder renderDrafts(boolean renderDrafts) {
this.renderDrafts = renderDrafts; this.renderDrafts = renderDrafts;
return this; return this;
@ -72,6 +108,11 @@ public class BlogBuilder {
Files.createDirectory(outputDirectory); Files.createDirectory(outputDirectory);
} }
/**
* Builds the blog by generating templates, copying assets, and rendering posts.
*
* @throws IOException if an I/O error occurs
*/
public void build() throws IOException { public void build() throws IOException {
if (site == null) if (site == null)
this.site = Site.fromConfig(workingDirectory.resolve("site-config.json")); this.site = Site.fromConfig(workingDirectory.resolve("site-config.json"));

View file

@ -10,19 +10,41 @@ import java.nio.charset.StandardCharsets;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
/**
* The {@code Server} class represents a basic HTTP server designed to serve files
* from a specified webroot directory.
*/
public class Server implements HttpHandler { public class Server implements HttpHandler {
private final Path webroot; private final Path webroot;
private InetSocketAddress listenAddress; private InetSocketAddress listenAddress;
/**
* Constructs a {@link Server} instance with the specified webroot directory
* and the specified listening address.
*
* @param webroot the path to the webroot directory from which files will be served
* @param listenAddress the address and port the server will listen on
*/
public Server(Path webroot, InetSocketAddress listenAddress) { public Server(Path webroot, InetSocketAddress listenAddress) {
this.webroot = webroot; this.webroot = webroot;
this.listenAddress = listenAddress; this.listenAddress = listenAddress;
} }
/**
* Constructs a {@link Server} instance with the specified webroot directory.
* The server will bind to the localhost address with a dynamically chosen port.
*
* @param webroot the path to the webroot directory from which files will be served
*/
public Server(Path webroot) { public Server(Path webroot) {
this(webroot, new InetSocketAddress("localhost", 0)); this(webroot, new InetSocketAddress("localhost", 0));
} }
/**
* Starts an HTTP server on the specified listen address.
*
* @throws IOException if an I/O error occurs during server initialization
*/
public void start() throws IOException { public void start() throws IOException {
var server = HttpServer.create(listenAddress, 0); var server = HttpServer.create(listenAddress, 0);
server.createContext("/", this); server.createContext("/", this);
@ -33,6 +55,9 @@ public class Server implements HttpHandler {
this.listenAddress = server.getAddress(); this.listenAddress = server.getAddress();
} }
/**
* Attempts to open the default web browser and navigate to the server's URL.
*/
public void openBrowser() { public void openBrowser() {
try { try {
var process = Runtime.getRuntime().exec(new String[] { "xdg-open", "http://" + listenAddress.getHostString() + ":" + listenAddress.getPort() }); var process = Runtime.getRuntime().exec(new String[] { "xdg-open", "http://" + listenAddress.getHostString() + ":" + listenAddress.getPort() });

View file

@ -16,9 +16,18 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
/**
* The {@code TemplateRenderer} class is responsible for rendering dynamic HTML templates
* using the Pebble templating engine.
*/
public class TemplateRenderer { public class TemplateRenderer {
private final PebbleTemplate indexTemplate, articleTemplate; private final PebbleTemplate indexTemplate, articleTemplate;
/**
* Constructs a TemplateRenderer instance for rendering templates from the specified directory.
*
* @param templateDirectory the root directory containing the template files
*/
public TemplateRenderer(Path templateDirectory) { public TemplateRenderer(Path templateDirectory) {
var loader = new FileLoader(); var loader = new FileLoader();
loader.setPrefix(templateDirectory.toString()); loader.setPrefix(templateDirectory.toString());
@ -61,6 +70,14 @@ public class TemplateRenderer {
this.articleTemplate = pebbleEngine.getTemplate("article_template"); this.articleTemplate = pebbleEngine.getTemplate("article_template");
} }
/**
* Renders the index page using this template.
*
* @param site the {@link Site} this index is for
* @param posts the {@link Post}s to be included in the index page
* @return the rendered index HTML page as a string
* @throws IOException if an error occurs during the template evaluation
*/
public String renderIndex(Site site, ArrayList<Post> posts) throws IOException { public String renderIndex(Site site, ArrayList<Post> posts) throws IOException {
Map<String, Object> context = Map.of( Map<String, Object> context = Map.of(
"site", site, "site", site,
@ -73,6 +90,14 @@ public class TemplateRenderer {
return writer.toString(); return writer.toString();
} }
/**
* Renders the content of a post using this template.
*
* @param site the {@link Site} containing the post
* @param post the {@link Post} to be rendered
* @return the rendered post HTML page as a string
* @throws IOException if an error occurs during template evaluation
*/
public String renderPost(Site site, Post post) throws IOException { public String renderPost(Site site, Post post) throws IOException {
Map<String, Object> context = Map.of( Map<String, Object> context = Map.of(
"site", site, "site", site,

View file

@ -4,6 +4,13 @@ import java.time.format.DateTimeFormatter;
import java.util.List; import java.util.List;
public class Feed { public class Feed {
/**
* Generates an RSS feed XML string for a given website and its list of blog posts.
*
* @param site the {@code Site} object representing the website for which the RSS feed is generated
* @param posts the list of {@code Post} objects representing the blog posts to include in the RSS feed
* @return a {@code String} containing the formatted RSS feed in XML
*/
public static String generateRss(Site site, List<Post> posts) { public static String generateRss(Site site, List<Post> posts) {
var content = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?><rss version=\"2.0\">"; var content = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?><rss version=\"2.0\">";
content += "<channel><title>%s</title><link>%s</link>".formatted(site.name(), site.baseUrl()); content += "<channel><title>%s</title><link>%s</link>".formatted(site.name(), site.baseUrl());

View file

@ -10,6 +10,21 @@ import java.time.*;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
/**
* The {@code Post} class represents a blog post with various attributes including metadata and content.
*
* @param slug A unique identifier for the post derived from the file name.
* @param title The title of the post.
* @param summary A brief summary of the post content.
* @param draft Indicates whether the post is marked as a draft or published.
* @param revisions The number of revisions the post has undergone in version control.
* @param createdBy The name of the author who created the post.
* @param createdAt The timestamp of when the post was first created.
* @param modifiedBy The name of the author who last modified the post.
* @param modifiedAt The timestamp of the last modification to the post.
* @param custom A map of custom properties or metadata associated with the post.
* @param rawContent The raw content of the post, which <em>currently</em> is usually HTML.
*/
public record Post( public record Post(
String slug, String slug,
String title, String title,
@ -25,11 +40,17 @@ public record Post(
Map<String, String> custom, Map<String, String> custom,
String rawContent String rawContent
) { ) {
// this is because we'll be not only supporting html /**
public String htmlContent() { * Creates a {@link Post} instance by reading and parsing the content of a post file.
return rawContent; * <p>
} * The method extracts metadata properties, content, and versioning information
* based on the Git history of the file.
*
* @param git the Git repository used to retrieve versioning and commit information
* @param path the relative path to the file within the "posts" directory
* @return a {@link Post} object populated with data extracted from the specified file
* @throws IOException if an error occurs during file reading
*/
public static Post fromFile(Git git, Path path) throws IOException { public static Post fromFile(Git git, Path path) throws IOException {
/* read properties before filtering */ /* read properties before filtering */
@ -106,4 +127,13 @@ public record Post(
return new Post(slug, title, summary, draft, revisions, createdBy, createdAt, modifiedBy, modifiedAt, custom, content); return new Post(slug, title, summary, draft, revisions, createdBy, createdAt, modifiedBy, modifiedAt, custom, content);
} }
/**
* Retrieves the raw HTML content associated with the post.
*
* @return the raw HTML content as a string
*/
public String htmlContent() {
return rawContent;
}
} }

View file

@ -8,12 +8,27 @@ import java.nio.file.Path;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
/**
* The {@code Site} class represents a website and its configuration.
*
* @param name the name of the site
* @param baseUrl the base URL of the site
* @param custom a map of additional custom properties
*/
public record Site( public record Site(
String name, String name,
String baseUrl, String baseUrl,
Map<String, Object> custom Map<String, Object> custom
) { ) {
/**
* Creates a {@link Site} object by reading and parsing the configuration file at the specified path.<br>
* The configuration file must be a JSON file.
*
* @param path the path to the configuration file
* @return a {@link Site} object initialized with the data from the configuration file
* @throws IOException if an error occurs during file reading
*/
public static Site fromConfig(Path path) throws IOException { public static Site fromConfig(Path path) throws IOException {
var content = Files.readString(path); var content = Files.readString(path);
var json = new JSONObject(content); var json = new JSONObject(content);
@ -41,36 +56,4 @@ public record Site(
name, baseUrl, custom name, baseUrl, custom
); );
} }
/* remained from gson
private static Map<String, Object> deep(JsonObject jsonObject) {
return jsonObject.entrySet().stream()
.map(e -> new AbstractMap.SimpleEntry<>(e.getKey(), eToO(e.getValue())))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}
private static Object eToO(JsonElement element) {
if (element.isJsonArray()) {
return element.getAsJsonArray().asList().stream().map(Site::eToO).toList();
} else if (element.isJsonObject()) {
return deep(element.getAsJsonObject());
} else if (element.isJsonPrimitive()) {
try {
return element.getAsBoolean();
} catch (IllegalStateException e) { }
try {
return element.getAsLong();
} catch (NumberFormatException e) { }
try {
return element.getAsDouble();
} catch (NumberFormatException e) { }
// TODO
}
return null;
}*/
} }