package ch.elexis.core.jpa.model.adapter.internal;

import java.util.Stack;

import javax.persistence.criteria.CriteriaBuilder;

import ch.elexis.core.jpa.model.adapter.PredicateGroup;

/**
 * Manage PredicateGroup objects using a {@link Stack}.
 * 
 * @author thomas
 *
 */
public class PredicateGroupStack {
	
	private CriteriaBuilder criteriaBuilder;
	private Stack<PredicateGroup> predicateGroups;
	
	public PredicateGroupStack(CriteriaBuilder criteriaBuilder){
		this.criteriaBuilder = criteriaBuilder;
		this.predicateGroups = new Stack<>();
	}
	
	/**
	 * Get the current (top) {@link PredicateGroup} from the stack, or create a new
	 * {@link PredicateGroup} and put it on top of the stack.
	 * 
	 * @return
	 */
	public PredicateGroup getCurrentPredicateGroup(){
		PredicateGroup ret = null;
		if (predicateGroups.isEmpty()) {
			ret = createPredicateGroup();
		} else {
			return predicateGroups.peek();
		}
		return ret;
	}
	
	/**
	 * Create a new {@link PredicateGroup} on the stack.
	 * 
	 * @return
	 */
	public PredicateGroup createPredicateGroup(){
		PredicateGroup ret = new PredicateGroup(criteriaBuilder);
		predicateGroups.push(ret);
		return ret;
	}
	
	/**
	 * Join the 2 last created {@link PredicateGroup}s with and, and return the resulting
	 * {@link PredicateGroup}. It is also the new top of the stack.
	 * 
	 * @return
	 */
	public PredicateGroup andPredicateGroups(){
		if (predicateGroups.size() > 1) {
			PredicateGroup top = predicateGroups.pop();
			PredicateGroup join = predicateGroups.pop();
			return predicateGroups.push(new PredicateGroup(criteriaBuilder,
				criteriaBuilder.and(join.getPredicate(), top.getPredicate())));
		} else {
			throw new IllegalStateException("At least 2 groups required for and operation");
		}
	}
	
	/**
	 * Join the 2 last created {@link PredicateGroup}s with or, and return the resulting
	 * {@link PredicateGroup}. It is also the new top of the stack.
	 * 
	 * @return
	 */
	public PredicateGroup orPredicateGroups(){
		if (predicateGroups.size() > 1) {
			PredicateGroup top = predicateGroups.pop();
			PredicateGroup join = predicateGroups.pop();
			return predicateGroups.push(new PredicateGroup(criteriaBuilder,
				criteriaBuilder.or(join.getPredicate(), top.getPredicate())));
		} else {
			throw new IllegalStateException("At least 2 groups required for or operation");
		}
	}
	
	/**
	 * Get the current size of the {@link PredicateGroup} stack.
	 * 
	 * @return
	 */
	public int getPredicateGroupsSize(){
		return predicateGroups.size();
	}
}

@@ -45,16 +45,43 @@
 ernal;%0A%0A
+import java.util.Iterator;%0A
 import j
@@ -1408,32 +1408,57 @@
 dicateGroups()%7B%0A
+%09%09removeInvalidGroups();%0A
 %09%09if (predicateG
@@ -1469,32 +1469,32 @@
 s.size() %3E 1) %7B%0A
-
 %09%09%09PredicateGrou
@@ -1808,32 +1808,391 @@
 ;%0A%09%09%7D%0A%09%7D%0A%09%0A%09/**%0A
+%09 * Remove any %7B@link PredicateGroup%7D without a predicate.%0A%09 * %0A%09 */%0A%09private void removeInvalidGroups()%7B%0A%09%09for (Iterator%3CPredicateGroup%3E iterator = predicateGroups.iterator(); iterator.hasNext();) %7B%0A%09%09%09PredicateGroup predicateGroup = (PredicateGroup) iterator.next();%0A%09%09%09if (predicateGroup.getPredicate() == null) %7B%0A%09%09%09%09iterator.remove();%0A%09%09%09%7D%0A%09%09%7D%0A%09%7D%0A%09%0A%09/**%0A
 %09 * Join the 2 l
@@ -2340,32 +2340,32 @@
  * @return%0A%09 */%0A
-
 %09public Predicat
@@ -2392,16 +2392,41 @@
 oups()%7B%0A
+%09%09removeInvalidGroups();%0A
 %09%09if (pr

fix NPE by adding removeInvalidGroups to PredicateGroupStack