Make paged post queries generic over Bookmark and LazychatMessage.

Change-Id: Ie0ac96bfc80c3c31bb948681ef90a9a50a512cf4
diff --git a/src/main/java/eu/mulk/mulkcms2/benki/bookmarks/Bookmark.java b/src/main/java/eu/mulk/mulkcms2/benki/bookmarks/Bookmark.java
index f7dc9c0..8beda97 100644
--- a/src/main/java/eu/mulk/mulkcms2/benki/bookmarks/Bookmark.java
+++ b/src/main/java/eu/mulk/mulkcms2/benki/bookmarks/Bookmark.java
@@ -4,6 +4,7 @@
 import eu.mulk.mulkcms2.benki.users.User;
 import eu.mulk.mulkcms2.common.markdown.MarkdownConverter;
 import io.quarkus.security.identity.SecurityIdentity;
+import java.util.List;
 import java.util.Set;
 import javax.annotation.CheckForNull;
 import javax.persistence.CollectionTable;
@@ -14,8 +15,7 @@
 import javax.persistence.JoinColumn;
 import javax.persistence.Table;
 import javax.persistence.Transient;
-import javax.persistence.criteria.CriteriaBuilder;
-import javax.persistence.criteria.CriteriaQuery;
+import org.hibernate.Session;
 
 @Entity
 @Table(name = "bookmarks", schema = "benki")
@@ -43,12 +43,17 @@
     return new MarkdownConverter().htmlify(description);
   }
 
-  public static CriteriaQuery<Bookmark> findViewable(
-      SecurityIdentity readerIdentity,
+  public static List<Bookmark> findViewable(
+      Session session, SecurityIdentity viewer, @CheckForNull User owner) {
+    return findViewable(Bookmark.class, session, viewer, owner, null, null).posts;
+  }
+
+  public static PostPage<Bookmark> findViewable(
+      Session session,
+      SecurityIdentity viewer,
       @CheckForNull User owner,
       @CheckForNull Integer cursor,
-      CriteriaBuilder cb,
-      boolean forward) {
-    return Post.findViewable(Bookmark.class, readerIdentity, owner, cursor, cb, forward);
+      @CheckForNull Integer count) {
+    return findViewable(Bookmark.class, session, viewer, owner, cursor, count);
   }
 }
diff --git a/src/main/java/eu/mulk/mulkcms2/benki/bookmarks/BookmarkResource.java b/src/main/java/eu/mulk/mulkcms2/benki/bookmarks/BookmarkResource.java
index 7dd35d4..2272db2 100644
--- a/src/main/java/eu/mulk/mulkcms2/benki/bookmarks/BookmarkResource.java
+++ b/src/main/java/eu/mulk/mulkcms2/benki/bookmarks/BookmarkResource.java
@@ -31,7 +31,6 @@
 import java.util.Comparator;
 import java.util.Date;
 import java.util.List;
-import java.util.Objects;
 import java.util.Set;
 import java.util.stream.Collectors;
 import javax.annotation.CheckForNull;
@@ -102,10 +101,11 @@
 
     maxResults = maxResults == null ? defaultMaxResults : maxResults;
 
-    var q = selectBookmarks(null, cursor, maxResults);
+    var session = entityManager.unwrap(Session.class);
+    var q = Bookmark.findViewable(session, identity, null, cursor, maxResults);
 
     return bookmarkList
-        .data("bookmarks", q.bookmarks)
+        .data("bookmarks", q.posts)
         .data("feedUri", "/bookmarks/feed")
         .data("authenticated", !identity.isAnonymous())
         .data("hasPreviousPage", q.prevCursor != null)
@@ -126,10 +126,11 @@
     maxResults = maxResults == null ? defaultMaxResults : maxResults;
 
     var owner = User.findByNickname(ownerName);
-    var q = selectBookmarks(owner, cursor, maxResults);
+    var session = entityManager.unwrap(Session.class);
+    var q = Bookmark.findViewable(session, identity, owner, cursor, maxResults);
 
     return bookmarkList
-        .data("bookmarks", q.bookmarks)
+        .data("bookmarks", q.posts)
         .data("feedUri", String.format("/bookmarks/~%s/feed", ownerName))
         .data("authenticated", !identity.isAnonymous())
         .data("hasPreviousPage", q.prevCursor != null)
