All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.squid.core.sql.GroupingInterface Maven / Gradle / Ivy

/*******************************************************************************
 * Copyright © Squid Solutions, 2016
 *
 * This file is part of Open Bouquet software.
 *  
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation (version 3 of the License).
 *
 * There is a special FOSS exception to the terms and conditions of the 
 * licenses as they are applied to this program. See LICENSE.txt in
 * the directory of this program distribution.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 *
 * Squid Solutions also offers commercial licenses with additional warranties,
 * professional functionalities or services. If you purchase a commercial
 * license, then it supersedes and replaces any other agreement between
 * you and Squid Solutions (above licenses and LICENSE.txt included).
 * See http://www.squidsolutions.com/EnterpriseBouquet/
 *******************************************************************************/
package com.squid.core.sql;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;

import com.squid.core.domain.IDomain;
import com.squid.core.domain.aggregate.AggregateDomain;
import com.squid.core.domain.operators.ExtendedType;
import com.squid.core.expression.ExpressionAST;
import com.squid.core.expression.scope.ScopeException;
import com.squid.core.sql.model.SQLScopeException;
import com.squid.core.sql.render.IOrderByPiece;
import com.squid.core.sql.render.IPiece;
import com.squid.core.sql.render.ISelectPiece;
import com.squid.core.sql.render.ITypedPiece;
import com.squid.core.sql.render.RenderingException;
import com.squid.core.sql.render.SQLSkin;
import com.squid.core.sql.render.SimpleConstantValuePiece;
import com.squid.core.sql.render.groupby.GroupByElementPiece;
import com.squid.core.sql.render.groupby.GroupType;
import com.squid.core.sql.render.groupby.GroupingSetPiece;
import com.squid.core.sql.render.groupby.IGroupByElementPiece;
import com.squid.core.sql.render.groupby.IGroupByPiece;

public class GroupingInterface {
	
	private ISelect select;
	private boolean forceGroupBy;
	
	public class AdaptiveGroupByPiece
	implements IGroupByPiece
	{

		@Override
		public List getAllPieces() throws ScopeException, SQLScopeException {
			return Collections.unmodifiableList(computeGroupByPieces());
		}
		
		@Override
		public List getPieces(int filter) throws ScopeException, SQLScopeException {
			return Collections.unmodifiableList(computeGroupByPieces(filter));
		}

		@Override
		public String render(SQLSkin skin) throws RenderingException {
			return skin.render(skin, this);
		}
		
	}
	
	protected List createList(List expressions) {
		ArrayList result = new ArrayList();
		for (ExpressionAST e : expressions) {
			result.add(new GroupingElement(e));
		}
		return result;
	}
	
	public class GroupingSet {
		
		private LinkedHashSet set;
		
		public GroupingSet() {
			super();
			this.set = new LinkedHashSet();
		}

		public GroupingSet(List set) {
			super();
			this.set = new LinkedHashSet(set);
		}

		public GroupingSet(GroupingElement singleton) {
			super();
			this.set = new LinkedHashSet();
			this.set.add(singleton);
		}
		
		public void add(IPiece piece) {
			GroupingElement ep = new GroupingElement(piece);
			this.set.add(ep);
			GroupingInterface.this.sets_scope.add(ep);
		}
		
		public void add(ExpressionAST expression) {
			GroupingElement ep = new GroupingElement(expression);
			this.set.add(ep);
			GroupingInterface.this.sets_scope.add(ep);
		}

		public HashSet getSet() {
			return set;
		}
		
		@Override
		public int hashCode() {
			return this.set.hashCode();
		}
		
		@Override
		public boolean equals(Object obj) {
			if (obj instanceof GroupingSet) {
				return this.set.equals(((GroupingSet)obj).set);
			} else {
				return super.equals(obj);
			}
		}
		
	}
	
	private ArrayList sets = new ArrayList();
	private LinkedHashSet sets_scope = new LinkedHashSet();
	
