From 8985b64b0db0e2ff0d8376ebc1d24826541f6825 Mon Sep 17 00:00:00 2001 From: Nathan Rauh Date: Wed, 3 Jun 2026 14:46:47 -0500 Subject: [PATCH 1/3] Start with changes copied from Gavin's #1479 --- api/src/main/java/jakarta/data/Limit.java | 13 ++++--- .../java/jakarta/data/page/PageRequest.java | 35 ++++++++++++++++++- 2 files changed, 43 insertions(+), 5 deletions(-) diff --git a/api/src/main/java/jakarta/data/Limit.java b/api/src/main/java/jakarta/data/Limit.java index 39e9fafbf..f8470eed4 100644 --- a/api/src/main/java/jakarta/data/Limit.java +++ b/api/src/main/java/jakarta/data/Limit.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022,2024 Contributors to the Eclipse Foundation + * Copyright (c) 2022,2026 Contributors to the Eclipse Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -104,10 +104,15 @@ public int maxResults() { // Override to provide method documentation: /** - *

Offset at which to start when returning query results. - * The first query result is position {@code 1}.

+ *

The position at which to start when returning query results.

+ *

The first query result is at position one. If the start position + * is greater than one, some results at the beginning of the result set + * are skipped.

* - * @return offset of the first result. + * @return position of the first result. + * + * @apiNote Positions are indexed from one; + * {@linkplain #startOffset offsets} are indexed from zero. */ public long startAt() { return startAt; diff --git a/api/src/main/java/jakarta/data/page/PageRequest.java b/api/src/main/java/jakarta/data/page/PageRequest.java index 10fe0313a..085781419 100644 --- a/api/src/main/java/jakarta/data/page/PageRequest.java +++ b/api/src/main/java/jakarta/data/page/PageRequest.java @@ -223,12 +223,27 @@ static PageRequest beforeCursor(Cursor cursor, long pageNumber, int maxPageSize, Mode mode(); /** - * Returns the page to be returned. + * Returns the page number of the page to be returned. * * @return the page to be returned. + * @apiNote Page numbers are indexed from one; + * page {@linkplain #pageOffset offsets} are indexed from zero. */ long page(); + /** + * Returns the page offset of the page to be returned. + * + * @return the page to be returned. + * @since 1.1 + * + * @apiNote Page offsets are indexed from zero; + * page {@linkplain #pageNumber numbers} are indexed from one. + */ + default long pageOffset() { + return pageNumber() - 1; + } + /** * Returns the requested size of each page * @@ -266,9 +281,27 @@ static PageRequest beforeCursor(Cursor cursor, long pageNumber, int maxPageSize, * @return a new instance of {@code PageRequest}. This method never returns * {@code null}. * @since 1.1 + * @apiNote Page numbers are indexed from one; + * page {@linkplain #atPageOffset offsets} are indexed from zero. */ PageRequest page(long pageNumber); + /** + *

Creates a new page request with the same pagination information, + * but with the specified page offset.

+ * + * @param pageOffset the page offset. + * @return a new instance of {@code PageRequest}. This method never returns + * {@code null}. + * @since 1.1 + * + * @apiNote Page offsets are indexed from zero; + * page {@linkplain #atPageNumber numbers} are indexed from one. + */ + default PageRequest atPageOffset(long pageOffset) { + return atPageNumber(pageOffset + 1); + } + /** *

Creates a new page request with the same pagination information, * but with the specified maximum page size. When a page is retrieved from From 749b0f271572312ebaafc3498b99d29a081d046e Mon Sep 17 00:00:00 2001 From: Nathan Rauh Date: Wed, 3 Jun 2026 14:53:21 -0500 Subject: [PATCH 2/3] 0-based offsets relative to first page (PageRequest) and first result (Limit) --- api/src/main/java/jakarta/data/Limit.java | 49 +++++++++++++-- .../java/jakarta/data/page/PageRequest.java | 61 +++++++++++-------- .../data/messages/DataMessages.properties | 5 +- 3 files changed, 83 insertions(+), 32 deletions(-) diff --git a/api/src/main/java/jakarta/data/Limit.java b/api/src/main/java/jakarta/data/Limit.java index f8470eed4..b08e1bb6d 100644 --- a/api/src/main/java/jakarta/data/Limit.java +++ b/api/src/main/java/jakarta/data/Limit.java @@ -17,14 +17,22 @@ */ package jakarta.data; +import jakarta.data.messages.Messages; import jakarta.data.page.PageRequest; +import jakarta.data.repository.Find; +import jakarta.data.repository.Query; /** *

Specifies a limit on the number of results retrieved by a repository * method. The results of a single invocation of a repository method may be - * limited to a given {@linkplain #of(int) maximum number of results} or to a - * given {@linkplain #range(long, long) positioned range} defined in terms of an - * offset and maximum number of results.

+ * limited to + *

* *

A query method of a repository may have a parameter of type * {@code Limit} if its return type indicates that it may return multiple @@ -105,14 +113,14 @@ public int maxResults() { /** *

The position at which to start when returning query results.

- *

The first query result is at position one. If the start position + *

The first query result is at position {@code 1}. If the start position * is greater than one, some results at the beginning of the result set * are skipped.

* * @return position of the first result. * * @apiNote Positions are indexed from one; - * {@linkplain #startOffset offsets} are indexed from zero. + * offsets are indexed from zero. */ public long startAt() { return startAt; @@ -131,6 +139,37 @@ public static Limit of(int maxResults) { return new Limit(maxResults, DEFAULT_START_AT); } + /** + * Create a limit that caps the number of results at the given maximum, + * starting at the given {@code offset} from the first result. + *

+ * An {@code offset} of zero includes the first result. A positive + * {@code offset} skips the respective number of results. + * + * @param maxResults maximum number of results + * @param offset offset at which to start + * @return limit that can be supplied to a {@link Find} method or + * {@link Query} method that performs a find operation; + * will never be {@code null} + * @throws IllegalArgumentException if {@code maxResults} or + * {@code offset} is negative or the {@code offset} is + * {@link Long#MAX_VALUE} + * @since 1.1 + */ + public static Limit of(int maxResults, long offset) { + if (offset < 0) { + throw new IllegalArgumentException( + Messages.get("004.arg.negative", "offset")); + } + + if (offset == Long.MAX_VALUE) { + throw new IllegalArgumentException( + Messages.get("013.arg.invalid", "offset", offset)); + } + + return new Limit(maxResults, offset + 1); + } + /** *

Create a limit that restricts the results to a range, * beginning with the {@code startAt} position and ending after the diff --git a/api/src/main/java/jakarta/data/page/PageRequest.java b/api/src/main/java/jakarta/data/page/PageRequest.java index 085781419..054f0ce41 100644 --- a/api/src/main/java/jakarta/data/page/PageRequest.java +++ b/api/src/main/java/jakarta/data/page/PageRequest.java @@ -20,6 +20,7 @@ import jakarta.data.Limit; import jakarta.data.Order; import jakarta.data.Sort; +import jakarta.data.messages.Messages; import jakarta.data.repository.First; import jakarta.data.repository.OrderBy; @@ -223,27 +224,14 @@ static PageRequest beforeCursor(Cursor cursor, long pageNumber, int maxPageSize, Mode mode(); /** - * Returns the page number of the page to be returned. + * Returns the requested page number. Page numbers begin with {@code 1}. * - * @return the page to be returned. + * @return the requested page number * @apiNote Page numbers are indexed from one; * page {@linkplain #pageOffset offsets} are indexed from zero. */ long page(); - /** - * Returns the page offset of the page to be returned. - * - * @return the page to be returned. - * @since 1.1 - * - * @apiNote Page offsets are indexed from zero; - * page {@linkplain #pageNumber numbers} are indexed from one. - */ - default long pageOffset() { - return pageNumber() - 1; - } - /** * Returns the requested size of each page * @@ -274,32 +262,53 @@ default long pageOffset() { boolean requestTotal(); /** - *

Creates a new page request with the same pagination information, - * but with the specified page number.

+ * Creates a new page request with the same pagination information, + * but with the specified page number. The first page number is {@code 1}. * * @param pageNumber the page number. * @return a new instance of {@code PageRequest}. This method never returns * {@code null}. * @since 1.1 * @apiNote Page numbers are indexed from one; - * page {@linkplain #atPageOffset offsets} are indexed from zero. + * page {@linkplain #pageOffset offsets} are indexed from zero. */ PageRequest page(long pageNumber); /** - *

Creates a new page request with the same pagination information, - * but with the specified page offset.

+ * Creates a new page request with the same pagination information, + * but with the given {@code offset}. + *

+ * The offset is relative to the first page. An offset of {@code 0} + * requests the first page, and an offset of {@code 1} requests the + * second page. * - * @param pageOffset the page offset. - * @return a new instance of {@code PageRequest}. This method never returns - * {@code null}. + * @return the offset of the requested page + * @throws IllegalStateException if the {@link #mode()} is not + * {@link Mode#OFFSET} + * @throws IllegalArgumentException if the {@code offset} is negative + * or {@link Long#MAX_VALUE} * @since 1.1 * * @apiNote Page offsets are indexed from zero; - * page {@linkplain #atPageNumber numbers} are indexed from one. + * page {@linkplain #page numbers} are indexed from one. */ - default PageRequest atPageOffset(long pageOffset) { - return atPageNumber(pageOffset + 1); + default PageRequest pageOffset(long offset) { + if (mode() != Mode.OFFSET) { + throw new IllegalStateException( + Messages.get("014.mode.disallows.offset", mode())); + } + + if (offset < 0) { + throw new IllegalArgumentException( + Messages.get("004.arg.negative", "offset")); + } + + if (offset == Long.MAX_VALUE) { + throw new IllegalArgumentException( + Messages.get("013.arg.invalid", "offset", offset)); + } + + return page(offset + 1); } /** diff --git a/api/src/main/resources/jakarta/data/messages/DataMessages.properties b/api/src/main/resources/jakarta/data/messages/DataMessages.properties index bc276eb83..b7a2157e9 100644 --- a/api/src/main/resources/jakarta/data/messages/DataMessages.properties +++ b/api/src/main/resources/jakarta/data/messages/DataMessages.properties @@ -1,5 +1,5 @@ # -# Copyright (c) 2025 Contributors to the Eclipse Foundation +# Copyright (c) 2025,2026 Contributors to the Eclipse Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -35,3 +35,6 @@ identify the entity class that declares the attribute. For static metamodel \ classes, use an .of method that is defined on an Attribute subtype to supply \ the entity class and entity attribute type. +013.arg.invalid=The {0} argument cannot be {1} +014.mode.disallows.offset=A page cannot be requested to have an offset \ + and a mode of {0} From ff6b9194136a11bcbe00921d1bf4514d9c891e19 Mon Sep 17 00:00:00 2001 From: Nathan Rauh Date: Thu, 4 Jun 2026 08:15:39 -0500 Subject: [PATCH 3/3] Code review correction to description of range method MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Good catch - I rearranged some text that was there previously and you spotted an error in it. Co-authored-by: Stéphane Épardaud --- api/src/main/java/jakarta/data/Limit.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/main/java/jakarta/data/Limit.java b/api/src/main/java/jakarta/data/Limit.java index b08e1bb6d..bbcc9a948 100644 --- a/api/src/main/java/jakarta/data/Limit.java +++ b/api/src/main/java/jakarta/data/Limit.java @@ -31,7 +31,7 @@ *

  • a {@linkplain #of(int, long) maximum number of results relative to an * offset}, or
  • *
  • a {@linkplain #range(long, long) positioned range} defined in terms of - * a starting position and maximum number of results.
  • + * a starting position and an ending position. *

    * *

    A query method of a repository may have a parameter of type