@@ -155,7 +156,7 @@
   }
 
   private String makeFeed(@Nullable User owner, @Nullable String ownerName) throws FeedException {
-    var bookmarks = selectBookmarks(owner);
+    var bookmarks = Bookmark.findViewable(entityManager.unwrap(Session.class), identity, owner);
     var feed = new Feed("atom_1.0");
 
     var feedSubId = owner == null ? "" : String.format("/%d", owner.id);
@@ -289,69 +290,4 @@
   static String htmlDateTime(TemporalAccessor x) {
     return htmlDateFormatter.format(x);
   }
-
-  private static class BookmarkPage {
-    @CheckForNull Integer prevCursor;
-    @CheckForNull Integer cursor;
-    @CheckForNull Integer nextCursor;
-    List<Bookmark> bookmarks;
-
-    public BookmarkPage(
-        @CheckForNull Integer c0,
-        @CheckForNull Integer c1,
-        @CheckForNull Integer c2,
-        List<Bookmark> resultList) {
-      this.prevCursor = c0;
-      this.cursor = c1;
-      this.nextCursor = c2;
-      this.bookmarks = resultList;
-    }
-  }
-
-  private List<Bookmark> selectBookmarks(@CheckForNull User owner) {
-    return selectBookmarks(owner, null, null).bookmarks;
-  }
-
-  private BookmarkPage selectBookmarks(
-      @CheckForNull User owner, @CheckForNull Integer cursor, @CheckForNull Integer count) {
-
-    if (cursor != null) {
-      Objects.requireNonNull(count);
-    }
-
-    var cb = entityManager.unwrap(Session.class).getCriteriaBuilder();
-
-    var forwardCriteria = Bookmark.findViewable(identity, owner, cursor, cb, true);
-    var forwardQuery = entityManager.createQuery(forwardCriteria);
-
-    if (count != null) {
-      forwardQuery.setMaxResults(count + 1);
-    }
-
-    log.debug(forwardQuery.unwrap(org.hibernate.query.Query.class).getQueryString());
-
-    @CheckForNull Integer prevCursor = null;
-    @CheckForNull Integer nextCursor = null;
-
-    if (cursor != null) {
-      // Look backwards as well so we can find the prevCursor.
-      var backwardCriteria = Bookmark.findViewable(identity, owner, cursor, cb, false);
-      var backwardQuery = entityManager.createQuery(backwardCriteria);
-      backwardQuery.setMaxResults(count);
-      var backwardResults = backwardQuery.getResultList();
-      if (!backwardResults.isEmpty()) {
-        prevCursor = backwardResults.get(backwardResults.size() - 1).id;
-      }
-    }
-
-    var forwardResults = forwardQuery.getResultList();
-    if (count != null) {
-      if (forwardResults.size() == count + 1) {
-        nextCursor = forwardResults.get(count).id;
-        forwardResults.remove((int) count);
-      }
-    }
-
-    return new BookmarkPage(prevCursor, cursor, nextCursor, forwardResults);
-  }
 }
diff --git a/src/main/java/eu/mulk/mulkcms2/benki/generic/Post.java b/src/main/java/eu/mulk/mulkcms2/benki/generic/Post.java
index 502cb0d..898eeb4 100644
--- a/src/main/java/eu/mulk/mulkcms2/benki/generic/Post.java
+++ b/src/main/java/eu/mulk/mulkcms2/benki/generic/Post.java
@@ -9,6 +9,8 @@
 import io.quarkus.security.identity.SecurityIdentity;
 import java.time.OffsetDateTime;
 import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
 import java.util.Set;
 import javax.annotation.CheckForNull;
 import javax.persistence.Column;
@@ -30,12 +32,16 @@
 import javax.persistence.criteria.From;
 import javax.persistence.criteria.JoinType;
 import javax.persistence.criteria.Predicate;
