Skip to content

Commit

Permalink
docs: added JavaDocs
Browse files Browse the repository at this point in the history
  • Loading branch information
quintesse committed Jul 10, 2024
1 parent 032384e commit ff1e5e6
Show file tree
Hide file tree
Showing 9 changed files with 208 additions and 24 deletions.
68 changes: 63 additions & 5 deletions src/main/java/org/codejive/jpm/Jpm.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,11 @@
import java.nio.file.Path;
import java.util.*;
import org.codejive.jpm.json.AppInfo;
import org.codejive.jpm.util.FileUtils;
import org.codejive.jpm.util.ResolverUtils;
import org.codejive.jpm.util.SearchUtils;
import org.codejive.jpm.util.SyncStats;
import org.codejive.jpm.util.*;
import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.resolution.DependencyResolutionException;

/** The class implementing all the jpm command actions. */
public class Jpm {
private final Path directory;
private final boolean noLinks;
Expand All @@ -20,41 +18,81 @@ private Jpm(Path directory, boolean noLinks) {
this.noLinks = noLinks;
}

/**
* Create a new {@link Builder} instance for the {@link Jpm} class.
*
* @return A new {@link Builder} instance.
*/
public static Builder builder() {
return new Builder();
}

/** Builder class for the {@link Jpm} class. */
public static class Builder {
private Path directory;
private boolean noLinks;

private Builder() {}

/**
* Set the target directory to use for the jpm commands.
*
* @param directory The target directory.
* @return The builder instance for chaining.
*/
public Builder directory(Path directory) {
this.directory = directory;
return this;
}

/**
* Set whether to create symbolic links or not.
*
* @param noLinks Whether to create symbolic links or not.
* @return The builder instance for chaining.
*/
public Builder noLinks(boolean noLinks) {
this.noLinks = noLinks;
return this;
}

/**
* Builds the {@link Jpm} instance.
*
* @return A {@link Jpm} instance.
*/
public Jpm build() {
return new Jpm(directory, noLinks);
}
}

/**
* Copies the given artifacts to the target directory.
*
* @param artifactNames The artifacts to copy.
* @param sync Whether to sync the target directory or not.
* @return An instance of {@link SyncStats} containing the statistics of the copy operation.
* @throws IOException If an error occurred during the copy operation.
* @throws DependencyResolutionException If an error occurred during the dependency resolution.
*/
public SyncStats copy(String[] artifactNames, boolean sync)
throws IOException, DependencyResolutionException {
List<Path> files = ResolverUtils.resolveArtifactPaths(artifactNames);
return FileUtils.syncArtifacts(files, directory, noLinks, !sync);
}

/**
* Searches for artifacts matching the given pattern.
*
* @param artifactPattern The pattern to search for.
* @param count The maximum number of results to return.
* @return An array of artifact names matching the given pattern.
* @throws IOException If an error occurred during the search.
*/
public String[] search(String artifactPattern, int count) throws IOException {
List<Artifact> artifacts = new ArrayList<>();
int max = count <= 0 || count > 200 ? 200 : count;
SearchUtils.SearchResult result = SearchUtils.findArtifacts(artifactPattern, max);
SearchResult result = SearchUtils.findArtifacts(artifactPattern, max);
while (result != null) {
artifacts.addAll(result.artifacts);
result = count <= 0 ? SearchUtils.findNextArtifacts(result) : null;
Expand All @@ -66,6 +104,17 @@ private static String artifactGav(Artifact artifact) {
return artifact.getGroupId() + ":" + artifact.getArtifactId() + ":" + artifact.getVersion();
}

/**
* Installs the given artifacts to the target directory while also registering them as
* dependencies in the app.json file in the current directory. If no artifacts are given, all
* dependencies in the app.json file will be installed. NB: "installation" in this context
* basically means sync-copying the artifacts to the target directory.
*
* @param artifactNames The artifacts to install.
* @return An instance of {@link SyncStats} containing the statistics of the install operation.
* @throws IOException If an error occurred during the install operation.
* @throws DependencyResolutionException If an error occurred during the dependency resolution.
*/
public SyncStats install(String[] artifactNames)
throws IOException, DependencyResolutionException {
AppInfo appInfo = AppInfo.read();
Expand All @@ -88,6 +137,15 @@ public SyncStats install(String[] artifactNames)
}
}

/**
* Returns the paths of the given artifacts. If no artifacts are given, the paths for all
* dependencies in the app.json file will be returned instead.
*
* @param artifactNames The artifacts to get the paths for.
* @return A list of paths.
* @throws DependencyResolutionException If an error occurred during the dependency resolution.
* @throws IOException If an error occurred during the operation.
*/
public List<Path> path(String[] artifactNames)
throws DependencyResolutionException, IOException {
AppInfo appInfo = AppInfo.read();
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/org/codejive/jpm/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import picocli.CommandLine.Option;
import picocli.CommandLine.Parameters;

/** Main class for the jpm command line tool. */
@Command(
name = "jpm",
mixinStandardHelpOptions = true,
Expand Down Expand Up @@ -204,6 +205,11 @@ private static void printStats(SyncStats stats) {
stats.copied, stats.updated, stats.deleted);
}

/**
* Main entry point for the jpm command line tool.
*
* @param args The command line arguments.
*/
public static void main(String... args) {
new CommandLine(new Main()).execute(args);
}
Expand Down
23 changes: 23 additions & 0 deletions src/main/java/org/codejive/jpm/json/AppInfo.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,35 @@
import java.util.Map;
import java.util.TreeMap;

/**
* Represents the contents of an app.json file. There are methods for reading and writing instances
* from/to files.
*/
public class AppInfo {
private Map<String, Object> json;
public Map<String, String> dependencies = new TreeMap<>();

/** The official name of the app.json file. */
public static final String APP_INFO_FILE = "app.json";

/**
* Returns the dependencies as an array of strings in the format "groupId:artifactId:version".
*
* @return An array of strings
*/
public String[] getDependencyGAVs() {
return dependencies.entrySet().stream()
.map(e -> e.getKey() + ":" + e.getValue())
.toArray(String[]::new);
}

/**
* Reads the app.json file in the current directory and returns its content as an AppInfo
* object.
*
* @return An instance of AppInfo
* @throws IOException if an error occurred while reading or parsing the file
*/
public static AppInfo read() throws IOException {
Path prjJson = Path.of(APP_INFO_FILE);
AppInfo appInfo = new AppInfo();
Expand All @@ -41,6 +58,12 @@ public static AppInfo read() throws IOException {
return appInfo;
}

/**
* Writes the AppInfo object to the app.json file in the current directory.
*
* @param appInfo The AppInfo object to write
* @throws IOException if an error occurred while writing the file
*/
public static void write(AppInfo appInfo) throws IOException {
Path prjJson = Path.of(APP_INFO_FILE);
try (Writer out = Files.newBufferedWriter(prjJson)) {
Expand Down
12 changes: 12 additions & 0 deletions src/main/java/org/codejive/jpm/util/FileUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,19 @@
import java.util.List;
import java.util.Set;

/** Utility class for file operations. */
public class FileUtils {

/**
* Synchronizes a list of artifacts with a target directory.
*
* @param artifacts list of artifacts to synchronize
* @param directory target directory
* @param noLinks if true, copy artifacts instead of creating symbolic links
* @param noDelete if true, do not delete artifacts that are no longer needed
* @return An instance of {@link SyncStats} with statistics about the synchronization
* @throws IOException if an error occurred during the synchronization
*/
public static SyncStats syncArtifacts(
List<Path> artifacts, Path directory, boolean noLinks, boolean noDelete)
throws IOException {
Expand Down
24 changes: 24 additions & 0 deletions src/main/java/org/codejive/jpm/util/ResolverUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,42 @@
import org.eclipse.aether.resolution.DependencyResult;
import org.eclipse.aether.util.artifact.JavaScopes;

/** Utility class for resolving Maven artifacts. */
public class ResolverUtils {
/**
* Resolves the paths of the given artifacts. Handles parsing and resolving of artifacts and
* extracts their paths.
*
* @param artifactNames the artifacts to resolve as an array of strings in the format
* "groupId:artifactId:version"
* @return the paths of the resolved artifacts
* @throws DependencyResolutionException if an error occurs while resolving the artifacts
*/
public static List<Path> resolveArtifactPaths(String[] artifactNames)
throws DependencyResolutionException {
List<Artifact> artifacts = parseArtifacts(artifactNames);
List<ArtifactResult> resolvedArtifacts = resolveArtifacts(artifacts);
return resolvedArtifacts.stream().map(ar -> ar.getArtifact().getFile().toPath()).toList();
}

/**
* Parses the given artifact names into a list of {@link Artifact} instances.
*
* @param artifactNames the artifact names to parse as an array of strings in the format
* "groupId:artifactId:version"
* @return a list of {@link Artifact} instances
*/
public static List<Artifact> parseArtifacts(String[] artifactNames) {
return Arrays.stream(artifactNames).map(DefaultArtifact::new).collect(Collectors.toList());
}

/**
* Resolves the given artifacts.
*
* @param artifacts the artifacts to resolve as a list of {@link Artifact} instances
* @return the resolved artifacts as a list of {@link ArtifactResult} instances
* @throws DependencyResolutionException if an error occurs while resolving the artifacts
*/
public static List<ArtifactResult> resolveArtifacts(List<Artifact> artifacts)
throws DependencyResolutionException {
List<Dependency> dependencies =
Expand Down
41 changes: 41 additions & 0 deletions src/main/java/org/codejive/jpm/util/SearchResult.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package org.codejive.jpm.util;

import java.util.Collections;
import java.util.List;
import org.eclipse.aether.artifact.Artifact;

/** Hold the result of a search while also functioning as a kind of bookmark for paging purposes. */
public class SearchResult {
/** The artifacts that matched the search query. */
public final List<? extends Artifact> artifacts;

/** The search query that produced this result. */
public final String query;

/** The index of the first artifact in this result relative to the total result set. */
public final int start;

/** The maximum number of results to return */
public final int count;

/** The total number of artifacts that matched the search query. */
public final int total;

/**
* Create a new search result.
*
* @param artifacts The artifacts that matched the search query.
* @param query The search query that produced this result.
* @param start The index of the first artifact in this result relative to the total result set.
* @param count The maximum number of results to return.
* @param total The total number of artifacts that matched the search query.
*/
public SearchResult(
List<? extends Artifact> artifacts, String query, int start, int count, int total) {
this.artifacts = Collections.unmodifiableList(artifacts);
this.query = query;
this.start = start;
this.count = count;
this.total = total;
}
}
45 changes: 26 additions & 19 deletions src/main/java/org/codejive/jpm/util/SearchUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,38 +6,41 @@
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URLEncoder;
import java.util.Collections;
import java.util.List;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.artifact.DefaultArtifact;

/** Utility class for searching Maven artifacts. */
public class SearchUtils {

public static class SearchResult {
public final List<? extends Artifact> artifacts;
public final String query;
public final int start;
public final int count;
public final int total;

public SearchResult(
List<? extends Artifact> artifacts, String query, int start, int count, int total) {
this.artifacts = Collections.unmodifiableList(artifacts);
this.query = query;
this.start = start;
this.count = count;
this.total = total;
}
}

/**
* Find artifacts matching the given pattern. This will return the first page of results. If the
* pattern to search for is a simple name (there are no colons in the string), the search will
* match any part of an artifact's group or name. If there's a single colon, the search will
* match any part of the group id and artifact id separately. If there are two colons, the
* search will match the group id and artifact id exactly, and will return the artifact's
* versions.
*
* @param artifactPattern The pattern to search for.
* @param count The maximum number of results to return.
* @return The search result as an instance of {@link SearchResult}.
* @throws IOException If an error occurred during the search.
*/
public static SearchResult findArtifacts(String artifactPattern, int count) throws IOException {
return select(artifactPattern, 0, count);
}

/**
* Find the next page of artifacts. This takes a {@link SearchResult} returned by a previous
* call to {@link #findArtifacts(String, int)} and returns the next page of results.
*
* @param prevResult The previous search result.
* @return The next search result as an instance of {@link SearchResult}.
* @throws IOException If an error occurred during the search.
*/
public static SearchResult findNextArtifacts(SearchResult prevResult) throws IOException {
if (prevResult.start + prevResult.count >= prevResult.total) {
return null;
Expand All @@ -51,10 +54,14 @@ private static SearchResult select(String query, int start, int count) throws IO
String[] parts = query.split(":", -1);
String finalQuery;
if (parts.length >= 3) {
// Exact group/artifact match for retrieving versions
finalQuery = "g:%s AND a:%s".formatted(parts[0], parts[1]);
} else if (parts.length == 2) {
// Partial group/artifact match, we will filter the results
// to remove those that match an inverted artifact/group
finalQuery = "%s AND %s".formatted(parts[0], parts[1]);
} else {
// Simple partial match
finalQuery = query;
}
String searchUrl =
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/org/codejive/jpm/util/SyncStats.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
package org.codejive.jpm.util;

/** Utility class for keeping track of synchronization statistics. */
public class SyncStats {
/** The number of new artifacts that were copied. */
public int copied;

/** The number of existing artifacts that were updated. */
public int updated;

/** The number of existing artifacts that were deleted. */
public int deleted;
}
Loading

0 comments on commit ff1e5e6

Please sign in to comment.