PaginatedExpressionSQL.java

/*
 * GovWay - A customizable API Gateway 
 * https://govway.org
 * 
 * Copyright (c) 2005-2024 Link.it srl (https://link.it).
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 3, as published by
 * the Free Software Foundation.
 *
 * 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.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
package org.openspcoop2.generic_project.expression.impl.sql;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

import org.openspcoop2.generic_project.beans.FunctionField;
import org.openspcoop2.generic_project.beans.IField;
import org.openspcoop2.generic_project.beans.IModel;
import org.openspcoop2.generic_project.exception.ExpressionException;
import org.openspcoop2.generic_project.exception.ExpressionNotImplementedException;
import org.openspcoop2.generic_project.expression.LikeMode;
import org.openspcoop2.generic_project.expression.impl.BetweenExpressionImpl;
import org.openspcoop2.generic_project.expression.impl.Comparator;
import org.openspcoop2.generic_project.expression.impl.ComparatorExpressionImpl;
import org.openspcoop2.generic_project.expression.impl.ConjunctionExpressionImpl;
import org.openspcoop2.generic_project.expression.impl.DateTimePartExpressionImpl;
import org.openspcoop2.generic_project.expression.impl.DayFormatExpressionImpl;
import org.openspcoop2.generic_project.expression.impl.InExpressionImpl;
import org.openspcoop2.generic_project.expression.impl.LikeExpressionImpl;
import org.openspcoop2.generic_project.expression.impl.PaginatedExpressionImpl;
import org.openspcoop2.generic_project.expression.impl.formatter.IObjectFormatter;
import org.openspcoop2.utils.TipiDatabase;
import org.openspcoop2.utils.sql.DateTimePartEnum;
import org.openspcoop2.utils.sql.DayFormatEnum;
import org.openspcoop2.utils.sql.ISQLQueryObject;

/**
 * PaginatedExpressionSQL
 * 
 * @author Poli Andrea (apoli@link.it)
 * @author $Author$
 * @version $Rev$, $Date$
 */
public class PaginatedExpressionSQL extends PaginatedExpressionImpl {

	private boolean throwExpressionNotInitialized = false;

	private TipiDatabase databaseType;
	public TipiDatabase getDatabaseType() {
		return this.databaseType;
	}
	
	private ISQLFieldConverter sqlFieldConverter;
	public ISQLFieldConverter getSqlFieldConverter() {
		return this.sqlFieldConverter;
	}
	public void setSqlFieldConverter(ISQLFieldConverter sqlFieldConverter) {
		this.sqlFieldConverter = sqlFieldConverter;
	}
	
	private boolean usedForCountExpression = false;
	public boolean isUsedForCountExpression() {
		return this.usedForCountExpression;
	}
	public void setUsedForCountExpression(boolean usedForCountExpression) {
		this.usedForCountExpression = usedForCountExpression;
	}
	
	public PaginatedExpressionSQL(ISQLFieldConverter sqlFieldConverter) throws ExpressionException {
		super();
		this.sqlFieldConverter = sqlFieldConverter;
		if(this.sqlFieldConverter!=null){
			this.databaseType = this.sqlFieldConverter.getDatabaseType();
		}
	}
	public PaginatedExpressionSQL(ISQLFieldConverter sqlFieldConverter,IObjectFormatter objectFormatter) throws ExpressionException{
		super(objectFormatter);
		this.sqlFieldConverter = sqlFieldConverter;
		if(this.sqlFieldConverter!=null){
			this.databaseType = this.sqlFieldConverter.getDatabaseType();
		}
	}
	public PaginatedExpressionSQL(ExpressionSQL expression) throws ExpressionException {
		super(expression);
		this.sqlFieldConverter = expression.getSqlFieldConverter();
		this.fieldsManuallyAdd = expression.getFieldsManuallyAdd();
		/**this.checkFieldManuallyAdd = expression.checkFieldManuallyAdd;*/
		if(this.sqlFieldConverter!=null){
			this.databaseType = this.sqlFieldConverter.getDatabaseType();
		}
	}
	
