Move post list query method to Post class.

Change-Id: I8dbb664d2522a3012a477b850b67cb642bae9aa8
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 b4b4222..502cb0d 100644
--- a/src/main/java/eu/mulk/mulkcms2/benki/generic/Post.java
+++ b/src/main/java/eu/mulk/mulkcms2/benki/generic/Post.java
@@ -1,10 +1,16 @@
 package eu.mulk.mulkcms2.benki.generic;
 
 import eu.mulk.mulkcms2.benki.accesscontrol.Role;
+import eu.mulk.mulkcms2.benki.bookmarks.Bookmark;
+import eu.mulk.mulkcms2.benki.lazychat.LazychatMessage;
 import eu.mulk.mulkcms2.benki.users.User;
+import eu.mulk.mulkcms2.benki.users.User_;
 import io.quarkus.hibernate.orm.panache.PanacheEntityBase;
+import io.quarkus.security.identity.SecurityIdentity;
 import java.time.OffsetDateTime;
+import java.util.ArrayList;
 import java.util.Set;
+import javax.annotation.CheckForNull;
 import javax.persistence.Column;
 import javax.persistence.Entity;
 import javax.persistence.FetchType;
@@ -19,6 +25,11 @@
 import javax.persistence.ManyToOne;
 import javax.persistence.SequenceGenerator;
 import javax.persistence.Table;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.From;
+import javax.persistence.criteria.JoinType;
+import javax.persistence.criteria.Predicate;
 
 @Entity
 @Table(name = "posts", schema = "benki")
@@ -57,4 +68,60 @@
       joinColumns = @JoinColumn(name = "message"),
       inverseJoinColumns = @JoinColumn(name = "target"))
   public Set<Role> targets;
+
+  public static <T extends Post> CriteriaQuery<T> findViewable(
+      Class<T> entityClass,
+      SecurityIdentity readerIdentity,
+      @CheckForNull User owner,
+      @CheckForNull Integer cursor,
+      CriteriaBuilder cb,
+      boolean forward) {
+    CriteriaQuery<T> query = cb.createQuery(entityClass);
+
+    var conditions = new ArrayList<Predicate>();
+
+    From<?, T> post;
+    if (readerIdentity.isAnonymous()) {
+      post = query.from(entityClass);
+      var target = post.join(Post_.targets);
+      conditions.add(cb.equal(target, Role.getWorld()));
+    } else {
+      var userName = readerIdentity.getPrincipal().getName();
+      var user = User.findByNickname(userName);
+
+      var root = query.from(User.class);
+      conditions.add(cb.equal(root, user));
+      if (entityClass.isAssignableFrom(Bookmark.class)) {
+        post = (From<?, T>) root.join(User_.visibleBookmarks);
+      } else {
+        assert entityClass.isAssignableFrom(LazychatMessage.class) : entityClass;
+        post = (From<?, T>) root.join(User_.visibleLazychatMessages);
+      }
+    }
+
+    query.select(post);
+    post.fetch(Post_.owner, JoinType.LEFT);
+
+    if (owner != null) {
+      conditions.add(cb.equal(post.get(Post_.owner), owner));
+    }
+
+    if (forward) {
+      query.orderBy(cb.desc(post.get(Post_.id)));
+    } else {
+      query.orderBy(cb.asc(post.get(Post_.id)));
+    }
+
+    if (cursor != null) {
+      if (forward) {
+        conditions.add(cb.le(post.get(Post_.id), cursor));
+      } else {
+        conditions.add(cb.gt(post.get(Post_.id), cursor));
+      }
+    }
+
+    query.where(conditions.toArray(new Predicate[0]));
+
+    return query;
+  }
 }