+import org.hibernate.Session;
+import org.jboss.logging.Logger;
 
 @Entity
 @Table(name = "posts", schema = "benki")
 @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
 public abstract class Post extends PanacheEntityBase {
 
+  private static Logger log = Logger.getLogger(Post.class);
+
   @Id
   @SequenceGenerator(
       allocationSize = 1,
@@ -69,7 +75,7 @@
       inverseJoinColumns = @JoinColumn(name = "target"))
   public Set<Role> targets;
 
-  public static <T extends Post> CriteriaQuery<T> findViewable(
+  protected static <T extends Post> CriteriaQuery<T> queryViewable(
       Class<T> entityClass,
       SecurityIdentity readerIdentity,
       @CheckForNull User owner,
@@ -124,4 +130,75 @@
 
     return query;
   }
+
+  public static class PostPage<T extends Post> {
+    public @CheckForNull Integer prevCursor;
+    public @CheckForNull Integer cursor;
+    public @CheckForNull Integer nextCursor;
+    public List<T> posts;
+
+    private PostPage(
+        @CheckForNull Integer c0,
+        @CheckForNull Integer c1,
+        @CheckForNull Integer c2,
+        List<T> resultList) {
+      this.prevCursor = c0;
+      this.cursor = c1;
+      this.nextCursor = c2;
+      this.posts = resultList;
+    }
+  }
+
+  protected static <T extends Post> List<T> findViewable(
+      Class<T> entityClass, Session session, SecurityIdentity viewer, @CheckForNull User owner) {
+    return findViewable(entityClass, session, viewer, owner, null, null).posts;
+  }
+
+  protected static <T extends Post> PostPage<T> findViewable(
+      Class<T> entityClass,
+      Session session,
+      SecurityIdentity viewer,
+      @CheckForNull User owner,
+      @CheckForNull Integer cursor,
+      @CheckForNull Integer count) {
+
+    if (cursor != null) {
+      Objects.requireNonNull(count);
+    }
+
+    var cb = session.getCriteriaBuilder();
+
+    var forwardCriteria = Bookmark.queryViewable(entityClass, viewer, owner, cursor, cb, true);
+    var forwardQuery = session.createQuery(forwardCriteria);
+
+    if (count != null) {
+      forwardQuery.setMaxResults(count + 1);
+    }
+
+    log.debug(forwardQuery.unwrap(org.hibernate.query.Query.class).getQueryString());
+
+    @CheckForNull Integer prevCursor = null;
+    @CheckForNull Integer nextCursor = null;
+
+    if (cursor != null) {
+      // Look backwards as well so we can find the prevCursor.
+      var backwardCriteria = Bookmark.queryViewable(entityClass, viewer, owner, cursor, cb, false);
+      var backwardQuery = session.createQuery(backwardCriteria);
+      backwardQuery.setMaxResults(count);
+      var backwardResults = backwardQuery.getResultList();
+      if (!backwardResults.isEmpty()) {
+        prevCursor = backwardResults.get(backwardResults.size() - 1).id;
+      }
+    }
+
+    var forwardResults = forwardQuery.getResultList();
+    if (count != null) {
+      if (forwardResults.size() == count + 1) {
+        nextCursor = forwardResults.get(count).id;
+        forwardResults.remove((int) count);
+      }
+    }
+
+    return new PostPage(prevCursor, cursor, nextCursor, forwardResults);
+  }
 }
diff --git a/src/main/java/eu/mulk/mulkcms2/benki/lazychat/LazychatMessage.java b/src/main/java/eu/mulk/mulkcms2/benki/lazychat/LazychatMessage.java
index 504dd10..4e58259 100644
--- a/src/main/java/eu/mulk/mulkcms2/benki/lazychat/LazychatMessage.java
+++ b/src/main/java/eu/mulk/mulkcms2/benki/lazychat/LazychatMessage.java
@@ -5,6 +5,7 @@
 import eu.mulk.mulkcms2.common.markdown.MarkdownConverter;
 import io.quarkus.security.identity.SecurityIdentity;
 import java.util.Collection;
+import java.util.List;
 import javax.annotation.CheckForNull;
 import javax.persistence.Column;
 import javax.persistence.Entity;
@@ -12,8 +13,7 @@
 import javax.persistence.OneToMany;
 import javax.persistence.Table;
 import javax.persistence.Transient;
-import javax.persistence.criteria.CriteriaBuilder;
-import javax.persistence.criteria.CriteriaQuery;
+import org.hibernate.Session;
 
 @Entity
 @Table(name = "lazychat_messages", schema = "benki")
@@ -33,12 +33,17 @@
     return new MarkdownConverter().htmlify(content);
   }
 
-  public static CriteriaQuery<LazychatMessage> findViewable(
-      SecurityIdentity readerIdentity,
+  public static List<LazychatMessage> findViewable(
+      Session session, SecurityIdentity viewer, @CheckForNull User owner) {
+    return findViewable(LazychatMessage.class, session, viewer, owner, null, null).posts;
+  }
+
+  public static PostPage<LazychatMessage> findViewable(
+      Session session,
+      SecurityIdentity viewer,
       @CheckForNull User owner,
       @CheckForNull Integer cursor,
-      CriteriaBuilder cb,
-      boolean forward) {
-    return Post.findViewable(LazychatMessage.class, readerIdentity, owner, cursor, cb, forward);
+      @CheckForNull Integer count) {
+    return findViewable(LazychatMessage.class, session, viewer, owner, cursor, count);
   }
 }