	private List<Object> fieldsManuallyAdd = new ArrayList<>();
	public List<Object> getFieldsManuallyAdd() {
		return this.fieldsManuallyAdd;
	}
	public void removeFieldManuallyAdd(Object o) {
		if(this.fieldsManuallyAdd.contains(o)){
			this.fieldsManuallyAdd.remove(o);
		}
	}
	
	private boolean checkFieldManuallyAdd = true;
	public void setCheckFieldManuallyAdd(boolean checkFieldManuallyAdd) {
		this.checkFieldManuallyAdd = checkFieldManuallyAdd;
	}
	@Override
	public boolean inUseField(IField field,boolean checkOnlyWhereCondition) throws ExpressionNotImplementedException, ExpressionException {
		return ExpressionSQL.inUse(field, checkOnlyWhereCondition, super.inUseField(field,checkOnlyWhereCondition), this.getFieldsManuallyAdd(),this.checkFieldManuallyAdd);
	}
	@Override
	public boolean inUseModel(IModel<?> model,boolean checkOnlyWhereCondition) throws ExpressionNotImplementedException, ExpressionException {
		return ExpressionSQL.inUse(model, checkOnlyWhereCondition, super.inUseModel(model,checkOnlyWhereCondition), this.getFieldsManuallyAdd(),this.checkFieldManuallyAdd);
	}
	@Override
	public List<IField> getFields(boolean onlyWhereCondition) throws ExpressionNotImplementedException, ExpressionException{
		return ExpressionSQL.getFields(onlyWhereCondition, super.getFields(onlyWhereCondition), this.getFieldsManuallyAdd(),this.checkFieldManuallyAdd);
	}
	
	
	/* ************ COMPARATOR *********** */
	
	@Override
	protected Comparator getCorrectComparator(Comparator comparator){
		return ExpressionSQL.getCorrectComparator(comparator, this.databaseType);
	}
	

	
	/* ************ TO SQL *********** */
	
	private String toSqlEngine(SQLMode mode,List<Object> oggettiPreparedStatement,Map<String, Object> oggettiJPA)throws ExpressionException{
			
		ISQLExpression sqlExpression = null;
		if(this.expressionEngine!=null){
			if(this.expressionEngine instanceof ISQLExpression){
				sqlExpression = (ISQLExpression)this.expressionEngine;
			}else{
				throw new ExpressionException("ExpressioneEngine (type:"+this.expressionEngine.getClass().getName()+") is not as cast with "+ISQLExpression.class.getName());
			}
		}else{
			if(this.throwExpressionNotInitialized)
				throw new ExpressionException("Expression is not initialized");
		}
		
		String s = null;
		if(this.expressionEngine!=null){
			switch (mode) {
			case STANDARD:
				s = sqlExpression.toSql();
				break;
			case PREPARED_STATEMENT:
				s = sqlExpression.toSqlPreparedStatement(oggettiPreparedStatement);
				break;
			case JPA:
				s = sqlExpression.toSqlJPA(oggettiJPA);
				break;
			}
		}else{
			s = "";
		}
		
		StringBuilder bf = new StringBuilder();
		bf.append(s);

		bf.append(ExpressionSQL.sqlGroupBy(this.sqlFieldConverter, this.getGroupByFields()));
		
		bf.append(ExpressionSQL.sqlOrder(this.sqlFieldConverter, this.getSortOrder(), this.getOrderedFields()));
				
		if(!SQLMode.JPA.equals(mode)){
			if(this.getLimit()!=null && this.getLimit()>=0){
				bf.append(" LIMIT "+this.getLimit());
			}
			if(this.getOffset()!=null && this.getOffset()>=0){
				bf.append(" OFFSET "+this.getOffset());
			}
		}
		
		bf.append(ExpressionSQL.sqlForceIndex(this.sqlFieldConverter, this.getForceIndexes())); // Lo metto in fondo tanto sono commenti
		
		return bf.toString();
		
	}
	