	private ArrayList groups = new ArrayList();
	private LinkedHashSet groups_scope = new LinkedHashSet();
	
	private ArrayList rollup = new ArrayList();
	
	public GroupingInterface(ISelect select) {
		super();
		this.select = select;
	}
	
	/**
	 * clear all groups and grouping sets
	 */
	public void clear() {
		sets = new ArrayList();
		sets_scope = new LinkedHashSet();
		groups = new ArrayList();
		groups_scope = new LinkedHashSet();
	}

	public boolean isForceGroupBy() {
		return forceGroupBy;
	}

	public void setForceGroupBy(boolean forceGroupBy) {
		this.forceGroupBy = forceGroupBy;
	}

	public void addGroupBy(ExpressionAST e) {
		GroupingElement ep = new GroupingElement(e);
		if (groups_scope.add(ep)) {
			groups.add(ep);
		}
	}

	public void addGroupBy(IPiece piece) {
		GroupingElement ep = new GroupingElement(piece);
		if (groups_scope.add(ep)) {
			groups.add(ep);
		}
	}
	
	public List getGroupingSet() {
		return Collections.unmodifiableList(sets);
	}

	public GroupingSet addGroupingSet() {
		GroupingSet x = new GroupingSet();
		sets.add(x);
		return x;
	}

	public void addGroupingSet(ExpressionAST e) {
		GroupingElement ep = new GroupingElement(e);
		if (sets.add(new GroupingSet(Collections.singletonList(ep)))) {
			sets_scope.add(ep);
		}
	}

	public void addGroupingSet(List epl) {
		if (sets.add(new GroupingSet(epl))) {
			sets_scope.addAll(epl);
		}
	}

	public void addGroupingSet(IPiece e) {
		GroupingElement ep = new GroupingElement(e);
		if (sets.add(new GroupingSet(ep))) {
			sets_scope.add(ep);
		}
	}

	/** 
	 * native rollup support
	 * @param axis
	 */
	public void addRollup(ExpressionAST expr) {
		if (rollup==null) {
			rollup = new ArrayList();
		}
		rollup.add(new GroupingElement(expr));
	}
	
	public IGroupByPiece createGroupByPiece() {
		return new AdaptiveGroupByPiece();
	}

	public List computeGroupByPieces() throws ScopeException, SQLScopeException {
		return computeGroupByPieces(IGroupByPiece.ALL);
	}
	
	public List computeGroupByPieces(int filter) throws ScopeException, SQLScopeException {
		ArrayList my_groups = new ArrayList(groups);
		HashSet my_scope = new HashSet();
		if ((filter & IGroupByPiece.GROUP_BY)>0) {
			my_scope.addAll(groups_scope);
		}
		if ((filter & IGroupByPiece.GROUPING_SETS)>0) {
			my_scope.addAll(sets_scope);
		} else {
			if (!sets.isEmpty()) {
				// we have to force using the grouping set as a group by
				for (GroupingSet set : sets) {
					for (GroupingElement piece : set.getSet()) {
						if (!my_scope.contains(piece)) {
							my_groups.add(piece);
							my_scope.add(piece);
						}
					}
				}
			}
		}
		// ---
		if (forceGroupBy) {
			List force_groups = computeGroupByExpressions();
			for (GroupingElement e : force_groups) {
				if (!my_scope.contains(e)) {
					my_groups.add(e);
					my_scope.add(e);
				}
			}
		}
		// --- T130: take care of orderby expression
		{
			List orderBy_groups = insertOrderByExpressions();
			for (GroupingElement e : orderBy_groups) {
				if (!my_scope.contains(e)) {
					my_groups.add(e);
					my_scope.add(e);
				}
			}
		}
		// ---
		ArrayList pieces = new ArrayList();
		for (GroupingElement expr : my_groups) {
			if (expr.isExpression()) {
				IPiece piece = select.createPiece(Context.GROUPBY, select.getScope(), expr.getExpression());
				pieces.add(new GroupByElementPiece(piece));
			} else {
				pieces.add(new GroupByElementPiece(expr.getPiece()));
			}
		}
		//
		if (!sets.isEmpty() && (filter & IGroupByPiece.GROUPING_SETS)>0) {
			GroupingSetPiece grouping = new GroupingSetPiece(new ArrayList());
			for (GroupingSet group : sets) {
				GroupingSetPiece inner = new GroupingSetPiece();
				for (GroupingElement ep : group.getSet()) {
					IPiece piece;
					if (ep.isExpression()) {
						piece = select.createPiece(Context.GROUPBY, select.getScope(), ep.getExpression());
					} else {
						piece = ep.getPiece();
					}
					GroupByElementPiece element = new GroupByElementPiece(piece);
					inner.getPieces().add(element);
				}
				grouping.getPieces().add(inner);
			}
			pieces.add(grouping);
		}
		// rollup support
		if (rollup!=null && !rollup.isEmpty()) {
			GroupingSetPiece piece = new GroupingSetPiece(new ArrayList(), GroupType.ROLLUP);
			for (GroupingElement element : rollup) {
				IPiece rpiece = select.createPiece(Context.GROUPBY, select.getScope(), element.getExpression());
				piece.getPieces().add(new GroupByElementPiece(rpiece));
			}
			pieces.add(piece);
		}
		//
		return pieces;
	}

