<commit_before>package info.elexis.server.core.connector.elexis.services;

import java.util.List;
import java.util.Optional;

import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.eclipse.persistence.config.CacheUsage;
import org.eclipse.persistence.config.QueryHints;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import info.elexis.server.core.connector.elexis.internal.ElexisEntityManager;
import info.elexis.server.core.connector.elexis.jpa.ElexisTypeMap;
import info.elexis.server.core.connector.elexis.jpa.model.annotated.AbstractDBObject;
import info.elexis.server.core.connector.elexis.jpa.model.annotated.AbstractDBObjectIdDeleted;
import info.elexis.server.core.connector.elexis.jpa.model.annotated.AbstractDBObjectIdDeleted_;
import info.elexis.server.core.connector.elexis.jpa.model.annotated.Xid;
import info.elexis.server.core.connector.elexis.jpa.model.annotated.Xid_;
import info.elexis.server.core.connector.elexis.jpa.model.annotated.types.XidQuality;

public abstract class AbstractService<T extends AbstractDBObjectIdDeleted> {

	private final Class<T> clazz;

	protected static Logger log = LoggerFactory.getLogger(AbstractService.class);

	private EntityTransaction transaction;

	protected EntityManager em;
	protected CriteriaBuilder cb;

	public AbstractService(Class<T> clazz) {
		this.clazz = clazz;
		
		em = ElexisEntityManager.createEntityManager();
		cb = em.getCriteriaBuilder();
	}

	/**
	 * Sychronous operation. Element is directly persisted, leading to id creation.
	 * @return
	 */
	public T create() {
		return create(null, true);
	}

	/**
	 * 
	 * @param performCommit
	 *            whether to perform a commit within the operation
	 * @return an instance of T, or <code>null</code> on any error
	 */
	public T create(final boolean performCommit) {
		return create(null, performCommit);
	}

	/**
	 * 
	 * @param id
	 * @param performCommit
	 * @return
	 */
	public T create(String id, final boolean performCommit) {
		try {
			T obj = clazz.newInstance();
			obj.setDeleted(false);
			if (performCommit)
				em.getTransaction().begin();
			if (id != null) {
				obj.setId(id);
			}
			em.persist(obj);
			if (performCommit)
				em.getTransaction().commit();
			return obj;
		} catch (IllegalAccessException | InstantiationException e) {
			log.error("Error creating instance " + clazz.getName(), e);
			return null;
		}
	}

	/**
	 * Create a transaction and flush all open changes onto the database
	 */
	public void flush() {
		em.getTransaction().begin();
		em.flush();
		em.getTransaction().commit();
	}

	/**
	 * Find an object by its primary id.
	 * 
	 * @param id
	 * @param entityClass
	 * @return
	 */
	public Optional<T> findById(Object id) {
		return Optional.ofNullable((T) em.find(clazz, id));
	}

	/**
	 * Returns a list of elements according to fuzzy ID starts with matching.
	 * Useful with deterministic Id strings.
	 * 
	 * @param string
	 * @param entityClass
	 * @return
	 */
	public List<T> findByIdStartingWith(String string) {
		CriteriaBuilder cb = em.getCriteriaBuilder();
		CriteriaQuery<T> c = cb.createQuery(clazz);
		Root<T> r = c.from(clazz);
		Predicate like = cb.like(r.get("id"), string + "%");
		c = c.where(like);
		TypedQuery<T> q = em.createQuery(c);
		return q.getResultList();
	}