	private void toSqlEngine(ISQLQueryObject sqlQueryObject,SQLMode mode,List<Object> oggettiPreparedStatement,Map<String, Object> oggettiJPA)throws ExpressionException{
	
		ISQLExpression sqlExpression = null;
		if(this.expressionEngine!=null){
			if(this.expressionEngine instanceof ISQLExpression){
				sqlExpression = (ISQLExpression)this.expressionEngine;
			}else{
				throw new ExpressionException("ExpressioneEngine (type:"+this.expressionEngine.getClass().getName()+") is not as cast with "+ISQLExpression.class.getName());
			}
		}else{
			if(this.throwExpressionNotInitialized)
				throw new ExpressionException("Expression is not initialized");
		}
		
		if(this.expressionEngine!=null){
			switch (mode) {
			case STANDARD:
				sqlExpression.toSql(sqlQueryObject);
				break;
			case PREPARED_STATEMENT:
				sqlExpression.toSqlPreparedStatement(sqlQueryObject,oggettiPreparedStatement);
				break;
			case JPA:
				sqlExpression.toSqlJPA(sqlQueryObject,oggettiJPA);
				break;
			}
		}
		
		try{
				
			// GroupBy
			ExpressionSQL.sqlGroupBy(this.sqlFieldConverter, sqlQueryObject, this.getGroupByFields());
						
			// OrderBy
			ExpressionSQL.sqlOrder(this.sqlFieldConverter, sqlQueryObject,this.getSortOrder(), this.getOrderedFields(), this.getGroupByFields());
						
			// Aggiungo select field relativi all'aggregazione
			ExpressionSQL.sqlGroupBySelectField(this.sqlFieldConverter, sqlQueryObject, this.getFieldsManuallyAdd(), 
					this.getGroupByFields(), this.usedForCountExpression);
						
			if(!SQLMode.JPA.equals(mode)){
				if(this.getLimit()!=null && this.getLimit()>=0){
					sqlQueryObject.setLimit(this.getLimit());
				}
				
				if(this.getOffset()!=null && this.getOffset()>=0){
					sqlQueryObject.setOffset(this.getOffset());
				}
			}
			
			// ForceIndex
			ExpressionSQL.sqlForceIndex(this.sqlFieldConverter, sqlQueryObject, this.getForceIndexes());
			
		}catch(Exception e){
			throw new ExpressionException(e);
		}
		
	}
	
	public String toSql() throws ExpressionException{
		return toSqlEngine(SQLMode.STANDARD, null, null);
	}
	protected String toSqlPreparedStatement(List<Object> oggetti) throws ExpressionException {
		return toSqlEngine(SQLMode.PREPARED_STATEMENT, oggetti, null);
	}
	protected String toSqlJPA(Map<String, Object> oggetti) throws ExpressionException {
		return toSqlEngine(SQLMode.JPA, null, oggetti);
	}
	
	public void toSql(ISQLQueryObject sqlQueryObject)throws ExpressionException{
		toSqlEngine(sqlQueryObject,SQLMode.STANDARD, null, null);
	}
	public void toSqlWithFromCondition(ISQLQueryObject sqlQueryObject,String tableNamePrincipale) throws ExpressionException, ExpressionNotImplementedException{
		
		// preparo condizione di where
		this.toSql(sqlQueryObject);
		
		// aggiungo condizione di from
		ExpressionSQL.sqlFrom(sqlQueryObject, this.getFields(false), this.getSqlFieldConverter(), tableNamePrincipale, this.getFieldsManuallyAdd(),
				this.getOrderedFields(),this.getGroupByFields());
	}
	
	protected void toSqlPreparedStatement(ISQLQueryObject sqlQueryObject,List<Object> oggetti)throws ExpressionException{
		toSqlEngine(sqlQueryObject,SQLMode.PREPARED_STATEMENT, oggetti, null);
	}
	protected void toSqlPreparedStatementWithFromCondition(ISQLQueryObject sqlQueryObject,List<Object> oggetti,String tableNamePrincipale) throws ExpressionException, ExpressionNotImplementedException{
		
		// preparo condizione di where
		this.toSqlPreparedStatement(sqlQueryObject, oggetti);
		
		// aggiungo condizione di from
		ExpressionSQL.sqlFrom(sqlQueryObject, this.getFields(false), this.getSqlFieldConverter(), tableNamePrincipale, this.getFieldsManuallyAdd(),
				this.getOrderedFields(),this.getGroupByFields());
	}
	