	/**
	 * create a list of expression that must be added to the group by set
	 * @return
	 * @throws SQLScopeException 
	 */
	private List computeGroupByExpressions() throws SQLScopeException {
		ArrayList groupBy = new ArrayList();
		for (ISelectPiece piece : select.getStatement().getSelectPieces()) {
			Object binding = select.getScope().get(piece);
			if (piece instanceof ITypedPiece) {
				// if the piece is typed, this is a more precise information
				ITypedPiece typed = (ITypedPiece)piece;
				ExtendedType type = typed.getType();
				if (!type.getDomain().isInstanceOf(AggregateDomain.DOMAIN)
				 && !type.getDomain().equals(IDomain.UNKNOWN)) {// this is the case if the piece is a constant or the null value - if so don't need to group-by
					groupBy.add(new GroupingElement(piece.getSelect()));
				}
			} else
			if (binding!=null && binding instanceof ExpressionAST) {
				ExpressionAST expr = (ExpressionAST)binding;
				if (!expr.getImageDomain().isInstanceOf(AggregateDomain.DOMAIN)) {
					groupBy.add(new GroupingElement(expr));
				} else {
					// don't add
				}
			} else {
				// no binding, so we cannot add it...
				// throw new SQLScopeException("invalid select piece");
				// loose hypothesis: if we don't know, add it
				IPiece select = piece.getSelect();
				if (select instanceof SimpleConstantValuePiece == false) {
					groupBy.add(new GroupingElement(select));
				}
			}
		}
		return groupBy;
	}
	
	/**
	 * check the orderBy clause and add expression if not yet in the group by scope and its not an aggregate
	 * T130
	 * @return
	 * @throws SQLScopeException
	 */
	private List insertOrderByExpressions() throws SQLScopeException {
		ArrayList groupBy = new ArrayList();
		for (IOrderByPiece orderBy : select.getStatement().getOrderByPieces()) {
			IPiece piece = orderBy.getPiece();
			Object binding = select.getScope().get(piece);
			if (binding==null) {// not in the select, must handle it
				if (piece instanceof ITypedPiece) {
					// if the piece is typed, this is a more precise information
					ITypedPiece typed = (ITypedPiece)piece;
					ExtendedType type = typed.getType();
					if (!type.getDomain().isInstanceOf(AggregateDomain.DOMAIN)
					 && !type.getDomain().equals(IDomain.UNKNOWN)) {// this is the case if the piece is a constant or the null value - if so don't need to group-by
						groupBy.add(new GroupingElement(piece));
					}
				}
			}
		}
		return groupBy;
	}

	public boolean isEmpty() {
		return groups_scope.isEmpty() && sets_scope.isEmpty();
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy