diff --git a/api/src/org/labkey/api/data/SQLFragment.java b/api/src/org/labkey/api/data/SQLFragment.java index f282a38d25c..bc4f6ded6c7 100644 --- a/api/src/org/labkey/api/data/SQLFragment.java +++ b/api/src/org/labkey/api/data/SQLFragment.java @@ -50,9 +50,22 @@ import static org.labkey.api.query.ExprColumn.STR_TABLE_ALIAS; -/** - * Holds both the SQL text and JDBC parameter values to use during invocation. - */ +/// A composable SQL builder that pairs SQL text with its JDBC parameter values, ensuring +/// they travel together through query construction. Implements [Appendable] and +/// [CharSequence] for fluent assembly of SQL statements. +/// +/// Provides type-safe `appendValue()` methods for inlining literals of common +/// types (integers, strings, dates, GUIDs, etc.) and `add()` methods for binding +/// JDBC `?` parameters. Fragments can be composed via `append(SQLFragment)` to +/// merge both SQL text and parameter lists. +/// +/// Supports Common Table Expressions (CTEs) through +/// [#addCommonTableExpression(SqlDialect, Object, String, SQLFragment)], which +/// manages deduplication, token substitution, and correct ordering of WITH clauses +/// across nested and combined fragments. +/// +/// Enforces basic SQL injection safeguards by rejecting unmatched quotes and +/// semicolons in appended text. public class SQLFragment implements Appendable, CharSequence { public static final String FEATUREFLAG_DISABLE_STRICT_CHECKS = "sqlfragment-disable-strict-checks"; diff --git a/api/src/org/labkey/api/dataiterator/DataIterator.java b/api/src/org/labkey/api/dataiterator/DataIterator.java index 57a5cb09ff2..702431c5f19 100644 --- a/api/src/org/labkey/api/dataiterator/DataIterator.java +++ b/api/src/org/labkey/api/dataiterator/DataIterator.java @@ -26,14 +26,20 @@ import java.util.function.Supplier; import java.util.stream.Stream; -/** - * User: matthewb - * Date: May 16, 2011 - * - * Sticking with the jdbc style 1-based indexing - * - * Column 0 is the row number, used for error reporting - */ +/// A cursor-style interface for iterating over tabular row data, analogous to +/// [java.sql.ResultSet] but designed for LabKey's ETL and import pipelines. +/// Extends [DataIteratorBuilder] (so it can return itself) and [Closeable]. +/// +/// Columns use JDBC-style **1-based indexing**. Column 0 is reserved for the +/// row number, used for error reporting. Call `next()` to advance the cursor, +/// then `getSupplier(int)` to obtain a `Supplier` for retrieving column values. +/// Prefer `getSupplier(int)` over `get(int)`. Values may be `null`, +/// a real value, or an `MvFieldWrapper` for missing-value indicators. +/// +/// Implementations are typically composed into a processing pipeline where each +/// `DataIterator` wraps another, adding transformations such as coercion, +/// validation, deduplication, or auditing (see [WrapperDataIterator], +/// [CoerceDataIterator], [DetailedAuditLogDataIterator], etc.). public interface DataIterator extends DataIteratorBuilder, Closeable { String ROWNUMBER_COLUMNNAME = "_rowNumber"; // TODO change to something like DataIterator.class().getName() + "#_rowNumber" diff --git a/api/src/org/labkey/api/jsp/JspTest.java b/api/src/org/labkey/api/jsp/JspTest.java index 27558384a20..7b8456f58ad 100644 --- a/api/src/org/labkey/api/jsp/JspTest.java +++ b/api/src/org/labkey/api/jsp/JspTest.java @@ -18,9 +18,34 @@ import org.labkey.api.util.TestContext; -/** - * Base class for junit tests implemented in a JSP - */ +/// Base class for junit tests implemented in a JSP. +/// +/// Example minimal JSP test (MyTestCase.jsp): +/// +/// ```jsp +/// <%@ page import="org.junit.After" %> +/// <%@ page import="org.junit.Before" %> +/// <%@ page import="org.junit.Test" %> +/// <%@ page import="static org.junit.Assert.assertTrue" %> +/// <%@ page extends="org.labkey.api.jsp.JspTest.BVT" %> +/// <%! +/// @Before +/// public void setUp() +/// { +/// } +/// +/// @After +/// public void tearDown() +/// { +/// } +/// +/// @Test +/// public void testExample() +/// { +/// assertTrue(true); +/// } +/// %> +/// ``` public abstract class JspTest extends JspContext { protected final TestContext testContext; diff --git a/api/src/org/labkey/api/util/DOM.java b/api/src/org/labkey/api/util/DOM.java index 167043204a8..1feb02da7b1 100644 --- a/api/src/org/labkey/api/util/DOM.java +++ b/api/src/org/labkey/api/util/DOM.java @@ -34,12 +34,80 @@ import static org.labkey.api.util.HtmlString.unsafe; import static org.labkey.api.util.PageFlowUtil.filter; -/** - * Builders to safely create properly encoded HTML. - * Many of the element classes take a var-arg Object array to represent their children, - * see {@link DOM#appendBody(Appendable, Object)} for details about what's supported, and {@link DomTestCase} for - * example usages. - */ +/// A Java DSL for safely building properly encoded HTML. All text content is automatically +/// HTML-encoded via [PageFlowUtil#filter], preventing XSS vulnerabilities. Uppercase static +/// factory methods (`DIV()`, `TABLE()`, `SPAN()`, etc.) return [Renderable] objects that nest +/// to compose an HTML tree. +/// +/// ## Recommended imports +/// +/// ```java +/// import static org.labkey.api.util.DOM.*; +/// import static org.labkey.api.util.DOM.Attribute.*; +/// ``` +/// +/// ## Elements, attributes, and classes +/// +/// Elements accept an optional [Attributes] argument followed by var-arg children. +/// Use [#at(Attribute, Object, Object...)] for attributes, [#cl(String...)] for CSS classes, +/// and chain them together via the [_Attributes] builder: +/// +/// ```java +/// DIV(cl("container"), +/// H1(id("title"), "Hello World"), +/// A(at(href, "https://example.com").cl("nav-link").data("section", "main"), "Click here"), +/// P(at(style, "color:red"), "Styled text"), +/// TR(cl(isOdd, "labkey-alternate-row", "labkey-row"), TD("cell")) +/// ) +/// ``` +/// +/// ## Dynamic content +/// +/// Children can be arrays, [Iterable]s, or [Stream]s of supported types: +/// +/// ```java +/// // Stream of options in a select +/// SELECT(id("users"), Stream.of("alice", "bob", "charles").map(DOM::OPTION)) +/// +/// // Building rows in a loop +/// List rows = new ArrayList<>(); +/// for (var item : items) +/// rows.add(TR(TD(item.getName()), TD(String.valueOf(item.getCount())))); +/// TABLE(cl("labkey-data-region"), THEAD(TR(TH("Name"), TH("Count"))), TBODY(rows)) +/// ``` +/// +/// ## LabKey extensions ([LK]) +/// +/// ```java +/// LK.FORM(at(method, "POST", action, url), INPUT(at(type, "text", name, "q")), INPUT(at(type, "submit"))) +/// LK.CHECKBOX(at(name, "enabled")) // checkbox + hidden field marker +/// LK.FA("plus-square") // font-awesome icon +/// LK.ERRORS(bindingResult) // render Spring validation errors +/// ``` +/// +/// ## Rendering to output +/// +/// ```java +/// HtmlString html = DOM.createHtmlFragment(DIV("hello"), BR(), DIV("world")); // to HtmlString +/// myRenderable.appendTo(out); // to Appendable (JspWriter, StringBuilder) +/// String raw = DIV("test").renderToString(); // to String +/// ``` +/// +/// ## Template support +/// +/// Use [#renderTemplate(Renderable, Appendable)] with [#BODY_PLACE_HOLDER] to split rendering +/// around a body that is not yet available (e.g. for JSP `BodyTagSupport` or `WebPartFrame`): +/// +/// ```java +/// Renderable frame = DIV(cl("frame"), DIV(cl("header"), "Title"), BODY_PLACE_HOLDER, DIV(cl("footer"), "Footer")); +/// HtmlString endMarkup = DOM.renderTemplate(frame, out); +/// // ... render body content to out ... +/// out.write(endMarkup.toString()); +/// ``` +/// +/// Supported child types: `null` (ignored), [CharSequence] (HTML-encoded), [Number]/[Boolean] +/// (rendered as-is), [Renderable], [HtmlString] (no encoding), arrays, [Iterable]s, and [Stream]s +/// of these types. See [#appendBody(Appendable, Object)] for details and [DomTestCase] for more examples. public class DOM { public interface Attributes extends Iterable> {} diff --git a/api/src/org/labkey/api/util/HtmlString.java b/api/src/org/labkey/api/util/HtmlString.java index 2075039ac44..c9859a5fa84 100644 --- a/api/src/org/labkey/api/util/HtmlString.java +++ b/api/src/org/labkey/api/util/HtmlString.java @@ -26,6 +26,21 @@ import java.io.Serializable; import java.util.Objects; +/// An immutable wrapper around a [String] known to contain safe, properly encoded HTML. Instances are +/// created via the [#of] factory methods, which HTML-encode their input, or via [#unsafe], +/// which trusts the caller to supply pre-encoded markup. +/// +/// `HtmlString` is the fundamental unit of safe HTML in the LabKey API. It implements +/// [DOM.Renderable], so it can be used as a child element in the [DOM] HTML-building DSL +/// (e.g., `DIV(myHtmlString)`). When you need to build up HTML incrementally through concatenation, +/// use [HtmlStringBuilder], which acts as a mutable builder and produces an `HtmlString` via +/// [HtmlStringBuilder#getHtmlString()]. For constructing structured HTML trees with elements and +/// attributes, prefer the [DOM] DSL instead. +/// +/// @see HtmlStringBuilder +/// @see DOM +/// @see DOM.Renderable +/// @see SafeToRender public final class HtmlString implements SafeToRender, DOM.Renderable, Comparable, Serializable, JSONString { // Helpful constants for convenience (and efficiency) diff --git a/api/src/org/labkey/api/util/HtmlStringBuilder.java b/api/src/org/labkey/api/util/HtmlStringBuilder.java index cf7fafcf508..25c4d5f5148 100644 --- a/api/src/org/labkey/api/util/HtmlStringBuilder.java +++ b/api/src/org/labkey/api/util/HtmlStringBuilder.java @@ -19,6 +19,14 @@ import java.util.regex.Pattern; +/// A mutable builder for constructing an [HtmlString] incrementally. Plain `String` arguments +/// passed to [#append(String)] are automatically HTML-encoded; pre-encoded [HtmlString] values +/// are appended as-is. Call [#getHtmlString()] to obtain the final immutable [HtmlString]. +/// +/// For structured HTML with elements and attributes, prefer the [DOM] DSL instead. +/// +/// @see HtmlString +/// @see DOM public class HtmlStringBuilder implements HasHtmlString, SafeToRender { private final StringBuilder _sb = new StringBuilder(); diff --git a/api/src/org/labkey/api/view/ActionURL.java b/api/src/org/labkey/api/view/ActionURL.java index 10ccfb36d01..e5b28dd0e98 100644 --- a/api/src/org/labkey/api/view/ActionURL.java +++ b/api/src/org/labkey/api/view/ActionURL.java @@ -42,10 +42,32 @@ import static org.hamcrest.CoreMatchers.containsString; -/** - * Encapsulates URL generation and parsing based on controller/container/action conventions. - * This class has to be kept in sync with ViewServlet. - */ +/// Represents a URL that follows LabKey's container/action routing conventions. +/// Extends [URLHelper] with structured access to the controller name, action name, and +/// container path, generating URLs in the format: `/contextPath/containerPath/controller-action.view` +/// +/// Scheme, host, and port are lazily initialized from [AppProps] and only needed when +/// generating absolute URLs. +/// +/// ### Examples +/// +/// ```java +/// // From an action class (preferred) +/// ActionURL url = new ActionURL(MyAction.class, container); +/// +/// // From controller/action strings +/// ActionURL url = new ActionURL("core", "login", container); +/// +/// // Parsed from a URL string +/// ActionURL url = new ActionURL("/containerPath/core-login.view?returnUrl=..."); +/// +/// // Adding parameters (fluent API) +/// url.addParameter("key", "value") +/// .addReturnUrl(returnUrl); +/// ``` +/// +/// @see URLHelper +/// @see ViewServlet public class ActionURL extends URLHelper implements Cloneable { private static boolean useContainerRelativeURL() diff --git a/api/src/org/labkey/api/view/HtmlView.java b/api/src/org/labkey/api/view/HtmlView.java index d984e7a07fa..a588b7d1777 100644 --- a/api/src/org/labkey/api/view/HtmlView.java +++ b/api/src/org/labkey/api/view/HtmlView.java @@ -21,7 +21,24 @@ import org.labkey.api.util.HtmlStringBuilder; import org.labkey.api.writer.HtmlWriter; -/** Renders a fixed set of HTML at the content of the view */ +/// A view that renders a fixed block of HTML content. Accepts either an [HtmlString] or a [DOM.Renderable]. +/// +/// ```java +/// // From safe, pre-encoded HTML +/// HtmlView view = new HtmlView(HtmlString.of("Hello, world!")); +/// +/// // With a title (automatically uses PORTAL frame) +/// HtmlView view = new HtmlView("My Section", HtmlString.of("Some content")); +/// +/// // From a DOM renderable +/// HtmlView view = new HtmlView(DOM.DIV("Hello")); +/// +/// // Plain text (auto-escaped) +/// HtmlView view = HtmlView.of("User-supplied text"); +/// +/// // Error message styled with labkey-error +/// HtmlView view = HtmlView.err("Something went wrong"); +/// ``` public class HtmlView extends WebPartView { private String _contentType = null; diff --git a/api/src/org/labkey/api/view/HttpView.java b/api/src/org/labkey/api/view/HttpView.java index c672b62c678..6a1d966bc2e 100644 --- a/api/src/org/labkey/api/view/HttpView.java +++ b/api/src/org/labkey/api/view/HttpView.java @@ -52,11 +52,54 @@ import java.util.function.Supplier; -/** - * BEWARE: Our shorts are showing a bit here. Our primary HTML components (aka views) are not spring Views. - * Architecturally, they act like ModelAndView. We may want to fix this in the future, but we're moving forward with - * this conceptual co-mingling for now. - */ +/// Base class for LabKey's server-side HTML rendering components (views). +/// +/// `HttpView` serves a dual role: it is both a Spring [View] (capable of rendering a response) +/// and a [ModelAndView][org.springframework.web.servlet.ModelAndView] (carrying its own model bean +/// of type `ModelBean`). This dual nature means an `HttpView` can be rendered directly by Spring's +/// `DispatcherServlet` and can also be composed inside other views via [#include(ModelAndView)]. +/// +/// ## Subclassing +/// +/// Subclasses typically override one of the `renderInternal` methods: +/// +/// - [#renderInternal(Object, PrintWriter)] — for simple HTML output via a `PrintWriter`. +/// - [#renderInternal(Object, HttpServletRequest, HttpServletResponse)] — when full access to the +/// servlet request/response is needed (e.g., [JspView]). +/// +/// ## View stack and thread-local context +/// +/// During rendering, `HttpView` maintains a thread-local stack of active views +/// ([ViewStackEntry] records). This allows any code running on the render thread to access +/// the current request, response, [ViewContext], and [PageConfig] via static helpers: +/// +/// - [#currentRequest()] / [#currentResponse()] +/// - [#currentContext()] — the [ViewContext] of the innermost `HttpView` on the stack. +/// - [#currentPageConfig()] — the active page configuration. +/// - [#currentView()] / [#currentModel()] +/// +/// The stack is pushed automatically in [#render] and can be initialized for non-view +/// contexts (e.g., filters, servlets) via [#initForRequest]. +/// +/// ## Composition +/// +/// Views can be nested using named child views stored in [#_views]: +/// +/// - [#setBody] / [#getBody] — convenience accessors for the well-known `BODY` slot. +/// - [#setView(String, ModelAndView)] / [#getView(String)] — arbitrary named slots. +/// - [#include(ModelAndView)] — renders a child view inline within the current response. +/// +/// ## Client dependencies +/// +/// Each view can declare CSS/JS dependencies via [#addClientDependency]. These are +/// aggregated recursively over the view tree by [#getClientDependencies()] so that page +/// templates can emit the full set of required resources. +/// +/// @param the type of the model object this view renders +/// @see WebPartView +/// @see JspView +/// @see HtmlView +/// @see org.labkey.api.query.QueryView public abstract class HttpView extends DefaultModelAndView implements View, HasViewContext { private static final int _debug = Debug.getLevel(HttpView.class); diff --git a/api/src/org/labkey/api/view/JspView.java b/api/src/org/labkey/api/view/JspView.java index 617e8db6b6b..bca55deec84 100644 --- a/api/src/org/labkey/api/view/JspView.java +++ b/api/src/org/labkey/api/view/JspView.java @@ -40,6 +40,14 @@ import java.util.Map; +/// A view that renders a JSP page, optionally with a typed model object and validation errors. +/// +/// ```java +/// // Render a JSP with a model bean +/// JspView view = new JspView<>("/org/labkey/mymodule/view/myPage.jsp", form); +/// ``` +/// +/// @param the type of the model object accessible from the JSP via `getModelBean()` public class JspView extends WebPartView { protected String _path; diff --git a/api/src/org/labkey/api/view/NavTree.java b/api/src/org/labkey/api/view/NavTree.java index 4faf50532ff..377b88cc195 100644 --- a/api/src/org/labkey/api/view/NavTree.java +++ b/api/src/org/labkey/api/view/NavTree.java @@ -24,6 +24,7 @@ import org.labkey.api.util.LinkBuilder; import org.labkey.api.util.PageFlowUtil; import org.labkey.api.util.URLHelper; +import org.labkey.api.view.menu.NavTreeMenu; import java.net.URISyntaxException; import java.util.Arrays; @@ -38,12 +39,33 @@ import static org.labkey.api.util.DOM.cl; import static org.labkey.api.util.DOM.createHtmlFragment; -/** - * NavTree can be used three ways in different places in the product - * 1) as a single navigation element (no children) - * 2) as a list of navigation elements (ignore the root node, use children as list of elements) - * 3) as a tree, may be rendered as a tree, or menu - */ +/// A tree-structured navigation element used throughout the application to build menus, breadcrumbs, +/// button dropdowns, tab strips, webpart controls, and other navigational UI. Each node carries display +/// properties (text, icon, tooltip) and behavior (href, onclick script, POST with confirmation). +/// +/// NavTree is used in many contexts — as a single link, a flat list of items, a hierarchical menu, +/// or serialized to JSON/JavaScript for client-side rendering. The special singleton [MENU_SEPARATOR] +/// inserts visual dividers between groups of menu items. +/// +/// ### Examples +/// +/// ```java +/// // Simple link +/// NavTree link = new NavTree("Settings", settingsUrl); +/// +/// // Dropdown menu with items +/// NavTree menu = new NavTree("Admin"); +/// menu.addChild("Users", usersUrl); +/// menu.addChild("Permissions", permissionsUrl); +/// menu.addSeparator(); +/// menu.addChild("Site Settings", siteSettingsUrl); +/// +/// // POST action +/// NavTree delete = new NavTree("Delete", deleteUrl); +/// delete.usePost(); +/// ``` +/// +/// @see NavTreeMenu public class NavTree implements Collapsible { diff --git a/api/src/org/labkey/api/view/WebPartView.java b/api/src/org/labkey/api/view/WebPartView.java index 82ce82b3c85..032af9a457d 100644 --- a/api/src/org/labkey/api/view/WebPartView.java +++ b/api/src/org/labkey/api/view/WebPartView.java @@ -49,10 +49,22 @@ import java.util.Objects; import java.util.Set; -/** - * - * @param the class to use as the data model for configuration - */ +/// An [HttpView] that wraps its content in a configurable frame (title bar, chrome, collapse controls). +/// +/// The frame style is controlled by [FrameType]: +/// +/// - [FrameType#PORTAL] — full portal chrome with title bar, customize/move widgets, and drag handles. +/// - [FrameType#TITLE] — lightweight title with a horizontal rule, no portal controls. +/// - [FrameType#DIALOG] — dialog-style frame. +/// - [FrameType#DIV] — bare `
` wrapper with a CSS class, no title or chrome. +/// - [FrameType#LEFT_NAVIGATION] — Only used by NavTreeMenu now +/// - [FrameType#NONE] — no frame at all; content is rendered directly. +/// - [FrameType#NOT_HTML] — same as `NONE`, used as a marker for non-HTML responses. +/// +/// Subclasses override [#renderView] instead of `renderInternal`. The frame's start/end +/// tags are emitted automatically around the subclass content. +/// +/// @param the class to use as the data model for configuration public abstract class WebPartView extends HttpView { /** diff --git a/api/src/org/labkey/vfs/FileLike.java b/api/src/org/labkey/vfs/FileLike.java index 78639f02774..adeec086ef3 100644 --- a/api/src/org/labkey/vfs/FileLike.java +++ b/api/src/org/labkey/vfs/FileLike.java @@ -26,6 +26,23 @@ import java.util.List; import java.util.function.Predicate; +/// A file or directory within a [FileSystemLike]. All paths are relative to the file system root, +/// ensuring that access stays within the scoped boundary. +/// +/// ```java +/// FileLike root = new FileSystemLike.Builder(dir).readwrite().root(); +/// FileLike child = root.resolveChild("data.tsv"); +/// +/// // Read/write via streams +/// try (OutputStream out = child.openOutputStream()) { ... } +/// try (InputStream in = child.openInputStream()) { ... } +/// +/// // Navigate to a nested path +/// FileLike nested = root.resolveFile(Path.parse("subdir/file.txt")); +/// +/// // Convert to java.nio.file.Path when needed (local file systems only) +/// java.nio.file.Path nioPath = child.toNioPathForRead(); +/// ``` @JsonSerialize(using = FileLike.FileLikeSerializer.class) @JsonDeserialize(using = FileLike.FileLikeDeserializer.class) public interface FileLike extends Comparable diff --git a/api/src/org/labkey/vfs/FileSystemLike.java b/api/src/org/labkey/vfs/FileSystemLike.java index ac81a2b131d..072e462566f 100644 --- a/api/src/org/labkey/vfs/FileSystemLike.java +++ b/api/src/org/labkey/vfs/FileSystemLike.java @@ -28,38 +28,50 @@ import static org.apache.commons.lang3.StringUtils.defaultIfBlank; import static org.labkey.api.util.FileUtil.FILE_SCHEME; -/** - * In LabKey most files are accessed within a directory with a particular role. For instance, a directory might be: - *
- * - a pipeline root used for storing assay files - *
- * - a temporary working directory used for assay import or a report - *
- * - a directory with configuration files - *

- * In any of these scenarios the code using that directory usually does not need access to files _outside_ that directory. - * Using java.io.File makes it difficult to enforce this. Instead of this common pattern - *

- *      File workingdir = new File("tempdir");
- *      File file = new File(workingdir, anypath))
- * 
- * We can now follow this pattern, which validates the scope of the resolved path. - *
- *     FileLike workingdir = new FileSystemLike.Builder("tempdir").readwrite().root();
- *     FileLike file = workingdir.resolveFile(anypath);
- * 
- * - *

- * implementation notes: - * - This is meant to be a wrapper over java.nio.file.Path, java.io.File or org.apache.commons.vfs2.FileObject or other implementations. - * However, it is still lower level than Resource. For instance, it does not know about Permissions or ContentType, etc. - *
- * - FileLike objects always present String path and util.Path relative to the FileSystemLike root. - * If the FileLike wraps a local path, toNioPath() can be used. - *
- * - These classes generally do not cache metadata, but the wrapped impl might. This is why FileLike has a reset() method. - * - Caching versions can be explicitly requested. - */ +/// A scoped virtual file system that constrains file access to a particular root directory. +/// +/// In LabKey, files are typically accessed within a directory that has a specific role, such as: +/// - A pipeline root for storing assay files +/// - A temporary working directory for assay import or a report +/// - A directory containing configuration files +/// +/// Code working within these directories usually does not need access to files outside the root. +/// Using `java.io.File` makes it difficult to enforce this boundary. Instead of: +/// +/// ```java +/// File workingdir = new File("tempdir"); +/// File file = new File(workingdir, anypath); // no path validation! +/// ``` +/// +/// Use `FileSystemLike`, which validates that resolved paths stay within scope: +/// +/// ```java +/// FileLike workingdir = new FileSystemLike.Builder("tempdir").readwrite().root(); +/// FileLike file = workingdir.resolveFile(anypath); // throws if path escapes root +/// ``` +/// +/// ### Builder examples +/// +/// ```java +/// // Read-only access to a directory +/// FileLike root = new FileSystemLike.Builder(path).readonly().root(); +/// +/// // Temporary working directory (read-write, root is deletable) +/// FileLike tmp = new FileSystemLike.Builder(tempDir).tempDir().root(); +/// +/// // Caching file system (caches metadata like type and children) +/// FileLike root = new FileSystemLike.Builder(path).readonly().caching().root(); +/// ``` +/// +/// ### Implementation notes +/// +/// - Wraps `java.nio.file.Path`, `java.io.File`, `org.apache.commons.vfs2.FileObject`, or other +/// backends. Lower level than `Resource` — has no concept of Permissions or ContentType. +/// - [FileLike] objects present paths relative to the `FileSystemLike` root. +/// Use `toNioPath()` to obtain a local path when the backing store is local. +/// - Metadata is generally not cached, but the underlying implementation may cache. +/// Use [FileLike#refresh] to force a reload. Caching can be explicitly requested +/// via [Builder#caching] or [#getCachingFileSystem]. public interface FileSystemLike { // NOTE: a full webdav path consist of case-sensitive and case-insensitive parts