diff --git a/pkl-core/src/main/java/org/pkl/core/module/FileResolver.java b/pkl-core/src/main/java/org/pkl/core/module/FileResolver.java
index 87c4f2179..3bf008833 100644
--- a/pkl-core/src/main/java/org/pkl/core/module/FileResolver.java
+++ b/pkl-core/src/main/java/org/pkl/core/module/FileResolver.java
@@ -1,5 +1,5 @@
/*
- * Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
+ * Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,13 +26,29 @@
import java.util.List;
import org.pkl.core.util.IoUtils;
+/** Utilities for inspecting file-system paths while resolving module glob imports. */
public final class FileResolver {
private FileResolver() {}
+ /**
+ * Returns the immediate, non-symbolic-link children of the directory identified by {@code
+ * baseUri}.
+ *
+ *
Returns an empty list if {@code baseUri} does not exist or does not identify a directory.
+ *
+ * @throws IOException if the directory cannot be read
+ */
public static List listElements(URI baseUri) throws IOException {
return listElements(IoUtils.pathOf(baseUri));
}
+ /**
+ * Returns the immediate, non-symbolic-link children of {@code path}.
+ *
+ * Returns an empty list if {@code path} does not exist or is not a directory.
+ *
+ * @throws IOException if the directory cannot be read
+ */
public static List listElements(Path path) throws IOException {
try (var stream = Files.newDirectoryStream(path)) {
var ret = new ArrayList();
@@ -49,10 +65,12 @@ public static List listElements(Path path) throws IOException {
}
}
+ /** Returns whether the file-system path identified by {@code elementUri} exists. */
public static boolean hasElement(URI elementUri) {
return Files.exists(IoUtils.pathOf(elementUri));
}
+ /** Returns whether {@code path} exists. */
public static boolean hasElement(Path path) {
return Files.exists(path);
}
diff --git a/pkl-core/src/main/java/org/pkl/core/module/ModulePathResolver.java b/pkl-core/src/main/java/org/pkl/core/module/ModulePathResolver.java
index 2b8ab8b27..8e4242775 100644
--- a/pkl-core/src/main/java/org/pkl/core/module/ModulePathResolver.java
+++ b/pkl-core/src/main/java/org/pkl/core/module/ModulePathResolver.java
@@ -57,10 +57,19 @@ public final class ModulePathResolver implements AutoCloseable {
private static final ModulePathResolver EMPTY = new ModulePathResolver(Collections.emptyList());
+ /** Returns a resolver whose module path contains no entries. */
public static ModulePathResolver empty() {
return EMPTY;
}
+ /**
+ * Creates a resolver for the given module path entries.
+ *
+ * Each entry can be a directory, JAR file, or ZIP file. Entries are searched in iteration
+ * order; if more than one entry contains the same path, the first entry wins.
+ *
+ * @param modulePath the module path entries to search
+ */
public ModulePathResolver(Iterable modulePath) {
this.modulePath = modulePath;
}
@@ -103,6 +112,13 @@ private Map getFileCache() throws IOException {
}
}
+ /**
+ * Resolves a {@code modulepath:} URI to the path containing its source.
+ *
+ * @throws FileNotFoundException if the module path contains no matching file
+ * @throws IOException if a module path entry cannot be read
+ * @throws IllegalStateException if this resolver has been closed
+ */
public Path resolve(URI uri) throws IOException {
var modulePath = getModulePath(uri);
var result = getFileCache().get(modulePath);
@@ -111,6 +127,12 @@ public Path resolve(URI uri) throws IOException {
throw new FileNotFoundException();
}
+ /**
+ * Returns whether the module path contains the element identified by {@code elementUri}.
+ *
+ * @throws UncheckedIOException if a module path entry cannot be read
+ * @throws IllegalStateException if this resolver has been closed
+ */
public boolean hasElement(URI elementUri) {
var path = elementUri.getPath();
try {
@@ -121,6 +143,11 @@ public boolean hasElement(URI elementUri) {
}
}
+ /**
+ * Closes file systems opened for JAR and ZIP entries.
+ *
+ * Calling this method more than once has no effect.
+ */
@Override
public void close() {
synchronized (lock) {
diff --git a/pkl-core/src/main/java/org/pkl/core/module/PathElement.java b/pkl-core/src/main/java/org/pkl/core/module/PathElement.java
index 31dd8d471..568a93bca 100644
--- a/pkl-core/src/main/java/org/pkl/core/module/PathElement.java
+++ b/pkl-core/src/main/java/org/pkl/core/module/PathElement.java
@@ -24,7 +24,9 @@
import org.jspecify.annotations.Nullable;
import org.pkl.core.util.EconomicMaps;
+/** Describes a child of a hierarchical module or resource path. */
public class PathElement {
+ /** Orders files before directories, then orders elements lexicographically by name. */
public static final Comparator comparator =
(o1, o2) -> {
if (o1.isDirectory && !o2.isDirectory) {
@@ -39,19 +41,23 @@ public class PathElement {
private final boolean isDirectory;
+ /** Creates an element whose type cannot be inspected and is therefore treated as a file. */
public static PathElement opaque(String name) {
return new PathElement(name, false);
}
+ /** Creates an element with the given name and directory status. */
public PathElement(String name, boolean isDirectory) {
this.name = name;
this.isDirectory = isDirectory;
}
+ /** Returns this element's name relative to its parent. */
public String getName() {
return name;
}
+ /** Returns an equivalent element with {@code name}, or this element if its name is unchanged. */
public PathElement withName(String name) {
if (name.equals(this.name)) {
return this;
@@ -59,6 +65,7 @@ public PathElement withName(String name) {
return new PathElement(name, isDirectory);
}
+ /** Returns whether this element represents a directory. */
public boolean isDirectory() {
return isDirectory;
}
@@ -80,13 +87,19 @@ public String toString() {
return "PathElement{" + "name='" + name + '\'' + ", isDirectory=" + isDirectory + '}';
}
+ /** A path element that stores its descendants as a tree. */
public static final class TreePathElement extends PathElement {
private final EconomicMap children = EconomicMaps.create();
+ /** Creates a tree element with the given name and directory status. */
public TreePathElement(String name, boolean isDirectory) {
super(name, isDirectory);
}
+ /**
+ * Adds {@code child} unless a child named {@code name} already exists, and returns the stored
+ * child.
+ */
public TreePathElement putIfAbsent(String name, TreePathElement child) {
children.putIfAbsent(name, child);
return children.get(name);
@@ -113,10 +126,12 @@ public TreePathElement putIfAbsent(String name, TreePathElement child) {
return getElement(Path.of(basePath));
}
+ /** Returns this element's children, keyed by name. */
public EconomicMap getChildren() {
return children;
}
+ /** Returns a snapshot of this element's child values. */
public List getChildrenValues() {
var ret = new ArrayList(children.size());
for (var elem : EconomicMaps.getValues(children)) {
diff --git a/pkl-core/src/main/java/org/pkl/core/module/ProjectDependenciesManager.java b/pkl-core/src/main/java/org/pkl/core/module/ProjectDependenciesManager.java
index 3b9e3c2bf..a643ead2d 100644
--- a/pkl-core/src/main/java/org/pkl/core/module/ProjectDependenciesManager.java
+++ b/pkl-core/src/main/java/org/pkl/core/module/ProjectDependenciesManager.java
@@ -40,9 +40,12 @@
import org.pkl.core.util.IoUtils;
import org.pkl.core.util.json.Json.JsonParseException;
+/** Resolves a project's declared package dependencies against its lock file. */
public final class ProjectDependenciesManager {
+ /** The conventional name of a Pkl project file. */
public static final String PKL_PROJECT_FILENAME = "PklProject";
+ /** The conventional name of a Pkl project's dependency lock file. */
public static final String PKL_PROJECT_DEPS_FILENAME = "PklProject.deps.json";
private final DeclaredDependencies declaredDependencies;
@@ -66,6 +69,13 @@ public final class ProjectDependenciesManager {
private final Object lock = new Object();
+ /**
+ * Creates a dependency manager for a project.
+ *
+ * @param declaredDependencies the dependencies declared by the project
+ * @param moduleResolver the resolver used to load the project's dependency lock file
+ * @param securityManager the security manager used when loading the dependency lock file
+ */
public ProjectDependenciesManager(
DeclaredDependencies declaredDependencies,
ModuleResolver moduleResolver,
@@ -77,6 +87,7 @@ public ProjectDependenciesManager(
this.securityManager = securityManager;
}
+ /** Returns whether {@code uri} is located within this project. */
public boolean hasUri(URI uri) {
return projectBaseUri.getScheme().equals(uri.getScheme())
&& Objects.equals(projectBaseUri.getAuthority(), uri.getAuthority())
@@ -164,6 +175,7 @@ private Map doBuildResolvedDependenciesForProject(
}
// `ensureDependenciesInitialized` makes `myDependencies` safe to access
+ /** Returns the project's direct dependencies, keyed by their declared names. */
@SuppressWarnings({"FieldAccessNotGuarded", "GuardedBy"})
public Map getDependencies() {
ensureDependenciesInitialized();
@@ -172,6 +184,7 @@ public Map getDependencies() {
}
// `ensureDependenciesInitialized` makes `localPackageDependencies` safe to access
+ /** Returns whether {@code packageUri} identifies a local dependency of this project. */
@SuppressWarnings({"FieldAccessNotGuarded", "GuardedBy"})
public boolean isLocalPackage(PackageUri packageUri) {
ensureDependenciesInitialized();
@@ -179,6 +192,7 @@ public boolean isLocalPackage(PackageUri packageUri) {
}
// `ensureDependenciesInitialized` makes `localPackageDependencies` safe to access
+ /** Returns the dependencies of the local package identified by {@code packageUri}. */
@SuppressWarnings({"FieldAccessNotGuarded", "GuardedBy"})
public Map getLocalPackageDependencies(PackageUri packageUri) {
ensureDependenciesInitialized();
@@ -188,6 +202,13 @@ public Map getLocalPackageDependencies(PackageUri packageUri
return dep;
}
+ /**
+ * Resolves a package's declared dependencies against this project's lock file.
+ *
+ * @param packageUri the package whose dependencies are being resolved
+ * @param dependencyMetadata the package's published dependency metadata
+ * @return dependencies keyed by their names in the package metadata
+ */
public Map getResolvedDependenciesForPackage(
PackageUri packageUri, DependencyMetadata dependencyMetadata) {
synchronized (lock) {
@@ -217,10 +238,12 @@ public Map getResolvedDependenciesForPackage(
}
}
+ /** Returns this project's declared dependencies. */
public DeclaredDependencies getDeclaredDependencies() {
return declaredDependencies;
}
+ /** Returns the locked dependency matching {@code packageUri}. */
public Dependency getResolvedDependency(PackageUri packageUri) {
var dep = getProjectDeps().get(CanonicalPackageUri.fromPackageUri(packageUri));
if (dep == null) {
@@ -229,14 +252,17 @@ public Dependency getResolvedDependency(PackageUri packageUri) {
return dep;
}
+ /** Returns the base URI used to resolve paths within this project. */
public URI getProjectBaseUri() {
return projectBaseUri;
}
+ /** Returns the URI of this project's dependency lock file. */
public URI getProjectDepsFileUri() {
return IoUtils.resolve(projectBaseUri, PKL_PROJECT_DEPS_FILENAME);
}
+ /** Returns the URI of this project's project file. */
public URI getProjectFileUri() {
return declaredDependencies.projectFileUri();
}
diff --git a/pkl-core/src/main/java/org/pkl/core/module/ResolvedModuleKey.java b/pkl-core/src/main/java/org/pkl/core/module/ResolvedModuleKey.java
index 2228d6458..ddeee95e7 100644
--- a/pkl-core/src/main/java/org/pkl/core/module/ResolvedModuleKey.java
+++ b/pkl-core/src/main/java/org/pkl/core/module/ResolvedModuleKey.java
@@ -1,5 +1,5 @@
/*
- * Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
+ * Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@
/** SPI for identifying a resolved module and loading its source code. */
public interface ResolvedModuleKey {
+ /** Returns the unresolved module key from which this key was resolved. */
ModuleKey getOriginal();
/**
diff --git a/pkl-core/src/main/java/org/pkl/core/module/ResolvedModuleKeys.java b/pkl-core/src/main/java/org/pkl/core/module/ResolvedModuleKeys.java
index 3d2685c69..21607289a 100644
--- a/pkl-core/src/main/java/org/pkl/core/module/ResolvedModuleKeys.java
+++ b/pkl-core/src/main/java/org/pkl/core/module/ResolvedModuleKeys.java
@@ -34,6 +34,7 @@ private ResolvedModuleKeys() {}
* loaded from that file path and cached using the given URI as cache key.
*
* @param nofollow if true, the file will be opened with {@link LinkOption#NOFOLLOW_LINKS}.
+ * @return a resolved module key backed by {@code path}
*/
public static ResolvedModuleKey file(ModuleKey original, URI uri, Path path, boolean nofollow) {
return new FileKey(original, uri, path, nofollow);
@@ -42,6 +43,8 @@ public static ResolvedModuleKey file(ModuleKey original, URI uri, Path path, boo
/**
* Creates a resolved module key backed by the given file path. The resulting module will be
* loaded from that file path and cached using the given URI as cache key.
+ *
+ * @return a resolved module key backed by {@code path}
*/
public static ResolvedModuleKey file(ModuleKey original, URI uri, Path path) {
return new FileKey(original, uri, path, false);
@@ -50,6 +53,8 @@ public static ResolvedModuleKey file(ModuleKey original, URI uri, Path path) {
/**
* Creates a resolved module key backed by the given URL. The resulting module will be loaded from
* that URL and cached using the given URI as cache key.
+ *
+ * @return a resolved module key backed by {@code url}
*/
public static ResolvedModuleKey url(ModuleKey original, URI uri, URL url) {
return new Url(original, uri, url);
@@ -58,6 +63,8 @@ public static ResolvedModuleKey url(ModuleKey original, URI uri, URL url) {
/**
* Creates a resolved module key backed by the given source code. If {@code cached} is {@code
* true}, the resulting module will be cached using the given URI as cache key.
+ *
+ * @return a resolved module key backed by {@code sourceText}
*/
public static ResolvedModuleKey virtual(
ModuleKey original, URI uri, String sourceText, boolean cached) {
@@ -67,6 +74,8 @@ public static ResolvedModuleKey virtual(
/**
* Creates a resolved module key that behaves like {@code delegate}, except with {@code original}
* as its original module key.
+ *
+ * @return a resolved module key backed by {@code delegate}
*/
public static ResolvedModuleKey delegated(ResolvedModuleKey delegate, ModuleKey original) {
return new Delegated(delegate, original);
diff --git a/pkl-core/src/main/java/org/pkl/core/module/package-info.java b/pkl-core/src/main/java/org/pkl/core/module/package-info.java
index 42eeedb06..b04acad46 100644
--- a/pkl-core/src/main/java/org/pkl/core/module/package-info.java
+++ b/pkl-core/src/main/java/org/pkl/core/module/package-info.java
@@ -1,3 +1,9 @@
+/**
+ * APIs for resolving Pkl module URIs and loading module source code.
+ *
+ * Custom module schemes can be implemented with {@link org.pkl.core.module.ModuleKeyFactory},
+ * {@link org.pkl.core.module.ModuleKey}, and {@link org.pkl.core.module.ResolvedModuleKey}.
+ */
@NullMarked
package org.pkl.core.module;