	protected void toSqlJPA(ISQLQueryObject sqlQueryObject,Map<String, Object> oggetti)throws ExpressionException{
		toSqlEngine(sqlQueryObject,SQLMode.JPA, null, oggetti);
	}
	protected void toSqlJPAWithFromCondition(ISQLQueryObject sqlQueryObject,Map<String, Object> oggetti,String tableNamePrincipale) throws ExpressionException, ExpressionNotImplementedException{
		
		// preparo condizione di where
		this.toSqlJPA(sqlQueryObject, oggetti);
		
		// aggiungo condizione di from
		ExpressionSQL.sqlFrom(sqlQueryObject, this.getFields(false), this.getSqlFieldConverter(), tableNamePrincipale, this.getFieldsManuallyAdd(),
				this.getOrderedFields(),this.getGroupByFields());
	}
	
	public void addField(ISQLQueryObject sqlQueryObject, IField field, boolean appendTablePrefix)throws ExpressionException{
		ExpressionSQL.addFieldEngine(sqlQueryObject,this.getSqlFieldConverter(),field, null, appendTablePrefix);
		this.getFieldsManuallyAdd().add(field);
	}
	public void addField(ISQLQueryObject sqlQueryObject, IField field, String aliasField, boolean appendTablePrefix)throws ExpressionException{
		ExpressionSQL.addFieldEngine(sqlQueryObject,this.getSqlFieldConverter(),field, aliasField, appendTablePrefix);
		this.getFieldsManuallyAdd().add(field);
	}
	public void addAliasField(ISQLQueryObject sqlQueryObject, IField field, boolean appendTablePrefix)throws ExpressionException{
		ExpressionSQL.addAliasFieldEngine(sqlQueryObject,this.getSqlFieldConverter(),field, null, appendTablePrefix);
		this.getFieldsManuallyAdd().add(field);
	}
	public void addField(ISQLQueryObject sqlQueryObject, FunctionField field, boolean appendTablePrefix)throws ExpressionException{
		ExpressionSQL.addFieldEngine(sqlQueryObject,this.getSqlFieldConverter(),field, null, appendTablePrefix);
		this.getFieldsManuallyAdd().add(field);
	}

	
	
	/* ************ OBJECTS ************ */
	@Override
	protected ComparatorExpressionImpl getComparatorExpression(IField field, Object value, Comparator c) {
		return new ComparatorExpressionSQL(this.sqlFieldConverter,this.objectFormatter,field,value,c);
	}
	@Override
	protected BetweenExpressionImpl getBetweenExpression(IField field, Object lower, Object high) {
		return new BetweenExpressionSQL(this.sqlFieldConverter,this.objectFormatter,field,lower,high);
	}
	@Override
	protected InExpressionImpl getInExpression(IField field, Object... values) {
		List<Object> lista = new ArrayList<>();
		if(values!=null && values.length>0){
			lista.addAll(Arrays.asList(values));
		}
		return new InExpressionSQL(this.sqlFieldConverter,this.objectFormatter,field, lista);
	}
	@Override
	protected LikeExpressionImpl getLikeExpression(IField field, String value, LikeMode mode, boolean caseInsensitive) {
		return new LikeExpressionSQL(this.sqlFieldConverter,this.objectFormatter,field, value, mode, caseInsensitive);
	}
	@Override
	protected DateTimePartExpressionImpl getDateTimePartExpression(IField field, String value, DateTimePartEnum dateTimePartEnum) {
		return new DateTimePartExpressionSQL(this.sqlFieldConverter,this.objectFormatter,field, value, dateTimePartEnum);
	}
	@Override
	protected DayFormatExpressionImpl getDayFormatExpression(IField field, String value, DayFormatEnum dayFormatEnum) {
		return new DayFormatExpressionSQL(this.sqlFieldConverter,this.objectFormatter,field, value, dayFormatEnum);
	}
	@Override
	protected ConjunctionExpressionImpl getConjunctionExpression() {
		return new ConjunctionExpressionSQL(this.sqlFieldConverter,this.objectFormatter);
	}
	
}