	/**
	 * Return all elements of a given type
	 * 
	 * @param includeElementsMarkedDeleted
	 *            if <code>true</code> include elements marked as deleted
	 * @return
	 */
	public List<T> findAll(boolean includeElementsMarkedDeleted) {<commit_msg>Gracefully handle null arguments in AbstractService#find*<commit_after>package info.elexis.server.core.connector.elexis.services;

import java.util.Collections;
import java.util.List;
import java.util.Optional;

import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.eclipse.persistence.config.CacheUsage;
import org.eclipse.persistence.config.QueryHints;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import info.elexis.server.core.connector.elexis.internal.ElexisEntityManager;
import info.elexis.server.core.connector.elexis.jpa.ElexisTypeMap;
import info.elexis.server.core.connector.elexis.jpa.model.annotated.AbstractDBObject;
import info.elexis.server.core.connector.elexis.jpa.model.annotated.AbstractDBObjectIdDeleted;
import info.elexis.server.core.connector.elexis.jpa.model.annotated.AbstractDBObjectIdDeleted_;
import info.elexis.server.core.connector.elexis.jpa.model.annotated.Xid;
import info.elexis.server.core.connector.elexis.jpa.model.annotated.Xid_;
import info.elexis.server.core.connector.elexis.jpa.model.annotated.types.XidQuality;

public abstract class AbstractService<T extends AbstractDBObjectIdDeleted> {

	private final Class<T> clazz;

	protected static Logger log = LoggerFactory.getLogger(AbstractService.class);

	private EntityTransaction transaction;

	protected EntityManager em;
	protected CriteriaBuilder cb;

	public AbstractService(Class<T> clazz) {
		this.clazz = clazz;
		
		em = ElexisEntityManager.createEntityManager();
		cb = em.getCriteriaBuilder();
	}

	/**
	 * Sychronous operation. Element is directly persisted, leading to id creation.
	 * @return
	 */
	public T create() {
		return create(null, true);
	}

	/**
	 * 
	 * @param performCommit
	 *            whether to perform a commit within the operation
	 * @return an instance of T, or <code>null</code> on any error
	 */
	public T create(final boolean performCommit) {
		return create(null, performCommit);
	}

	/**
	 * 
	 * @param id
	 * @param performCommit
	 * @return
	 */
	public T create(String id, final boolean performCommit) {
		try {
			T obj = clazz.newInstance();
			obj.setDeleted(false);
			if (performCommit)
				em.getTransaction().begin();
			if (id != null) {
				obj.setId(id);
			}
			em.persist(obj);
			if (performCommit)
				em.getTransaction().commit();
			return obj;
		} catch (IllegalAccessException | InstantiationException e) {
			log.error("Error creating instance " + clazz.getName(), e);
			return null;
		}
	}

	/**
	 * Create a transaction and flush all open changes onto the database
	 */
	public void flush() {
		em.getTransaction().begin();
		em.flush();
		em.getTransaction().commit();
	}

	/**
	 * Find an object by its primary id.
	 * 
	 * @param id
	 * @param entityClass
	 * @return
	 */
	public Optional<T> findById(Object id) {
		if(id==null) {
			log.warn("null provided as argument to findById(Object id)");
			return Optional.empty();
		}
		return Optional.ofNullable((T) em.find(clazz, id));
	}

	/**
	 * Returns a list of elements according to fuzzy ID starts with matching.
	 * Useful with deterministic Id strings.
	 * 
	 * @param string
	 * @param entityClass
	 * @return
	 */
	public List<T> findByIdStartingWith(String string) {
		if(string==null) {
			log.warn("null provided as argument to findByIdStartingWith(String string)");
			return Collections.emptyList();
		}
		
		CriteriaBuilder cb = em.getCriteriaBuilder();
		CriteriaQuery<T> c = cb.createQuery(clazz);
		Root<T> r = c.from(clazz);
		Predicate like = cb.like(r.get("id"), string + "%");
		c = c.where(like);
		TypedQuery<T> q = em.createQuery(c);
		return q.getResultList();
	}

	/**
	 * Return all elements of a given type
	 * 
	 * @param includeElementsMarkedDeleted
	 *            if <code>true</code> include elements marked as deleted
	 * @return
	 */
	public List<T> findAll(boolean includeElementsMarkedDeleted) {
{'fraction_non_alphanumeric': 0.11697015680323723, 'fraction_numerical': 0.0005058168942842691, 'mean_word_length': 4.2135794330916285, 'pattern_counts': {'":': 0, '<': 29, '<?xml version=': 0, '>': 29, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3115944', 'n_tokens_mistral': 2613, 'n_tokens_neox': 2466, 'n_words': 561}