PkgStore.java

/*
 * Copyright © 2021-2026 The CTAN Team and individual authors
 *
 * This file is distributed under the 3-clause BSD license.
 * See file LICENSE for details.
 */

package org.ctan.site.stores;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.ctan.site.domain.catalogue.Pkg;
import org.ctan.site.services.search.base.IndexType;
import org.ctan.site.services.search.base.IndexingSession;
import org.ctan.site.stores.base.AbstractIndexingStore;
import org.ctan.site.stores.base.GeneralPage;
import org.hibernate.SessionFactory;

import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Root;
import lombok.NonNull;

/**
 * The class <code>PkgStore</code> contains the repository for authors.
 *
 * @author <a href="mailto:gene@ctan.org">Gerd Neugebauer</a>
 */
public class PkgStore extends AbstractIndexingStore<Pkg> {

    /**
     * This is the constructor for the <code>PkgStore</code>.
     *
     * @param sessionFactory the session factory
     * @param indexingSession the indexing session
     */
    public PkgStore(SessionFactory sessionFactory,
        IndexingSession indexingSession) {

        super(sessionFactory, indexingSession);
    }

    /**
     * The method <code>findAll</code> collects all packages.
     *
     * @return a list of packages
     */
    @Override
    public List<Pkg> findAll() {

        CriteriaBuilder cb = currentSession().getCriteriaBuilder();
        var query = criteriaQuery();
        Root<Pkg> p = query.from(Pkg.class);
        query.orderBy(cb.asc(p.get("key")));
        return list(query);
    }

    /**
     * The method <code>findAllByKeyStartingWith</code> collects all packages
     * where the key has a given prefix.
     *
     * @param s the prefix or {@code null}
     * @return the list of matching packages
     */
    public List<Pkg> findAllByKeyStartingWith(String s) {

        var query = criteriaQuery();
        CriteriaBuilder cb = currentSession().getCriteriaBuilder();
        Root<Pkg> p = query.from(Pkg.class);
        if (s != null && !"".equals(s)) {
            query.where(cb.like(p.get("key"), s.toLowerCase() + "%"));
        }
        query.orderBy(cb.asc(p.get("key")));
        return list(query);
    }

    /**
     * The method <code>findAllByNameStartingWith</code> collects all packages
     * where the name has a given prefix.
     *
     * @param s the prefix or {@code null}
     * @return the list of matching packages
     */
    public List<Pkg> findAllByNameStartingWith(String s) {

        var query = criteriaQuery();
        CriteriaBuilder cb = currentSession().getCriteriaBuilder();
        Root<Pkg> p = query.from(Pkg.class);
        if (s != null && !"".equals(s)) {
            query.where(cb.like(p.get("name"), s.toLowerCase() + "%"));
        }
        query.orderBy(cb.asc(p.get("key")));
        return list(query);
    }

    /**
     * The method <code>getByCtanPath</code> provides means to retrieve a
     * package by its CTAN location.
     *
     * @param path the CTAN path
     * @return the package or {@code null}
     */
    public Pkg getByCtanPath(String path) {

        var query = criteriaQuery();
        CriteriaBuilder cb = currentSession().getCriteriaBuilder();
        Root<Pkg> p = query.from(Pkg.class);
        var ctanPath = "/" + path;
        query.where(cb.equal(p.get("ctanPath"), ctanPath));

        var list = list(query);
        return list.isEmpty() ? null : list.get(0);
    }

    /**
     * The method <code>getByKey</code> provides means to retrieve a package by
     * its key.
     *
     * @param id the id
     * @return the package or {@code null}
     */
    public Pkg getByKey(String id) {

        var query = criteriaQuery();
        CriteriaBuilder cb = currentSession().getCriteriaBuilder();
        Root<Pkg> p = query.from(Pkg.class);
        // Join<Pkg, PkgRef> refs = author.join("refs", JoinType.LEFT);
        // Join<PkgRef, Pkg> pkg = refs.join("pkg", JoinType.LEFT);

        // conditions.add(cb.equal(pkg.get("id"), associateId));
        // conditions.add(cb.isNull(refs.get("ack_date")));

        query.where(cb.equal(p.get("key"), id));

        return uniqueResult(query);
    }

    /**
     * {@inheritDoc}
     *
     * @see org.ctan.site.stores.base.AbstractIndexingStore#indexType()
     */
    @Override
    protected IndexType indexType() {

        return IndexType.PKG;
    }

    /**
     * The method <code>list</code> provides means to extract a page of items.
     *
     * @param term the search term
     * @param page the current page
     * @param pageSize the page size
     * @return the paged results
     */
    @Override
    public GeneralPage list(String term, int page, int pageSize,
        String orderBy, boolean asc) {

        if (page < 0 || pageSize < 1) {
            return null;
        }
        CriteriaBuilder cb = currentSession().getCriteriaBuilder();
        var query = criteriaQuery();
        Root<Pkg> pkg = query.from(Pkg.class);
        if (term != null && !term.isBlank()) {
            var t = "%" + term.toLowerCase() + "%";
            query.where(
                cb.or(
                    cb.like(cb.lower(pkg.get("key")), t),
                    cb.like(cb.lower(pkg.get("name")), t)));
        }
        if (orderBy != null && !orderBy.isBlank()) {
            if (asc) {
                query.orderBy(cb.asc(pkg.get(orderBy)));
            } else {
                query.orderBy(cb.desc(pkg.get(orderBy)));
            }
        }
        List<Pkg> hits = list(query);
        var hitCount = hits.size();
        List<Map<String, Object>> list = hits
            .subList(Math.min(page * pageSize, hitCount),
                Math.min((page + 1) * pageSize, hitCount))
            .stream()
            .map(it -> it.toMap())
            .collect(Collectors.toList());
        return GeneralPage.builder()
            .size(hitCount)
            .list(list)
            .build();
    }

    /**
     * {@inheritDoc}
     *
     * @see org.ctan.site.stores.base.AbstractStore#listQuery(java.lang.String,
     *     jakarta.persistence.criteria.CriteriaBuilder,
     *     jakarta.persistence.criteria.CriteriaQuery)
     */
    @Override
    protected Root<Pkg> listQuery(String term, CriteriaBuilder cb,
        CriteriaQuery<Pkg> query) {

        // TODO unimplemented
        throw new UnsupportedOperationException();
    }

    /**
     * {@inheritDoc}
     *
     * @see org.ctan.site.stores.base.AbstractStore#map(java.util.List)
     */
    @Override
    protected List<Map<String, Object>> map(List<Pkg> list) {

        // TODO unimplemented
        throw new UnsupportedOperationException();
    }

    /**
     * The method <code>set</code> provides means to set a single attribute and
     * save the instance.
     *
     * @param pkg the key of the package
     * @param key the key
     * @param value the new value
     * @return the package instance
     */
    public Pkg set(@NonNull String pkg, String key, String value) {

        // TODO unimplemented
        throw new UnsupportedOperationException();
    }

}