adalid.core.programmers.AbstractSqlProgrammer.java Source code

Java tutorial

Introduction

Here is the source code for adalid.core.programmers.AbstractSqlProgrammer.java

Source

/*
 * Este programa es software libre; usted puede redistribuirlo y/o modificarlo bajo los trminos
 * de la licencia "GNU General Public License" publicada por la Fundacin "Free Software Foundation".
 * Este programa se distribuye con la esperanza de que pueda ser til, pero SIN NINGUNA GARANTIA;
 * vea la licencia "GNU General Public License" para obtener cursor informacion.
 */
package adalid.core.programmers;

import adalid.commons.util.StrUtils;
import adalid.commons.util.TimeUtils;
import adalid.core.Instance;
import adalid.core.Operation;
import adalid.core.Primitive;
import adalid.core.data.types.CharacterData;
import adalid.core.enums.ComparisonOp;
import adalid.core.enums.DataAggregateOp;
import adalid.core.enums.InheritanceMappingStrategy;
import adalid.core.enums.QueryJoinOp;
import adalid.core.enums.RowsAggregateOp;
import adalid.core.enums.ScalarOp;
import adalid.core.enums.SortOption;
import adalid.core.enums.SpecialBooleanValue;
import adalid.core.enums.SpecialCharacterValue;
import adalid.core.enums.SpecialEntityValue;
import adalid.core.enums.SpecialNumericValue;
import adalid.core.enums.SpecialTemporalValue;
import adalid.core.enums.SqlQualifierType;
import adalid.core.enums.StandardRelationalOp;
import adalid.core.enums.ViewFieldAggregation;
import adalid.core.expressions.VariantX;
import adalid.core.interfaces.Artifact;
import adalid.core.interfaces.ComparisonX;
import adalid.core.interfaces.ConditionalX;
import adalid.core.interfaces.DataAggregateX;
import adalid.core.interfaces.Entity;
import adalid.core.interfaces.Expression;
import adalid.core.interfaces.NamedValue;
import adalid.core.interfaces.Operator;
import adalid.core.interfaces.OrderedPairX;
import adalid.core.interfaces.PersistentEntity;
import adalid.core.interfaces.Property;
import adalid.core.interfaces.RowsAggregateX;
import adalid.core.interfaces.ScalarX;
import adalid.core.interfaces.SearchCriteria;
import adalid.core.interfaces.Segment;
import adalid.core.interfaces.SortCriteria;
import adalid.core.interfaces.SqlProgrammer;
import adalid.core.interfaces.TypedArtifact;
import adalid.core.interfaces.ValuedArtifact;
import adalid.core.sql.QueryJoin;
import adalid.core.sql.QueryTable;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Date;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

/**
 * @author Jorge Campins
 */
public abstract class AbstractSqlProgrammer extends AbstractProgrammer implements SqlProgrammer {

    private static final Logger logger = Logger.getLogger(SqlProgrammer.class);

    private static final String EMPTY = "";

    private static final String EXPRESSION_DOLLAR_INFIX = "$";

    private static final String EXPRESSION_SELECT_INFIX = "$select_";

    // <editor-fold defaultstate="collapsed" desc="string constants">
    protected static final String SEP$ = COM$;
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="SQL comparison operators">
    protected String getIsNull() {
        return "is null";
    }

    protected String getIsNotNull() {
        return "is not null";
    }

    protected String getIsTrue() {
        return "is true";
    }
    //
    //  protected String getIsNotTrue() {
    //      return "is not true";
    //  }

    protected String getIsFalse() {
        return "is false";
    }
    //
    //  protected String getIsNotFalse() {
    //      return "is not false";
    //  }

    protected String getEQ() {
        return "=";
    }

    protected String getNEQ() {
        return "<>";
    }

    protected String getGT() {
        return ">";
    }

    protected String getLTEQ() {
        return "<=";
    }

    protected String getGTEQ() {
        return ">=";
    }

    protected String getLT() {
        return "<";
    }

    protected String getLike() {
        return "like";
    }

    protected String getNotLike() {
        return "not like";
    }

    protected String getIn() {
        return "in";
    }

    protected String getNotIn() {
        return "not in";
    }

    protected String getIsNullOr() {
        return "is null or";
    }

    protected String getIsNotNullAnd() {
        return "is not null and";
    }

    protected String getBetween() {
        return "between";
    }

    protected String getNotBetween() {
        return "not between";
    }

    protected String getExists() {
        return "exists";
    }

    protected String getNotExists() {
        return "not exists";
    }
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="SQL comparison compound operators">
    protected String getIsNullOr(String columna) {
        return columna + SPC$ + getIsNullOr() + SPC$ + columna;
    }

    protected String getIsNullOr(String columna, String columnb) {
        return columna + SPC$ + getIsNullOr() + SPC$ + columnb + SPC$ + getIsNotNullAnd() + SPC$ + columna;
    }

    protected String getIsNotNullAnd(String columna) {
        return columna + SPC$ + getIsNotNullAnd() + SPC$ + columna;
    }

    protected String getIsNotNullAnd(String columna, String columnb) {
        return columna + SPC$ + getIsNotNullAnd() + SPC$ + columnb + SPC$ + getIsNotNullAnd() + SPC$ + columna;
    }
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="referential integrity options">
    protected abstract String getRestricted();

    protected abstract String getCascade();

    protected abstract String getNullify();

    protected abstract String getNoAction();
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="join operators">
    protected String getInnerJoin() {
        return "inner join";
    }

    protected String getLeftJoin() {
        return "left outer join";
    }

    protected String getRightJoin() {
        return "right outer join";
    }

    protected String getFullJoin() {
        return "full join";
    }

    protected String getCrossJoin() {
        return "cross join";
    }

    protected String getDefaultJoin() {
        return "join";
    }
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="aggregate functions">
    protected String getCount() {
        return "count";
    }

    protected String getMaximum() {
        return "max";
    }

    protected String getMinimum() {
        return "min";
    }

    protected String getSum() {
        return "sum";
    }

    protected String getAverage() {
        return "avg";
    }
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="n-ary operators">
    protected String getConcat() {
        return "||";
    }

    protected String getAnd() {
        return "and";
    }

    protected String getOr() {
        return "or";
    }

    protected String getAdd() {
        return "+";
    }

    protected String getMultiply() {
        return "*";
    }

    protected String getCoalesce() {
        return "coalesce";
    }
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="unary operators">
    protected String getNot() {
        return "not";
    }

    protected String getModulus() {
        return "abs";
    }

    protected String getOpposite() {
        return "(-1)*";
    }

    protected String getReciprocal() {
        return "1/";
    }

    protected String getNz() {
        return "coalesce";
    }

    protected String getCast() {
        return "cast";
    }
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="sort operators">
    protected String getAscending() {
        return "asc";
    }

    protected String getDescending() {
        return "desc";
    }
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="keywords">
    protected String getTrue() {
        return "true";
    }

    protected String getFalse() {
        return "false";
    }

    protected String getCurrentDate() {
        return "current_date";
    }

    protected String getCurrentTime() {
        return "current_time";
    }

    protected String getCurrentTimestamp() {
        return "current_timestamp";
    }

    protected String getNull() {
        return "null";
    }

    protected String getNotNull() {
        return "not null";
    }
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="zeroes">
    protected String getZeroChar() {
        return SQM$ + SPC$ + SQM$;
    }

    protected String getZeroString() {
        return SQM$ + SQM$;
    }

    protected String getZeroNumber() {
        return "0";
    }

    protected String getZeroDate() {
        return SQM$ + "1970-01-01" + SQM$;
    }

    protected String getZeroTime() {
        return SQM$ + "00:00:00" + SQM$;
    }

    protected String getZeroTimestamp() {
        return SQM$ + "1970-01-01 00:00:00" + SQM$;
    }
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="select statement keywords">
    protected String getSelect() {
        return "select";
    }

    protected String getInto() {
        return "into";
    }

    protected String getFrom() {
        return "from";
    }

    protected String getAs() {
        return "as";
    }

    protected String getOn() {
        return "on";
    }

    protected String getWhere() {
        return "where";
    }

    protected String getOrderBy() {
        return "order by";
    }
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="case keywords">
    protected String getCase() {
        return "case";
    }

    protected String getWhen() {
        return "when";
    }

    protected String getThen() {
        return "then";
    }

    protected String getElse() {
        return "else";
    }

    protected String getEnd() {
        return "end";
    }

    protected String getCaseWhenThenPattern() {
        return "case when {0} then {1} end";
    }

    protected String getCaseWhenThenElsePattern() {
        return "case when {0} then {1} else {2} end";
    }
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="like special characters">
    protected String getLikeSingle() {
        return "_";
    }

    protected String getLikeString() {
        return "%";
    }
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="variables prefix and suffix">
    protected String getVariablesPrefix() {
        return "_";
    }

    protected String getVariablesSuffix() {
        return "$";
    }

    protected String getRecordVariableName() {
        return getSqlVariableName("record");
    }

    protected String getRecordVariableName(int index) {
        return getSqlVariableName("record" + index);
    }

    protected String getRowVariableName() {
        return getSqlVariableName("row");
    }

    protected String getRowVariableName(int index) {
        return getSqlVariableName("row" + index);
    }

    protected String getValueVariableName() {
        return getSqlVariableName("value");
    }

    protected String getValueVariableName(int index) {
        return getSqlVariableName("value" + index);
    }
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="getString...">
    public String getString(Object obj) {
        if (obj == null) {
            return null;
        } else if (obj instanceof Date) {
            return TimeUtils.jdbcDateString(obj);
        } else if (obj instanceof Time) {
            return TimeUtils.jdbcTimeString(obj);
        } else if (obj instanceof java.util.Date) {
            return TimeUtils.jdbcTimestampString(obj);
        } else {
            return obj.toString();
        }
    }

    public String getDelimitedString(Object obj) {
        String string = getString(obj);
        if (string == null) {
            return null;
        } else if (obj instanceof String) {
            return SQM$ + string + SQM$;
        } else if (obj instanceof Date) {
            return SQM$ + string + SQM$;
        } else if (obj instanceof Time) {
            return SQM$ + string + SQM$;
        } else if (obj instanceof java.util.Date) {
            return SQM$ + string + SQM$;
        } else {
            return string;
        }
    }

    protected String getSearchCriteriaString(SearchCriteria criteria) {
        ComparisonOp comparacion = criteria.getComparacion();
        if (comparacion == null) {
            return null;
        }
        String string = null;
        //      String columna = StringUtils.trimToNull(criterio.getColumna(dominio));
        String columna = StringUtils.trimToNull(criteria.getColumna());
        Object valor = criteria.getValor();
        if (columna == null) {
            if (valor != null && valor instanceof String) {
                String expresion = (String) valor;
                switch (comparacion) {
                case EXISTS:
                    string = getExists() + LRB$ + expresion + RRB$;
                    break;
                case NOT_EXISTS:
                    string = getNotExists() + LRB$ + expresion + RRB$;
                    break;
                }
            }
            return string == null ? null : LRB$ + string.trim() + RRB$;
        }
        if (valor == null) {
            switch (comparacion) {
            case IS_NULL:
                string = columna + SPC$ + getIsNull();
                break;
            case IS_NOT_NULL:
                string = columna + SPC$ + getIsNotNull();
                break;
            }
            return string == null ? null : LRB$ + string.trim() + RRB$;
        }
        switch (comparacion) {
        case IS_NULL:
            string = columna + SPC$ + getIsNull();
            break;
        case IS_NOT_NULL:
            string = columna + SPC$ + getIsNotNull();
            break;
        case EQ:
            string = columna + SPC$ + getEQ() + SPC$ + getDelimitedString(valor);
            break;
        case NEQ:
            string = columna + SPC$ + getNEQ() + SPC$ + getDelimitedString(valor);
            break;
        case GT:
            string = columna + SPC$ + getGT() + SPC$ + getDelimitedString(valor);
            break;
        case LTEQ:
            string = columna + SPC$ + getLTEQ() + SPC$ + getDelimitedString(valor);
            break;
        case GTEQ:
            string = columna + SPC$ + getGTEQ() + SPC$ + getDelimitedString(valor);
            break;
        case LT:
            string = columna + SPC$ + getLT() + SPC$ + getDelimitedString(valor);
            break;
        case STARTS_WITH:
            string = columna + SPC$ + getLike() + SPC$ + getDelimitedString(startsWithValue(valor));
            break;
        case NOT_STARTS_WITH:
            string = columna + SPC$ + getNotLike() + SPC$ + getDelimitedString(startsWithValue(valor));
            break;
        case CONTAINS:
            string = columna + SPC$ + getLike() + SPC$ + getDelimitedString(containsValue(valor));
            break;
        case NOT_CONTAINS:
            string = columna + SPC$ + getNotLike() + SPC$ + getDelimitedString(containsValue(valor));
            break;
        case ENDS_WITH:
            string = columna + SPC$ + getLike() + SPC$ + getDelimitedString(endsWithValue(valor));
            break;
        case NOT_ENDS_WITH:
            string = columna + SPC$ + getNotLike() + SPC$ + getDelimitedString(endsWithValue(valor));
            break;
        case IN:
            string = columna + SPC$ + getIn() + LRB$ + getString(valor) + RRB$;
            break;
        case NOT_IN:
            string = columna + SPC$ + getNotIn() + LRB$ + getString(valor) + RRB$;
            break;
        case IS_NULL_OR_EQ:
            string = getIsNullOr(columna) + SPC$ + getEQ() + SPC$ + getDelimitedString(valor);
            break;
        case IS_NULL_OR_NEQ:
            string = getIsNullOr(columna) + SPC$ + getNEQ() + SPC$ + getDelimitedString(valor);
            break;
        case IS_NULL_OR_GT:
            string = getIsNullOr(columna) + SPC$ + getGT() + SPC$ + getDelimitedString(valor);
            break;
        case IS_NULL_OR_LTEQ:
            string = getIsNullOr(columna) + SPC$ + getLTEQ() + SPC$ + getDelimitedString(valor);
            break;
        case IS_NULL_OR_GTEQ:
            string = getIsNullOr(columna) + SPC$ + getGTEQ() + SPC$ + getDelimitedString(valor);
            break;
        case IS_NULL_OR_LT:
            string = getIsNullOr(columna) + SPC$ + getLT() + SPC$ + getDelimitedString(valor);
            break;
        case IS_NULL_OR_STARTS_WITH:
            string = getIsNullOr(columna) + SPC$ + getLike() + SPC$ + getDelimitedString(startsWithValue(valor));
            break;
        case IS_NULL_OR_NOT_STARTS_WITH:
            string = getIsNullOr(columna) + SPC$ + getNotLike() + SPC$ + getDelimitedString(startsWithValue(valor));
            break;
        case IS_NULL_OR_CONTAINS:
            string = getIsNullOr(columna) + SPC$ + getLike() + SPC$ + getDelimitedString(containsValue(valor));
            break;
        case IS_NULL_OR_NOT_CONTAINS:
            string = getIsNullOr(columna) + SPC$ + getNotLike() + SPC$ + getDelimitedString(containsValue(valor));
            break;
        case IS_NULL_OR_ENDS_WITH:
            string = getIsNullOr(columna) + SPC$ + getLike() + SPC$ + getDelimitedString(endsWithValue(valor));
            break;
        case IS_NULL_OR_NOT_ENDS_WITH:
            string = getIsNullOr(columna) + SPC$ + getNotLike() + SPC$ + getDelimitedString(endsWithValue(valor));
            break;
        case IS_NULL_OR_IN:
            string = getIsNullOr(columna) + SPC$ + getIn() + LRB$ + getString(valor) + RRB$;
            break;
        case IS_NULL_OR_NOT_IN:
            string = getIsNullOr(columna) + SPC$ + getNotIn() + LRB$ + getString(valor) + RRB$;
            break;
        }
        return string == null || StringUtils.isBlank(string) ? null : LRB$ + string.trim() + RRB$;
    }

    protected String getSortCriteriaString(SortCriteria criteria) {
        String columna = StringUtils.trimToNull(criteria.getColumna());
        if (columna == null) {
            return null;
        }
        if (SortOption.DESC.equals(criteria.getOrden())) {
            return columna + SPC$ + getDescending();
        } else {
            //          return columna + SPC$ + getAscending();
            return columna;
        }
    }
    // </editor-fold>

    /**
     * @param artifact
     * @return the SQL-ish name
     */
    @Override
    public String getSqlishName(Artifact artifact) {
        return artifact == null ? null : getArtifactSqlName(artifact, 0);
    }

    /**
     * @param artifact
     * @return the SQL name
     */
    @Override
    public String getSqlName(Artifact artifact) {
        int maxIdentifierLength = getMaxIdentifierLength();
        return getSqlName(null, artifact, null, maxIdentifierLength);
    }

    /**
     * @param artifact
     * @param maxIdentifierLength
     * @return the SQL name
     */
    @Override
    public String getSqlName(Artifact artifact, int maxIdentifierLength) {
        return getSqlName(null, artifact, null, maxIdentifierLength);
    }

    /**
     * @param prefix
     * @param artifact
     * @return the SQL name
     */
    @Override
    public String getSqlName(String prefix, Artifact artifact) {
        int maxIdentifierLength = getMaxIdentifierLength();
        return getSqlName(prefix, artifact, null, maxIdentifierLength);
    }

    /**
     * @param prefix
     * @param artifact
     * @param maxIdentifierLength
     * @return the SQL name
     */
    @Override
    public String getSqlName(String prefix, Artifact artifact, int maxIdentifierLength) {
        return getSqlName(prefix, artifact, null, maxIdentifierLength);
    }

    /**
     * @param artifact
     * @param suffix
     * @return the SQL name
     */
    @Override
    public String getSqlName(Artifact artifact, String suffix) {
        int maxIdentifierLength = getMaxIdentifierLength();
        return getSqlName(null, artifact, suffix, maxIdentifierLength);
    }

    /**
     * @param artifact
     * @param suffix
     * @param maxIdentifierLength
     * @return the SQL name
     */
    @Override
    public String getSqlName(Artifact artifact, String suffix, int maxIdentifierLength) {
        return getSqlName(null, artifact, suffix, maxIdentifierLength);
    }

    /**
     * @param prefix
     * @param artifact
     * @param suffix
     * @return the SQL name
     */
    @Override
    public String getSqlName(String prefix, Artifact artifact, String suffix) {
        int maxIdentifierLength = getMaxIdentifierLength();
        return getSqlName(prefix, artifact, suffix, maxIdentifierLength);
    }

    /**
     * @param prefix
     * @param artifact
     * @param suffix
     * @param maxIdentifierLength
     * @return the SQL name
     */
    @Override
    public String getSqlName(String prefix, Artifact artifact, String suffix, int maxIdentifierLength) {
        String fullName = getFullLengthSqlName(artifact);
        if (fullName == null) {
            return null;
        }
        int mil = getMaxIdentifierLength();
        int max = Math.max(Math.min(maxIdentifierLength, mil), 0);
        String lowerPrefix = prefix == null ? null : StrUtils.getLowerHumplessCase(prefix);
        String lowerSuffix = suffix == null ? null : StrUtils.getLowerHumplessCase(suffix);
        return StrUtils.getIdentificadorSql(lowerPrefix, fullName, lowerSuffix, max);
    }

    private String getFullLengthSqlName(Artifact artifact) {
        if (artifact == null) {
            return null;
        }
        String name = artifact.getSqlName();
        if (StringUtils.isNotBlank(name)) {
            return StrUtils.getLowerHumplessCase(name);
        }
        return artifact instanceof Property ? getPropertySqlName(artifact, 0) : getArtifactSqlName(artifact, 0);
    }

    /**
     * @param property
     * @param queryTable
     * @return the SQL name of the property if it is found in queryTable; null otherwise
     */
    @Override
    public String getSqlAlias(Property property, QueryTable queryTable) {
        if (property == null || queryTable == null) {
            return null;
        }
        int index = queryTable.getSubqueryIndex();
        String name;
        for (Property p : queryTable.getColumns()) {
            if (p == property) {
                name = index == 0 ? getPropertySqlName(p) : getPropertySqlAlias(p, queryTable);
                return name;
            }
        }
        for (QueryJoin j : queryTable.getJoins()) {
            name = getSqlAlias(property, j.getRightTable());
            if (name == null) {
                continue;
            }
            return name;
        }
        return null;
    }

    /**
     * @param property
     * @param queryTable
     * @return the SQL name of the property if it is found in queryTable; null otherwise
     */
    @Override
    public String getSqlQualifiedName(Property property, QueryTable queryTable) {
        if (property == null || queryTable == null) {
            return null;
        }
        String name;
        for (Property p : queryTable.getColumns()) {
            if (p == property) {
                name = queryTable.getAlias() + DOT$ + getPropertySqlName(p);
                return name;
            }
        }
        for (QueryJoin j : queryTable.getJoins()) {
            name = getSqlQualifiedName(property, j.getRightTable());
            if (name == null) {
                continue;
            }
            return name;
        }
        return null;
    }

    private String getPropertySqlAlias(Property property, QueryTable queryTable) {
        ////    PersistentEntity pent = property.getDeclaringFieldPersistentEntityRoot();
        ////    String name = pent == null ? getPropertySqlName(property) : getArtifactSqlName(property);
        //      String name = getPropertySqlName(property);
        //      return queryTable.getPrefix() + name + queryTable.getSuffix();
        String prefix = queryTable.getPrefix();
        String string = getSqlName(property, 0);
        String suffix = queryTable.getSuffix();
        int maxIdentifierLength = getMaxIdentifierLength();
        return StrUtils.getIdentificadorSql(prefix, string, suffix, maxIdentifierLength);
    }

    private String getPropertySqlName(Artifact artifact) {
        int maxIdentifierLength = getMaxIdentifierLength();
        return getPropertySqlName(artifact, maxIdentifierLength);
    }

    private String getPropertySqlName(Artifact artifact, int maxIdentifierLength) {
        String name = getArtifactFixedCaseName(artifact);
        PersistentEntity pent = artifact.getDeclaringFieldPersistentEntityRoot();
        if (pent != null) {
            //          String propertiesPrefix = pent.getPropertiesPrefix();
            //          String propertiesSuffix = pent.getPropertiesSuffix();
            String discriminatorValue = pent.getDiscriminatorValue();
            boolean notTable = pent.isNotTable();
            //          boolean prefixed = StringUtils.isNotBlank(propertiesPrefix);
            //          boolean suffixed = StringUtils.isNotBlank(propertiesSuffix);
            boolean discriminated = StringUtils.isNotBlank(discriminatorValue);
            //          if (prefixed) {
            //              name = propertiesPrefix + StringUtils.capitalize(name);
            //          }
            //          if (suffixed) {
            //                name += StringUtils.capitalize(propertiesSuffix);
            //          }
            //          if (b && notTable && discriminated && !prefixed && !suffixed) {
            if (notTable && discriminated) {
                //              name += StringUtils.capitalize("x" + discriminatorValue);
                String prefix = "";
                String string = StrUtils.getLowerHumplessCase(name);
                String suffix = "_x" + discriminatorValue.toLowerCase();
                return StrUtils.getIdentificadorSql(prefix, string, suffix, maxIdentifierLength);
            }
        }
        //      return StrUtils.getLowerHumplessCase(name);
        return StrUtils.getIdentificadorSql(StrUtils.getLowerHumplessCase(name), maxIdentifierLength);
    }

    private String getArtifactSqlName(Artifact artifact) {
        int maxIdentifierLength = getMaxIdentifierLength();
        return getArtifactSqlName(artifact, maxIdentifierLength);
    }

    private String getArtifactSqlName(Artifact artifact, int maxIdentifierLength) {
        String name = getArtifactFixedCaseName(artifact);
        //      return StrUtils.getLowerHumplessCase(name);
        return StrUtils.getIdentificadorSql(StrUtils.getLowerHumplessCase(name), maxIdentifierLength);
    }

    private String getArtifactFixedCaseName(Artifact artifact) {
        String name = artifact.getName();
        String NAME = StringUtils.upperCase(name);
        return StringUtils.equals(name, NAME) ? StringUtils.lowerCase(name) : name;
    }

    private String getSqlParameterName(Artifact artifact) {
        return "$" + LCB + getSqlQualifiedName(artifact) + RCB;
    }
    //
    //  private String getSqlParameterName(NamedValue value) {
    //      return "$" + LCB + value.name() + RCB;
    //  }

    /**
     * @param artifact
     * @return the SQL qualified name
     */
    @Override
    public String getSqlQualifiedName(Artifact artifact) {
        String p;
        if (artifact.getDeclaringField() == null || artifact.getDeclaringArtifact() == null) {
            p = getSqlName(artifact);
        } else {
            p = getSqlQualifiedName(artifact.getDeclaringArtifact()) + DOT$ + getSqlName(artifact);
            if (artifact.getDeclaringField().getType().isArray()) {
                p += UND$ + artifact.getDeclaringFieldIndex();
            }
            p = StringUtils.removeStart(p, DOT$);
        }
        return p;
    }

    /**
     * @param artifact
     * @return the SQL variable name
     */
    @Override
    public String getSqlVariableName(Artifact artifact) {
        //      String name = getSqlName(artifact);
        //      return getSqlVariableName(name);
        if (artifact == null) {
            return null;
        }
        String prefix = getVariablesPrefix();
        String suffix = getVariablesSuffix();
        return getSqlName(prefix, artifact, suffix);
    }

    /**
     * @param name
     * @return the SQL variable name
     */
    @Override
    public String getSqlVariableName(String name) {
        //      return name == null ? null : getVariablesPrefix() + name.trim() + getVariablesSuffix();
        if (name == null) {
            return null;
        }
        String prefix = getVariablesPrefix();
        String string = StrUtils.getLowerHumplessCase(name);
        String suffix = getVariablesSuffix();
        int maxIdentifierLength = getMaxIdentifierLength();
        return StrUtils.getIdentificadorSql(prefix, string, suffix, maxIdentifierLength);
    }

    /**
     * @param entity
     * @return the discriminator value
     */
    @Override
    public String getSqlDiscriminatorValue(PersistentEntity entity) {
        Property discriminatorProperty = entity.getDiscriminatorProperty();
        if (discriminatorProperty != null) {
            String discriminatorValue = entity.getDiscriminatorValue();
            if (discriminatorValue != null) {
                Class<?> dataClass = discriminatorProperty.getDataClass();
                if (CharacterData.class.isAssignableFrom(dataClass)) {
                    return SQM$ + discriminatorValue + SQM$;
                } else {
                    return discriminatorValue;
                }
            } else if (entity.isAbstractClass()) {
                return null;
            } else {
                return getNull();
            }
        }
        return null;
    }

    /**
     * @param entity
     * @return the discriminator values
     */
    @Override
    public List<String> getSqlDiscriminatorValues(PersistentEntity entity) {
        List<String> values = new ArrayList<>();
        String value = getSqlDiscriminatorValue(entity);
        if (value != null) {
            values.add(value);
        }
        List<Entity> extensionsList = entity.getExtensionsList();
        PersistentEntity pent;
        for (Entity ext : extensionsList) {
            if (ext instanceof PersistentEntity) {
                pent = (PersistentEntity) ext;
                value = getSqlDiscriminatorValue(pent);
                if (value != null) {
                    values.add(value);
                }
            }
        }
        Collections.sort(values);
        return values;
    }

    /**
     * @param entity
     * @return the schema name
     */
    @Override
    public String getSqlSchemaName(PersistentEntity entity) {
        Entity base = entity.getBaseRoot();
        PersistentEntity pent = base instanceof PersistentEntity ? (PersistentEntity) base : null;
        InheritanceMappingStrategy ims = pent == null ? null : pent.getInheritanceMappingStrategy();
        return InheritanceMappingStrategy.SINGLE_TABLE.equals(ims) ? getSqlSchemaName(pent)
                : StringUtils.trimToEmpty(entity.getSchema());
    }

    /**
     * @param entity
     * @return the table name
     */
    @Override
    public String getSqlTableName(PersistentEntity entity) {
        Entity base = entity.getBaseRoot();
        PersistentEntity pent = base instanceof PersistentEntity ? (PersistentEntity) base : null;
        InheritanceMappingStrategy ims = pent == null ? null : pent.getInheritanceMappingStrategy();
        return InheritanceMappingStrategy.SINGLE_TABLE.equals(ims) ? getSqlTableName(pent)
                : getSqlName(entity.getRoot());
    }

    /**
     * @param aggregation
     * @return the aggregation function name
     */
    @Override
    public String getSqlFunctionName(ViewFieldAggregation aggregation) {
        if (aggregation == null) {
            return null;
        }
        switch (aggregation) {
        case COUNT:
            return "COUNT";
        case MINIMUM:
            return "MIN";
        case MAXIMUM:
            return "MAX";
        case SUM:
            return "SUM";
        case AVERAGE:
            return "AVG";
        case DEVIATION:
            return "STDDEV";
        default:
            return "COUNT";
        }
    }
    //
    //  /**
    //   * @return the base table name
    //   */
    //  @Override
    //  public String getSqlBaseTableName(PersistentEntity entity) {
    //      Entity base = entity.getBaseTableRoot();
    //      PersistentEntity pent = base instanceof PersistentEntity ? (PersistentEntity) base : null;
    //      return pent == null ? "?" : getSqlTableName(pent);
    //  }

    @Override
    public String getSqlSchemaQualifier(PersistentEntity entity) {
        String schema = getSqlSchemaName(entity);
        return StringUtils.isBlank(schema) ? "" : schema.trim() + ".";
    }

    @Override
    public String getSqlSchemaQualifiedName(PersistentEntity entity) {
        return StrUtils.getQualifiedName(getSqlName(entity), getSqlSchemaName(entity));
    }

    @Override
    public String getSqlSchemaQualifiedShortName(PersistentEntity entity) {
        return StrUtils.getQualifiedShortName(getSqlName(entity), getSqlSchemaName(entity));
    }

    @Override
    public String getSqlSchemaUnqualifiedShortName(PersistentEntity entity) {
        return StrUtils.getUnqualifiedShortName(getSqlName(entity), getSqlSchemaName(entity));
    }

    @Override
    public String getSqlSchemaQualifiedTableName(PersistentEntity entity) {
        return StrUtils.getQualifiedName(getSqlTableName(entity), getSqlSchemaName(entity));
    }

    @Override
    public String getSqlSchemaQualifiedShortTableName(PersistentEntity entity) {
        return StrUtils.getQualifiedShortName(getSqlTableName(entity), getSqlSchemaName(entity));
    }

    @Override
    public String getSqlSchemaUnqualifiedShortTableName(PersistentEntity entity) {
        return StrUtils.getUnqualifiedShortName(getSqlTableName(entity), getSqlSchemaName(entity));
    }

    /**
     * @param artifact
     * @return the SQL nulls clause
     */
    @Override
    public String getSqlNull(Artifact artifact) {
        if (artifact == null) {
            return null;
        } else if (artifact instanceof Property) {
            return getSqlNull((Property) artifact);
        } else {
            return null;
        }
    }

    private String getSqlNull(Property p) {
        if (p.isNullable()) {
            return EMPTY;
        }
        PersistentEntity declaringEntity = p.getDeclaringPersistentEntityRoot();
        return declaringEntity != null && declaringEntity.isTable() ? getNotNull() : EMPTY;
    }

    /**
     * @param artifact
     * @return the SQL initial value
     */
    @Override
    public String getSqlInitialValue(Artifact artifact) {
        QueryTable queryTable = null;
        return getSqlInitialValue(artifact, queryTable);
    }

    /**
     * @param artifact
     * @param queryTable
     * @return the SQL initial value
     */
    @Override
    public String getSqlInitialValue(Artifact artifact, QueryTable queryTable) {
        Object initialValue = getInitialValue(artifact);
        Class<?> dataType = getDataType(artifact);
        return initialValue == null || dataType == null ? null : getSqlValue(initialValue, dataType, queryTable);
    }

    /**
     * @param artifact
     * @return the SQL default value
     */
    @Override
    public String getSqlDefaultValue(Artifact artifact) {
        QueryTable queryTable = null;
        return getSqlDefaultValue(artifact, queryTable);
    }

    /**
     * @param artifact
     * @param queryTable
     * @return the SQL default value
     */
    @Override
    public String getSqlDefaultValue(Artifact artifact, QueryTable queryTable) {
        Object defaultValue = getDefaultValue(artifact);
        Class<?> dataType = getDataType(artifact);
        return defaultValue == null || dataType == null ? null : getSqlValue(defaultValue, dataType, queryTable);
    }

    /**
     * @param artifact
     * @return the SQL current value
     */
    @Override
    public String getSqlCurrentValue(Artifact artifact) {
        QueryTable queryTable = null;
        return getSqlCurrentValue(artifact, queryTable);
    }

    /**
     * @param artifact
     * @param queryTable
     * @return the SQL current value
     */
    @Override
    public String getSqlCurrentValue(Artifact artifact, QueryTable queryTable) {
        Object currentValue = getCurrentValue(artifact);
        Class<?> dataType = getDataType(artifact);
        return currentValue == null || dataType == null ? null : getSqlValue(currentValue, dataType, queryTable);
    }

    /**
     * @param object
     * @return the SQL value
     */
    //  @Override
    public String getSqlValue(Object object) {
        return getSqlValue(object, null);
    }

    /**
     * @param object
     * @param queryTable
     * @return the SQL value
     */
    //  @Override
    public String getSqlValue(Object object, QueryTable queryTable) {
        return object == null ? null : getSqlValue(object, object.getClass(), queryTable);
    }

    private String getSqlValue(Object object, Class<?> type, QueryTable queryTable) {
        if (object == null || type == null) {
            return null;
        } else if (object instanceof Property) {
            Property property = (Property) object;
            return queryTable == null ? null : getQualifiedName(property, queryTable, SqlQualifierType.RECORD);
        } else if (object instanceof Expression) {
            Expression expression = (Expression) object;
            return queryTable == null ? null : getSqlExpression(expression, queryTable, SqlQualifierType.RECORD);
        } else if (object instanceof Instance) { // && Entity.class.isAssignableFrom(type)
            return getDelimitedString(((Instance) object).getInstanceKeyValue());
        } else if (object instanceof Artifact) {
            Artifact artifact = (Artifact) object;
            return getSqlQualifiedName(artifact);
        } else if (object instanceof SpecialBooleanValue) {
            SpecialBooleanValue value = (SpecialBooleanValue) object;
            return getSpecialBooleanValue(value);
        } else if (object instanceof SpecialCharacterValue) {
            SpecialCharacterValue value = (SpecialCharacterValue) object;
            return getSpecialCharacterValue(value);
        } else if (object instanceof SpecialEntityValue) {
            SpecialEntityValue value = (SpecialEntityValue) object;
            return getSpecialEntityValue(value);
        } else if (object instanceof SpecialNumericValue) {
            SpecialNumericValue value = (SpecialNumericValue) object;
            return getSpecialNumericValue(value);
        } else if (object instanceof SpecialTemporalValue) {
            SpecialTemporalValue value = (SpecialTemporalValue) object;
            return getSpecialTemporalValue(value);
        } else if (object instanceof NamedValue) {
            NamedValue value = (NamedValue) object;
            return value.name();
        } else {
            return getDelimitedString(object);
        }
    }

    protected abstract String getSpecialBooleanValue(SpecialBooleanValue value);

    protected abstract String getSpecialCharacterValue(SpecialCharacterValue value);

    protected abstract String getSpecialEntityValue(SpecialEntityValue value);

    protected abstract String getSpecialNumericValue(SpecialNumericValue value);

    protected abstract String getSpecialTemporalValue(SpecialTemporalValue value);

    private Class<?> getDataType(Artifact artifact) {
        TypedArtifact typedArtifact = (artifact instanceof TypedArtifact) ? (TypedArtifact) artifact : null;
        return typedArtifact == null ? null : typedArtifact.getDataType();
    }

    private Object getInitialValue(Artifact artifact) {
        ValuedArtifact valuedArtifact = (artifact instanceof ValuedArtifact) ? (ValuedArtifact) artifact : null;
        return valuedArtifact == null ? null : valuedArtifact.getInitialValue();
    }

    private Object getDefaultValue(Artifact artifact) {
        ValuedArtifact valuedArtifact = (artifact instanceof ValuedArtifact) ? (ValuedArtifact) artifact : null;
        return valuedArtifact == null ? null : valuedArtifact.getDefaultValue();
    }

    private Object getCurrentValue(Artifact artifact) {
        ValuedArtifact valuedArtifact = (artifact instanceof ValuedArtifact) ? (ValuedArtifact) artifact : null;
        return valuedArtifact == null ? null : valuedArtifact.getCurrentValue();
    }

    /**
     * @param object
     * @return the SQL expression
     */
    @Override
    public String getSqlExpression(Object object) {
        QueryTable queryTable = null;
        return getSqlExpression(object, queryTable);
    }

    /**
     * @param object
     * @param queryTable
     * @return the SQL expression
     */
    @Override
    public String getSqlExpression(Object object, QueryTable queryTable) {
        return getSqlExpression(object, queryTable, SqlQualifierType.RECORD);
    }

    /**
     * @param object
     * @param queryTable
     * @param qualifier
     * @return the SQL expresion
     */
    @Override
    public String getSqlExpression(Object object, QueryTable queryTable, SqlQualifierType qualifier) {
        return getSqlExpression(object, queryTable, qualifier, null, false);
    }

    /**
     * @param object
     * @param queryTablesMap
     * @return the SQL expression
     */
    @Override
    public String getSqlExpression(Object object, Map<String, QueryTable> queryTablesMap) {
        return getSqlExpression(object, queryTablesMap, SqlQualifierType.RECORD);
    }

    /**
     * @param object
     * @param queryTablesMap
     * @param qualifier
     * @return the SQL expresion
     */
    @Override
    public String getSqlExpression(Object object, Map<String, QueryTable> queryTablesMap,
            SqlQualifierType qualifier) {
        return getSqlExpression(object, queryTablesMap, qualifier, null, false);
    }

    /**
     * @param object
     * @return the SQL parameterized expresion
     */
    @Override
    public ParameterizedExpression getSqlParameterizedExpression(Object object) {
        QueryTable queryTable = null;
        return getSqlParameterizedExpression(object, queryTable);
    }

    /**
     * @param object
     * @param queryTable
     * @return the SQL parameterized expresion
     */
    @Override
    public ParameterizedExpression getSqlParameterizedExpression(Object object, QueryTable queryTable) {
        return getSqlParameterizedExpression(object, queryTable, SqlQualifierType.RECORD);
    }

    /**
     * @param object
     * @param queryTable
     * @param qualifier
     * @return the SQL parameterized expresion
     */
    @Override
    public ParameterizedExpression getSqlParameterizedExpression(Object object, QueryTable queryTable,
            SqlQualifierType qualifier) {
        ParameterizedExpression px = new ParameterizedExpression();
        String expression = getSqlExpression(object, queryTable, qualifier, px, false);
        px.setExpression(expression);
        return px;
    }

    /**
     * @param object
     * @param queryTablesMap
     * @return the SQL parameterized expresion
     */
    @Override
    public ParameterizedExpression getSqlParameterizedExpression(Object object,
            Map<String, QueryTable> queryTablesMap) {
        return getSqlParameterizedExpression(object, queryTablesMap, SqlQualifierType.RECORD);
    }

    /**
     * @param object
     * @param queryTablesMap
     * @param qualifier
     * @return the SQL parameterized expresion
     */
    @Override
    public ParameterizedExpression getSqlParameterizedExpression(Object object,
            Map<String, QueryTable> queryTablesMap, SqlQualifierType qualifier) {
        ParameterizedExpression px = new ParameterizedExpression();
        String expression = getSqlExpression(object, queryTablesMap, qualifier, px, false);
        px.setExpression(expression);
        return px;
    }

    /**
     * @param object
     * @param queryObject
     * @param qualifier
     * @param px
     * @param enclose
     * @return the SQL expresion
     */
    protected String getSqlExpression(Object object, Object queryObject, SqlQualifierType qualifier,
            ParameterizedExpression px, boolean enclose) {
        if (object == null) {
            return null;
        } else if (object instanceof Entity && queryObject instanceof QueryTable
                && object.equals(((QueryTable) queryObject).getEntity())) {
            Entity entity = (Entity) object;
            Property property = entity.getPrimaryKeyProperty();
            return getSqlExpression(property, queryObject, qualifier, px, enclose);
        } else if (object instanceof Property) {
            Property property = (Property) object;
            return queryObject == null ? getPropertySqlName(property)
                    : getQualifiedName(property, queryObject, qualifier, px);
        } else if (object instanceof Expression) {
            Expression expression = (Expression) object;
            return getSqlExpression(expression, queryObject, qualifier, px, enclose);
        } else if (object instanceof Instance) {
            Instance instance = (Instance) object;
            return getDelimitedString(instance.getInstanceKeyValue());
        } else if (object instanceof SpecialBooleanValue) {
            SpecialBooleanValue value = (SpecialBooleanValue) object;
            return getSpecialBooleanValue(value);
        } else if (object instanceof SpecialCharacterValue) {
            SpecialCharacterValue value = (SpecialCharacterValue) object;
            return getSpecialCharacterValue(value);
        } else if (object instanceof SpecialEntityValue) {
            SpecialEntityValue value = (SpecialEntityValue) object;
            return getSpecialEntityValue(value);
        } else if (object instanceof SpecialNumericValue) {
            SpecialNumericValue value = (SpecialNumericValue) object;
            return getSpecialNumericValue(value);
        } else if (object instanceof SpecialTemporalValue) {
            SpecialTemporalValue value = (SpecialTemporalValue) object;
            return getSpecialTemporalValue(value);
        } else if (object instanceof NamedValue) {
            NamedValue namedValue = (NamedValue) object;
            return getNamedValueName(namedValue, px);
        } else if (object instanceof Artifact) {
            Artifact artifact = (Artifact) object;
            return getSqlQualifiedName(artifact);
        } else {
            return getDelimitedString(object);
        }
    }

    /**
     * @param expression
     * @param queryObject
     * @param qualifier
     * @param px
     * @param enclose
     * @return the SQL expresion
     */
    protected String getSqlExpression(Expression expression, Object queryObject, SqlQualifierType qualifier,
            ParameterizedExpression px, boolean enclose) {
        String string;
        if (expression == null) {
            return null;
        } else if (expression instanceof Property) {
            Property property = (Property) expression;
            return queryObject == null ? getPropertySqlName(property)
                    : getQualifiedName(property, queryObject, qualifier, px);
        } else if (expression instanceof ComparisonX) {
            string = getSqlComparisonExpression((ComparisonX) expression, queryObject, qualifier, px);
        } else if (expression instanceof ConditionalX) {
            string = getSqlConditionalExpression((ConditionalX) expression, queryObject, qualifier, px);
        } else if (expression instanceof DataAggregateX) {
            string = getSqlDataAggregateExpression((DataAggregateX) expression, queryObject, qualifier, px);
        } else if (expression instanceof RowsAggregateX) {
            string = getSqlRowsAggregateExpression((RowsAggregateX) expression, queryObject, qualifier, px);
        } else if (expression instanceof OrderedPairX) {
            string = getSqlOrderedPairExpression((OrderedPairX) expression, queryObject, qualifier, px);
        } else if (expression instanceof ScalarX) {
            string = getSqlScalarExpression((ScalarX) expression, queryObject, qualifier, px);
        } else if (expression instanceof VariantX) {
            string = getSqlVariantExpression((VariantX) expression, queryObject, qualifier, px);
        } else {
            string = getDelimitedString(expression);
        }
        return enclose ? StrUtils.encloseSqlExpression(string) : StrUtils.disclose(string);
    }

    /**
     * @param expression
     * @param queryObject
     * @param qualifier
     * @param px
     * @return the SQL expresion
     */
    protected String getSqlComparisonExpression(ComparisonX expression, Object queryObject,
            SqlQualifierType qualifier, ParameterizedExpression px) {
        ComparisonOp operator = expression.getOperator();
        Object x = expression.getX();
        Object y = expression.getY();
        if (operator == null || x == null) {
            return null;
        }
        boolean primitive = x instanceof Primitive;
        String arg1 = getSqlExpression(x, queryObject, qualifier, px, true);
        String arg2 = getSqlExpression(y, queryObject, qualifier, px, true);
        String pattern;
        switch (operator) {
        case EXISTS:
            pattern = getExists() + LRB$ + ARG0 + RRB$;
            break;
        case NOT_EXISTS:
            pattern = getNotExists() + LRB$ + ARG0 + RRB$;
            break;
        case IS_NULL:
            pattern = ARG0 + SPC$ + getIsNull();
            break;
        case IS_NOT_NULL:
            pattern = ARG0 + SPC$ + getIsNotNull();
            break;
        case IS_TRUE:
            //              pattern = ARG0 + SPC$ + getIsTrue();
            pattern = primitive ? primitiveIsTruePattern() : expressionIsTruePattern();
            break;
        case IS_FALSE:
            //              pattern = ARG0 + SPC$ + getIsFalse();
            pattern = primitive ? primitiveIsFalsePattern() : expressionIsFalsePattern();
            break;
        case IS_NULL_OR_TRUE:
            //              pattern = getIsNullOr(ARG0) + SPC$ + getIsTrue();
            pattern = primitive ? primitiveIsTruePattern() : expressionIsTruePattern();
            pattern = ARG0 + SPC$ + getIsNullOr() + SPC$ + pattern;
            break;
        case IS_NULL_OR_FALSE:
            //              pattern = getIsNullOr(ARG0) + SPC$ + getIsFalse();
            pattern = primitive ? primitiveIsFalsePattern() : expressionIsFalsePattern();
            pattern = ARG0 + SPC$ + getIsNullOr() + SPC$ + pattern;
            break;
        case EQ:
            pattern = ARG0 + SPC$ + getEQ() + SPC$ + ARG1;
            break;
        case NEQ:
            pattern = ARG0 + SPC$ + getNEQ() + SPC$ + ARG1;
            break;
        case GT:
            pattern = ARG0 + SPC$ + getGT() + SPC$ + ARG1;
            break;
        case GTEQ:
            pattern = ARG0 + SPC$ + getGTEQ() + SPC$ + ARG1;
            break;
        case LT:
            pattern = ARG0 + SPC$ + getLT() + SPC$ + ARG1;
            break;
        case LTEQ:
            pattern = ARG0 + SPC$ + getLTEQ() + SPC$ + ARG1;
            break;
        case STARTS_WITH:
            pattern = ARG0 + SPC$ + getLike() + SPC$ + startsWithArgument(1);
            break;
        case NOT_STARTS_WITH:
            pattern = ARG0 + SPC$ + getNotLike() + SPC$ + startsWithArgument(1);
            break;
        case CONTAINS:
            pattern = ARG0 + SPC$ + getLike() + SPC$ + containsArgument(1);
            break;
        case NOT_CONTAINS:
            pattern = ARG0 + SPC$ + getNotLike() + SPC$ + containsArgument(1);
            break;
        case ENDS_WITH:
            pattern = ARG0 + SPC$ + getLike() + SPC$ + endsWithArgument(1);
            break;
        case NOT_ENDS_WITH:
            pattern = ARG0 + SPC$ + getNotLike() + SPC$ + endsWithArgument(1);
            break;
        case IN:
            pattern = ARG0 + SPC$ + getIn() + LRB$ + ARG1 + RRB$;
            break;
        case NOT_IN:
            pattern = ARG0 + SPC$ + getNotIn() + LRB$ + ARG1 + RRB$;
            break;
        case IS_NULL_OR_EQ:
            pattern = getIsNullOr(ARG0) + SPC$ + getEQ() + SPC$ + ARG1;
            break;
        case IS_NULL_OR_NEQ:
            pattern = getIsNullOr(ARG0) + SPC$ + getNEQ() + SPC$ + ARG1;
            break;
        case IS_NULL_OR_GT:
            pattern = getIsNullOr(ARG0) + SPC$ + getGT() + SPC$ + ARG1;
            break;
        case IS_NULL_OR_LTEQ:
            pattern = getIsNullOr(ARG0) + SPC$ + getLTEQ() + SPC$ + ARG1;
            break;
        case IS_NULL_OR_GTEQ:
            pattern = getIsNullOr(ARG0) + SPC$ + getGTEQ() + SPC$ + ARG1;
            break;
        case IS_NULL_OR_LT:
            pattern = getIsNullOr(ARG0) + SPC$ + getLT() + SPC$ + ARG1;
            break;
        case IS_NULL_OR_STARTS_WITH:
            pattern = getIsNullOr(ARG0) + SPC$ + getLike() + SPC$ + startsWithArgument(1);
            break;
        case IS_NULL_OR_NOT_STARTS_WITH:
            pattern = getIsNullOr(ARG0) + SPC$ + getNotLike() + SPC$ + startsWithArgument(1);
            break;
        case IS_NULL_OR_CONTAINS:
            pattern = getIsNullOr(ARG0) + SPC$ + getLike() + SPC$ + containsArgument(1);
            break;
        case IS_NULL_OR_NOT_CONTAINS:
            pattern = getIsNullOr(ARG0) + SPC$ + getNotLike() + SPC$ + containsArgument(1);
            break;
        case IS_NULL_OR_ENDS_WITH:
            pattern = getIsNullOr(ARG0) + SPC$ + getLike() + SPC$ + endsWithArgument(1);
            break;
        case IS_NULL_OR_NOT_ENDS_WITH:
            pattern = getIsNullOr(ARG0) + SPC$ + getNotLike() + SPC$ + endsWithArgument(1);
            break;
        case IS_NULL_OR_IN:
            pattern = getIsNullOr(ARG0) + SPC$ + getIn() + LRB$ + ARG1 + RRB$;
            break;
        case IS_NULL_OR_NOT_IN:
            pattern = getIsNullOr(ARG0) + SPC$ + getNotIn() + LRB$ + ARG1 + RRB$;
            break;
        default:
            pattern = call(operator, y == null ? 1 : 2);
            break;
        }
        return format(pattern, arg1, arg2);
    }

    protected String primitiveIsTruePattern() {
        return ARG0 + SPC$ + getIsTrue();
    }

    protected String primitiveIsFalsePattern() {
        return ARG0 + SPC$ + getIsFalse();
    }

    protected String expressionIsTruePattern() {
        return ARG0 + SPC$ + getIsTrue();
    }

    protected String expressionIsFalsePattern() {
        return ARG0 + SPC$ + getIsFalse();
    }

    /**
     * @param expression
     * @param queryObject
     * @param qualifier
     * @param px
     * @return the SQL expresion
     */
    protected String getSqlConditionalExpression(ConditionalX expression, Object queryObject,
            SqlQualifierType qualifier, ParameterizedExpression px) {
        Expression b = expression.getBooleanExpression();
        Object x = expression.getThenValue();
        Object y = expression.getElseValue();
        if (b == null || x == null) {
            return null;
        }
        String arg0 = getSqlExpression(b, queryObject, qualifier, px, true);
        String arg1 = getSqlExpression(x, queryObject, qualifier, px, true);
        String arg2 = getSqlExpression(y, queryObject, qualifier, px, true);
        String pattern = y == null ? getCaseWhenThenPattern() : getCaseWhenThenElsePattern();
        return format(pattern, arg0, arg1, arg2);
    }

    /**
     * @param expression
     * @param queryObject
     * @param qualifier
     * @param px
     * @return the SQL expresion
     */
    protected String getSqlDataAggregateExpression(DataAggregateX expression, Object queryObject,
            SqlQualifierType qualifier, ParameterizedExpression px) {
        DataAggregateOp operator = expression.getOperator();
        Object[] operands = expression.getOperands();
        if (operator == null || operands == null || operands.length < 2) {
            return null;
        }
        String[] arguments = new String[operands.length];
        for (int i = 0; i < operands.length; i++) {
            arguments[i] = getSqlExpression(operands[i], queryObject, qualifier, px, true);
        }
        String string;
        switch (operator) {
        case COUNT:
            string = count(arguments);
            break;
        case MAXIMUM:
            string = maximum(arguments);
            break;
        case MINIMUM:
            string = minimum(arguments);
            break;
        case AND:
            string = and(arguments);
            break;
        case NAND:
            string = not(and(arguments));
            break;
        case OR:
            string = or(arguments);
            break;
        case NOR:
            string = not(or(arguments));
            break;
        case NAXOR:
            string = xor(arguments);
            break;
        case NAXNOR:
            string = not(xor(arguments));
            break;
        case NOR_OR_NAXOR:
            string = or(not(or(arguments)), xor(arguments));
            break;
        case CONCAT:
            string = connect(getConcat(), arguments);
            break;
        case SUM:
            string = connect(getAdd(), arguments);
            break;
        case PRODUCT:
            string = connect(getMultiply(), arguments);
            break;
        case AVERAGE:
            string = average(arguments);
            break;
        default:
            string = call(operator, arguments);
            break;
        }
        return string;
    }

    /**
     * @param expression
     * @param queryObject
     * @param qualifier
     * @param px
     * @return the SQL expresion
     */
    protected String getSqlRowsAggregateExpression(RowsAggregateX expression, Object queryObject,
            SqlQualifierType qualifier, ParameterizedExpression px) {
        String errmsg = EMPTY;
        if (expression == null || queryObject == null) {
            return null;
        }
        errmsg += "failed to generate code for expression " + stringOf(expression);
        Entity declaringEntity = expression.getDeclaringEntity();
        if (declaringEntity == null) {
            logger.error(errmsg);
            return null;
        }
        errmsg += " at entity " + stringOf(declaringEntity);
        //      String function = expression.getSqlSelectFunctionName();
        //      String function = getSqlExpressionSelectFunctionName(expression);
        String function = getSqlSchemaQualifiedShortExpressionSelectFunctionName(expression);
        if (function == null) {
            logger.error(errmsg);
            return null;
        }
        Entity dimension = expression.getDimension();
        Property argument = dimension instanceof Property ? (Property) dimension : null;
        String argname = argument == null ? EMPTY : getQualifiedName(argument, queryObject, qualifier, px);
        return function + LRB$ + argname + RRB$;
    }

    /**
     * @param expression
     * @param queryObject
     * @param qualifier
     * @param px
     * @return the SQL expresion
     */
    protected abstract String getSqlOrderedPairExpression(OrderedPairX expression, Object queryObject,
            SqlQualifierType qualifier, ParameterizedExpression px);

    /**
     * @param expression
     * @param queryObject
     * @param qualifier
     * @param px
     * @return the SQL expresion
     */
    protected abstract String getSqlScalarExpression(ScalarX expression, Object queryObject,
            SqlQualifierType qualifier, ParameterizedExpression px);

    /**
     * @param expression
     * @param queryObject
     * @param qualifier
     * @param px
     * @return the SQL expresion
     */
    protected String getSqlVariantExpression(VariantX expression, Object queryObject, SqlQualifierType qualifier,
            ParameterizedExpression px) {
        String errmsg = EMPTY;
        if (expression == null || queryObject == null) {
            return null;
        }
        errmsg += "failed to generate code for expression " + stringOf(expression);
        Entity declaringEntity = expression.getDeclaringEntity();
        if (declaringEntity == null) {
            logger.error(errmsg);
            return null;
        }
        errmsg += " at entity " + stringOf(declaringEntity);
        //      String function = expression.getSqlExpressionFunctionName();
        //      String function = getSqlExpressionFunctionName(expression);
        String function = getSqlSchemaQualifiedShortExpressionFunctionName(expression);
        //      Property argument = expression.getSqlExpressionFunctionArgument();
        Property argument = expression.getExpressionFunctionArgument();
        if (function == null || argument == null) {
            logger.error(errmsg);
            return null;
        }
        boolean doubtful = declaringEntity.isRootInstance();
        if (doubtful) {
            // <editor-fold defaultstate="collapsed">
            //          String name = expression.getName();
            //          Class<?> type = expression.getDataType();
            //          String foreignName = expression.getForeignName();
            //          Class<?> foreignType = expression.getForeignType();
            // </editor-fold>
            Expression foreignExpression = expression.getForeignExpression();
            if (foreignExpression instanceof RowsAggregateX) {
                RowsAggregateX foreignRowsAggregateX = (RowsAggregateX) foreignExpression;
                //              String select = foreignRowsAggregateX.getSqlSelectFunctionName();
                //              String select = getSqlExpressionSelectFunctionName(foreignRowsAggregateX);
                String select = getSqlSchemaQualifiedShortExpressionSelectFunctionName(foreignRowsAggregateX);
                if (select != null) {
                    Entity dimension = foreignRowsAggregateX.getDimension();
                    if (dimension == null) {
                        function = select;
                        argument = null;
                        doubtful = false;
                    } else if (declaringEntity.getClass().isAssignableFrom(dimension.getClass())) {
                        function = select;
                        doubtful = false;
                    }
                }
                if (doubtful) {
                    errmsg += "; cannot bind it to its foreign expression " + stringOf(foreignExpression);
                    errmsg += " at entity " + stringOf(foreignExpression.getDeclaringEntity());
                }
            }
        }
        if (doubtful) {
            logger.error(errmsg);
            return null;
        } else if (argument == null) {
            return function + LRB$ + RRB$;
        } else {
            return function + LRB$ + getQualifiedName(argument, queryObject, qualifier, px) + RRB$;
        }
    }

    /**
     * @param operation
     * @return the sql operation function name
     */
    @Override
    public String getSqlOperationFunctionName(Operation operation) {
        int maxIdentifierLength = getMaxIdentifierLength();
        return getSqlOperationFunctionName(operation, maxIdentifierLength);
    }

    /**
     * @param operation
     * @param maxIdentifierLength
     * @return the sql operation function name
     */
    @Override
    public String getSqlOperationFunctionName(Operation operation, int maxIdentifierLength) {
        if (operation == null) {
            return null;
        }
        Entity e = operation.getDeclaringEntity();
        //      return e == null ? getSqlName(operation) : getSqlName(e.getRoot()) + OPERATION_DOLLAR_INFIX + getSqlName(operation);
        String r = e == null ? "" : getSqlName(e.getRoot(), 0) + EXPRESSION_DOLLAR_INFIX;
        String x = getSqlName(operation, 0);
        return StrUtils.getIdentificadorSql(r + x, maxIdentifierLength);
    }

    /**
     * @param operation
     * @return the sql operation schema qualified function name
     */
    @Override
    public String getSqlSchemaQualifiedOperationFunctionName(Operation operation) {
        if (operation == null) {
            return null;
        }
        String name = getSqlOperationFunctionName(operation);
        PersistentEntity pent = operation.getDeclaringPersistentEntity();
        return pent == null ? name : StrUtils.getQualifiedName(name, getSqlSchemaName(pent));
    }

    /**
     * @param operation
     * @param maxIdentifierLength
     * @return the sql operation schema qualified function name
     */
    @Override
    public String getSqlSchemaQualifiedOperationFunctionName(Operation operation, int maxIdentifierLength) {
        if (operation == null) {
            return null;
        }
        String name = getSqlOperationFunctionName(operation, maxIdentifierLength);
        PersistentEntity pent = operation.getDeclaringPersistentEntity();
        return pent == null ? name : StrUtils.getQualifiedName(name, getSqlSchemaName(pent));
    }

    /**
     * @param operation
     * @return the sql operation schema qualified function name
     */
    @Override
    public String getSqlSchemaQualifiedShortOperationFunctionName(Operation operation) {
        if (operation == null) {
            return null;
        }
        String name = getSqlOperationFunctionName(operation);
        PersistentEntity pent = operation.getDeclaringPersistentEntity();
        return pent == null ? name : StrUtils.getQualifiedShortName(name, getSqlSchemaName(pent));
    }

    /**
     * @param operation
     * @param maxIdentifierLength
     * @return the sql operation schema qualified function name
     */
    @Override
    public String getSqlSchemaQualifiedShortOperationFunctionName(Operation operation, int maxIdentifierLength) {
        if (operation == null) {
            return null;
        }
        String name = getSqlOperationFunctionName(operation, maxIdentifierLength);
        PersistentEntity pent = operation.getDeclaringPersistentEntity();
        return pent == null ? name : StrUtils.getQualifiedShortName(name, getSqlSchemaName(pent));
    }

    /**
     * @param expression
     * @return the sql expression function name
     */
    @Override
    public String getSqlExpressionFunctionName(Expression expression) {
        int maxIdentifierLength = getMaxIdentifierLength();
        return getSqlExpressionFunctionName(expression, maxIdentifierLength);
    }

    /**
     * @param expression
     * @param maxIdentifierLength
     * @return the sql expression function name
     */
    @Override
    public String getSqlExpressionFunctionName(Expression expression, int maxIdentifierLength) {
        if (expression == null) {
            return null;
        }
        Entity e = expression.getDeclaringEntity();
        //      return e == null ? getSqlName(expression) : getSqlName(e.getRoot()) + EXPRESSION_DOLLAR_INFIX + getSqlName(expression);
        String r = e == null ? "" : getSqlName(e.getRoot(), 0) + EXPRESSION_DOLLAR_INFIX;
        String x = getSqlName(expression, 0);
        return StrUtils.getIdentificadorSql(r + x, maxIdentifierLength);
    }

    /**
     * @param expression
     * @return the sql expression schema qualified function name
     */
    @Override
    public String getSqlSchemaQualifiedExpressionFunctionName(Expression expression) {
        if (expression == null) {
            return null;
        }
        String name = getSqlExpressionFunctionName(expression);
        PersistentEntity pent = expression.getDeclaringPersistentEntity();
        return pent == null ? name : StrUtils.getQualifiedName(name, getSqlSchemaName(pent));
    }

    /**
     * @param expression
     * @param maxIdentifierLength
     * @return the sql expression schema qualified function name
     */
    @Override
    public String getSqlSchemaQualifiedExpressionFunctionName(Expression expression, int maxIdentifierLength) {
        if (expression == null) {
            return null;
        }
        String name = getSqlExpressionFunctionName(expression, maxIdentifierLength);
        PersistentEntity pent = expression.getDeclaringPersistentEntity();
        return pent == null ? name : StrUtils.getQualifiedName(name, getSqlSchemaName(pent));
    }

    /**
     * @param expression
     * @return the sql expression schema qualified function name
     */
    @Override
    public String getSqlSchemaQualifiedShortExpressionFunctionName(Expression expression) {
        if (expression == null) {
            return null;
        }
        String name = getSqlExpressionFunctionName(expression);
        PersistentEntity pent = expression.getDeclaringPersistentEntity();
        return pent == null ? name : StrUtils.getQualifiedShortName(name, getSqlSchemaName(pent));
    }

    /**
     * @param expression
     * @param maxIdentifierLength
     * @return the sql expression schema qualified function name
     */
    @Override
    public String getSqlSchemaQualifiedShortExpressionFunctionName(Expression expression, int maxIdentifierLength) {
        if (expression == null) {
            return null;
        }
        String name = getSqlExpressionFunctionName(expression, maxIdentifierLength);
        PersistentEntity pent = expression.getDeclaringPersistentEntity();
        return pent == null ? name : StrUtils.getQualifiedShortName(name, getSqlSchemaName(pent));
    }

    /**
     * @param expression
     * @return the sql expression select function name
     */
    @Override
    public String getSqlExpressionSelectFunctionName(Expression expression) {
        int maxIdentifierLength = getMaxIdentifierLength();
        return getSqlExpressionSelectFunctionName(expression, maxIdentifierLength);
    }

    /**
     * @param expression
     * @param maxIdentifierLength
     * @return the sql expression select function name
     */
    @Override
    public String getSqlExpressionSelectFunctionName(Expression expression, int maxIdentifierLength) {
        if (expression == null) {
            return null;
        }
        Entity e = expression instanceof RowsAggregateX ? expression.getDeclaringEntity() : null;
        //      return e == null ? getSqlName(expression) : getSqlName(e.getRoot()) + EXPRESSION_SELECT_INFIX + getSqlName(expression);
        String r = e == null ? "" : getSqlName(e.getRoot(), 0) + EXPRESSION_SELECT_INFIX;
        String x = getSqlName(expression, 0);
        return StrUtils.getIdentificadorSql(r + x, maxIdentifierLength);
    }

    /**
     * @param expression
     * @return the sql expression schema qualified function name
     */
    @Override
    public String getSqlSchemaQualifiedExpressionSelectFunctionName(Expression expression) {
        if (expression == null) {
            return null;
        }
        String name = getSqlExpressionSelectFunctionName(expression);
        PersistentEntity pent = expression instanceof RowsAggregateX ? expression.getDeclaringPersistentEntity()
                : null;
        return pent == null ? name : StrUtils.getQualifiedName(name, getSqlSchemaName(pent));
    }

    /**
     * @param expression
     * @param maxIdentifierLength
     * @return the sql expression schema qualified function name
     */
    @Override
    public String getSqlSchemaQualifiedExpressionSelectFunctionName(Expression expression,
            int maxIdentifierLength) {
        if (expression == null) {
            return null;
        }
        String name = getSqlExpressionSelectFunctionName(expression, maxIdentifierLength);
        PersistentEntity pent = expression instanceof RowsAggregateX ? expression.getDeclaringPersistentEntity()
                : null;
        return pent == null ? name : StrUtils.getQualifiedName(name, getSqlSchemaName(pent));
    }

    /**
     * @param expression
     * @return the sql expression schema qualified function name
     */
    @Override
    public String getSqlSchemaQualifiedShortExpressionSelectFunctionName(Expression expression) {
        if (expression == null) {
            return null;
        }
        String name = getSqlExpressionSelectFunctionName(expression);
        PersistentEntity pent = expression instanceof RowsAggregateX ? expression.getDeclaringPersistentEntity()
                : null;
        return pent == null ? name : StrUtils.getQualifiedShortName(name, getSqlSchemaName(pent));
    }

    /**
     * @param expression
     * @param maxIdentifierLength
     * @return the sql expression schema qualified function name
     */
    @Override
    public String getSqlSchemaQualifiedShortExpressionSelectFunctionName(Expression expression,
            int maxIdentifierLength) {
        if (expression == null) {
            return null;
        }
        String name = getSqlExpressionSelectFunctionName(expression, maxIdentifierLength);
        PersistentEntity pent = expression instanceof RowsAggregateX ? expression.getDeclaringPersistentEntity()
                : null;
        return pent == null ? name : StrUtils.getQualifiedShortName(name, getSqlSchemaName(pent));
    }

    private String stringOf(Expression e) {
        return e == null ? "?"
                : e.getName() != null ? e.getName()
                        : e.getParentExpression() != null
                                ? stringOf(e.getParentExpression()) + "[" + e.toString() + "]"
                                : e.toString();
    }

    private String stringOf(Entity e) {
        return e == null ? "?" : e.getName() != null ? e.getName() : e.toString();
    }

    protected String getSqlExpressionDefaultValue(Expression expression) {
        Class<?> clazz = expression == null ? null : expression.getDataType();
        if (clazz == null) {
            return getNull();
        } else if (Boolean.class.isAssignableFrom(clazz)) {
            return getFalse();
        } else if (Character.class.isAssignableFrom(clazz)) {
            return getZeroChar();
        } else if (String.class.isAssignableFrom(clazz)) {
            return getZeroString();
        } else if (Byte.class.isAssignableFrom(clazz)) {
            return getZeroNumber();
        } else if (Short.class.isAssignableFrom(clazz)) {
            return getZeroNumber();
        } else if (Integer.class.isAssignableFrom(clazz)) {
            return getZeroNumber();
        } else if (Long.class.isAssignableFrom(clazz)) {
            return getZeroNumber();
        } else if (Float.class.isAssignableFrom(clazz)) {
            return getZeroNumber();
        } else if (Double.class.isAssignableFrom(clazz)) {
            return getZeroNumber();
        } else if (BigInteger.class.isAssignableFrom(clazz)) {
            return getZeroNumber();
        } else if (BigDecimal.class.isAssignableFrom(clazz)) {
            return getZeroNumber();
        } else if (Date.class.isAssignableFrom(clazz)) {
            return getZeroDate();
        } else if (Time.class.isAssignableFrom(clazz)) {
            return getZeroTime();
        } else if (Timestamp.class.isAssignableFrom(clazz)) {
            return getZeroTimestamp();
        } else {
            return getNull();
        }
    }

    /**
     * @param sortOption
     * @return the SQL sort option
     */
    @Override
    public String getSqlSortOption(SortOption sortOption) {
        if (sortOption != null) {
            switch (sortOption) {
            case ASC:
                return getAscending();
            case DESC:
                return getDescending();
            default:
                return null;
            }
        }
        return null;
    }

    /**
     * @param operator
     * @return the SQL join operator
     */
    @Override
    public String getSqlJoinOperator(QueryJoinOp operator) {
        if (operator != null) {
            switch (operator) {
            case INNER:
                return getInnerJoin();
            case LEFT:
                return getLeftJoin();
            case RIGHT:
                return getRightJoin();
            case FULL:
                return getFullJoin();
            case CROSS:
                return getCrossJoin();
            default:
                return getDefaultJoin();
            }
        }
        return null;
    }

    /**
     * @param queryTable
     * @return the SQL join qualifier
     */
    @Override
    public String getSqlJoinQualifier(QueryTable queryTable) {
        int index = queryTable == null ? 0 : queryTable.getSubqueryIndex();
        return index == 0 ? getRowVariableName() : getRecordVariableName(index);
    }

    /**
     * @param queryTable
     * @return the select columns map
     */
    @Override
    public Map<String, Property> getSelectColumnsMap(QueryTable queryTable) {
        String alias;
        Map<String, Property> map = new LinkedHashMap<>();
        for (Property p : queryTable.getColumns()) {
            alias = getPropertySqlAlias(p, queryTable);
            map.put(alias, p);
        }
        for (QueryJoin j : queryTable.getJoins()) {
            map.putAll(getSelectColumnsMap(j.getRightTable()));
        }
        return map;
    }

    /**
     * @param expression
     * @return the SQL statement
     */
    @Override
    public String getSqlSelectStatement(Expression expression) {
        QueryTable queryTable = null;
        return getSqlSelectStatement(expression, queryTable);
    }

    /**
     * @param expression
     * @param queryTable
     * @return the SQL statement
     */
    @Override
    public String getSqlSelectStatement(Expression expression, QueryTable queryTable) {
        if (expression instanceof RowsAggregateX) {
            return getSqlSelectStatement((RowsAggregateX) expression, queryTable);
        }
        return null;
    }

    protected String getSqlSelectStatement(RowsAggregateX expression, QueryTable queryTable) {
        RowsAggregateOp operator = expression.getOperator();
        Object measure = expression.getMeasure();
        Segment filter = expression.getFilter();
        Entity dimension = expression.getDimension();
        if (operator == null || measure == null) {
            return null;
        }
        String function;
        switch (operator) {
        case COUNT:
            function = getCount();
            break;
        case MAXIMUM:
            function = getMaximum();
            break;
        case MINIMUM:
            function = getMinimum();
            break;
        case SUM:
            function = getSum();
            break;
        case AVERAGE:
            function = getAverage();
            break;
        default:
            function = operator.name().toLowerCase();
            break;
        }
        String argument = getSqlExpression(measure, queryTable, SqlQualifierType.ALIAS, null, false);
        if (argument == null && operator.equals(RowsAggregateOp.COUNT)) {
            argument = AST$;
        }
        List<Property> referencedColumns = new ArrayList<>();
        if (measure instanceof Property) {
            referencedColumns.add((Property) measure);
        } else if (measure instanceof Expression) {
            referencedColumns.addAll(((Expression) measure).getReferencedColumnsList());
        }
        if (filter != null) {
            referencedColumns.addAll(filter.getReferencedColumnsList());
        }
        if (dimension instanceof Property) {
            referencedColumns.add((Property) dimension);
        }
        String string = getSelect();
        string += EOL$ + function + LRB$ + argument + RRB$;
        string += EOL$ + getInto() + SPC$ + getValueVariableName();
        string += EOL$ + getSelectFrom(queryTable, referencedColumns, false);
        String where = getWhere();
        if (filter != null) {
            string += EOL$ + where + SPC$
                    + getSqlExpression(filter, queryTable, SqlQualifierType.ALIAS, null, false);
            where = getAnd();
        }
        if (dimension != null) {
            string += EOL$ + where + SPC$
                    + getSqlExpression(dimension, queryTable, SqlQualifierType.ALIAS, null, false);
            string += SPC$ + getEQ() + SPC$ + getSqlVariableName(dimension);
        }
        return string;
    }

    /**
     * @param queryTable
     * @param referencedColumns
     * @param into
     * @param indent
     * @return the SQL statement
     */
    @Override
    public String getSqlSelectStatement(QueryTable queryTable, List<Property> referencedColumns, boolean into,
            boolean indent) {
        String string = getSelect();
        string += getSelectColumns(queryTable, referencedColumns, indent);
        if (into) {
            string += EOL$ + getInto() + SPC$ + getSqlJoinQualifier(queryTable);
        }
        string += EOL$ + getSelectFrom(queryTable, referencedColumns, indent);
        return string;
    }

    private String getSelectColumns(QueryTable queryTable, List<Property> referencedColumns, boolean indent) {
        String string = EMPTY;
        String tab = indent ? StringUtils.repeat(TAB$, queryTable.getDepth()) : EMPTY;
        String name, alias;
        for (Property p : queryTable.getColumns()) {
            if (referencedColumns == null || referencedColumns.isEmpty() || referencedColumns.contains(p)) {
                name = getPropertySqlName(p);
                alias = getPropertySqlAlias(p, queryTable);
                string += EOL$ + tab + queryTable.getAlias() + DOT$ + name;
                if (name.equals(alias)) {
                } else {
                    string += SPC$ + getAs() + SPC$ + alias;
                }
                string += SEP$;
            }
        }
        String s;
        for (QueryJoin j : queryTable.getJoins()) {
            s = getSelectColumns(j.getRightTable(), referencedColumns, indent);
            if (StringUtils.isNotBlank(s)) {
                string += s + SEP$;
            }
        }
        return StringUtils.removeEnd(string, SEP$);
    }

    private String getSelectFrom(QueryTable queryTable, List<Property> referencedColumns, boolean indent) {
        String string = getFrom() + SPC$ + queryTable.getName();
        if (!queryTable.getName().equals(queryTable.getAlias())) {
            //          string += SPC$ + getAs() + SPC$ + queryTable.getAlias();
            string += SPC$ + queryTable.getAlias();
        }
        string += getSelectJoins(queryTable, referencedColumns, indent);
        return string;
    }

    private String getSelectJoins(QueryTable queryTable, List<Property> referencedColumns, boolean indent) {
        String string = EMPTY;
        boolean norc = referencedColumns == null || referencedColumns.isEmpty();
        for (QueryJoin j : queryTable.getJoins()) {
            if (norc || containsAny(j.getRightTable(), referencedColumns, indent)) {
                string += getSelectJoin(j, referencedColumns, indent);
            }
        }
        return string;
    }

    private boolean containsAny(QueryTable queryTable, List<Property> referencedColumns, boolean indent) {
        for (Property p : queryTable.getColumns()) {
            if (referencedColumns.contains(p)) {
                return true;
            }
        }
        for (QueryJoin j : queryTable.getJoins()) {
            if (containsAny(j.getRightTable(), referencedColumns, indent)) {
                return true;
            }
        }
        return false;
    }

    private String getQualifiedName(Property property, Object queryObject, SqlQualifierType qualifier) {
        return getQualifiedName(property, queryObject, qualifier, null);
    }

    private String getQualifiedName(Property property, Object queryObject, SqlQualifierType qualifier,
            ParameterizedExpression px) {
        switch (qualifier) {
        case ALIAS:
            return getAliasQualifiedName(property, queryObject, px);
        case RECORD:
            return getRecordQualifiedName(property, queryObject, px);
        case SUFFIX:
            return getUnqualifiedButSuffixedName(property, queryObject, px);
        default:
            return getQualifiedName(property, px);
        }
    }

    private String getQualifiedName(Property property, ParameterizedExpression px) {
        if (px == null) {
            return getSqlQualifiedName(property);
        }
        String name = getSqlParameterName(property);
        px.getParametersMap().put(name, property);
        return name;
    }

    @SuppressWarnings("unchecked")
    private String getAliasQualifiedName(Property property, Object queryObject, ParameterizedExpression px) {
        QueryTable queryTable = queryObject instanceof QueryTable ? (QueryTable) queryObject : null;
        Map<String, QueryTable> queryTablesMap = queryObject instanceof Map ? (Map<String, QueryTable>) queryObject
                : null;
        return queryTable != null ? getAliasQualifiedName(property, queryTable, px)
                : queryTablesMap != null ? getAliasQualifiedName(property, queryTablesMap, px)
                        : getQualifiedName(property, px);
    }

    private String getAliasQualifiedName(Property property, QueryTable queryTable, ParameterizedExpression px) {
        int index = queryTable.getSubqueryIndex();
        for (Property p : queryTable.getColumns()) {
            if (p == property) {
                return queryTable.getAlias() + DOT$ + getPropertySqlName(p);
            }
        }
        String name;
        for (QueryJoin j : queryTable.getJoins()) {
            name = getAliasQualifiedName(property, j.getRightTable(), px);
            if (name == null) {
                continue;
            }
            return name;
        }
        return index == 0 ? getQualifiedName(property, px) : null;
    }

    private String getAliasQualifiedName(Property property, Map<String, QueryTable> queryTablesMap,
            ParameterizedExpression px) {
        QueryTable queryTable;
        for (String key : queryTablesMap.keySet()) {
            queryTable = queryTablesMap.get(key);
            if (queryTable.contains(property)) {
                return key + UND$ + getAliasQualifiedName(property, queryTable, px);
            }
        }
        //      return getQualifiedName(property, px);
        return getSqlVariableName(property);
    }

    @SuppressWarnings("unchecked")
    private String getRecordQualifiedName(Property property, Object queryObject, ParameterizedExpression px) {
        QueryTable queryTable = queryObject instanceof QueryTable ? (QueryTable) queryObject : null;
        Map<String, QueryTable> queryTablesMap = queryObject instanceof Map ? (Map<String, QueryTable>) queryObject
                : null;
        return queryTable != null ? getRecordQualifiedName(property, queryTable, px)
                : queryTablesMap != null ? getRecordQualifiedName(property, queryTablesMap, px)
                        : getQualifiedName(property, px);
    }

    private String getRecordQualifiedName(Property property, QueryTable queryTable, ParameterizedExpression px) {
        int index = queryTable.getSubqueryIndex();
        String qualifier = getSqlJoinQualifier(queryTable);
        String name;
        for (Property p : queryTable.getColumns()) {
            if (p == property) {
                name = index == 0 ? getPropertySqlName(p) : getPropertySqlAlias(p, queryTable);
                return qualifier + DOT$ + name;
            }
        }
        for (QueryJoin j : queryTable.getJoins()) {
            name = getRecordQualifiedName(property, j.getRightTable(), px);
            if (name == null) {
                continue;
            }
            return name;
        }
        //      return index == 0 ? getQualifiedName(property, px) : null;
        if (index == 0) {
            if (property instanceof Entity && px == null) {
                Entity entity = (Entity) property;
                Property primaryKeyProperty = entity.getPrimaryKeyProperty();
                if (primaryKeyProperty != null) {
                    name = getSqlName(primaryKeyProperty);
                    return qualifier + DOT$ + name;
                }
            }
            String qualifiedName = getQualifiedName(property, px);
            return qualifiedName;
        }
        return null;
    }

    private String getRecordQualifiedName(Property property, Map<String, QueryTable> queryTablesMap,
            ParameterizedExpression px) {
        QueryTable queryTable;
        for (String key : queryTablesMap.keySet()) {
            queryTable = queryTablesMap.get(key);
            if (queryTable.contains(property)) {
                return key + UND$ + getRecordQualifiedName(property, queryTable, px);
            }
        }
        //      return getQualifiedName(property, px);
        return getSqlVariableName(property);
    }

    @SuppressWarnings("unchecked")
    private String getUnqualifiedButSuffixedName(Property property, Object queryObject,
            ParameterizedExpression px) {
        QueryTable queryTable = queryObject instanceof QueryTable ? (QueryTable) queryObject : null;
        Map<String, QueryTable> queryTablesMap = queryObject instanceof Map ? (Map<String, QueryTable>) queryObject
                : null;
        return queryTable != null ? getUnqualifiedButSuffixedName(property, queryTable, px)
                : queryTablesMap != null ? getUnqualifiedButSuffixedName(property, queryTablesMap, px)
                        : getQualifiedName(property, px);
    }

    private String getUnqualifiedButSuffixedName(Property property, QueryTable queryTable,
            ParameterizedExpression px) {
        int index = queryTable.getSubqueryIndex();
        String name;
        for (Property p : queryTable.getColumns()) {
            if (p == property) {
                name = index == 0 ? getPropertySqlName(p) : getPropertySqlAlias(p, queryTable);
                return name;
            }
        }
        for (QueryJoin j : queryTable.getJoins()) {
            name = getUnqualifiedButSuffixedName(property, j.getRightTable(), px);
            if (name == null) {
                continue;
            }
            return name;
        }
        return index == 0 ? getQualifiedName(property, px) : null;
    }

    private String getUnqualifiedButSuffixedName(Property property, Map<String, QueryTable> queryTablesMap,
            ParameterizedExpression px) {
        QueryTable queryTable;
        for (String key : queryTablesMap.keySet()) {
            queryTable = queryTablesMap.get(key);
            if (queryTable.contains(property)) {
                return key + DOT$ + getUnqualifiedButSuffixedName(property, queryTable, px);
            }
        }
        //      return getQualifiedName(property, px);
        return getSqlVariableName(property);
    }

    private String getNamedValueName(NamedValue namedValue, ParameterizedExpression px) {
        String name = namedValue.name();
        if (px != null) {
            px.getNamedValuesMap().put(name, namedValue);
        }
        return name;
    }

    /**
     * @param queryJoin
     * @param referencedColumns
     * @param into
     * @param indent
     * @param where
     * @return the SQL statement
     */
    @Override
    public String getSqlSelectStatement(QueryJoin queryJoin, List<Property> referencedColumns, boolean into,
            boolean where, boolean indent) {
        String string = getSqlSelectStatement(queryJoin.getRightTable(), referencedColumns, into, indent);
        if (where) {
            string += EOL$ + getSelectWhere(queryJoin, indent);
        }
        return string;
    }

    private String getSelectJoin(QueryJoin queryJoin, List<Property> referencedColumns, boolean indent) {
        String string = EMPTY;
        QueryTable lt = queryJoin.getLeftTable();
        QueryTable rt = queryJoin.getRightTable();
        String nested = getSelectJoins(rt, referencedColumns, indent);
        String joinop = getSqlJoinOperator(queryJoin.getOperator());
        boolean b = StringUtils.isNotBlank(nested);
        String tab = indent ? StringUtils.repeat(TAB$, lt.getDepth()) : EMPTY;
        string += EOL$ + tab + joinop + (b ? LRB$ : SPC$) + rt.getName();
        if (!rt.getName().equals(rt.getAlias())) {
            //          string += SPC$ + getAs() + SPC$ + rt.getAlias();
            string += SPC$ + rt.getAlias();
        }
        string += b ? nested + RRB$ + EOL$ + tab : SPC$;
        string += getOn() + SPC$ + rt.getAlias() + DOT$ + getPropertySqlName(queryJoin.getRightColumn());
        string += SPC$ + getEQ() + SPC$ + lt.getAlias() + DOT$ + getPropertySqlName(queryJoin.getLeftColumn());
        return string;
    }

    private String getSelectWhere(QueryJoin queryJoin, boolean indent) {
        String string = getWhere();
        QueryTable lt = queryJoin.getLeftTable();
        QueryTable rt = queryJoin.getRightTable();
        string += SPC$ + rt.getAlias() + DOT$ + getPropertySqlName(queryJoin.getRightColumn());
        string += SPC$ + getEQ() + SPC$ + getSqlJoinQualifier(lt) + DOT$
                + getPropertySqlName(queryJoin.getLeftColumn());
        return string;
    }

    /**
     * @param arg1
     * @param operator
     * @return the SQL standard relational expression
     */
    @Override
    public String getSqlStandardRelationalExpression(String arg1, StandardRelationalOp operator) {
        return getSqlStandardRelationalExpression(arg1, operator, "?");
    }

    /**
     * @param arg1
     * @param op
     * @param arg2
     * @return the SQL standard relational expression
     */
    @Override
    public String getSqlStandardRelationalExpression(String arg1, StandardRelationalOp op, String arg2) {
        String expression;
        StandardRelationalOp operator = op == null ? StandardRelationalOp.EQ : op;
        switch (operator) {
        case EQ:
            expression = arg1 + SPC$ + getEQ() + SPC$ + arg2;
            break;
        case NEQ:
            expression = arg1 + SPC$ + getNEQ() + SPC$ + arg2;
            break;
        case GT:
            expression = arg1 + SPC$ + getGT() + SPC$ + arg2;
            break;
        case GTEQ:
            expression = arg1 + SPC$ + getGTEQ() + SPC$ + arg2;
            break;
        case LT:
            expression = arg1 + SPC$ + getLT() + SPC$ + arg2;
            break;
        case LTEQ:
            expression = arg1 + SPC$ + getLTEQ() + SPC$ + arg2;
            break;
        case LIKE:
            expression = arg1 + SPC$ + getLike() + SPC$ + startsWithArgument(1);
            break;
        case NOT_LIKE:
            expression = arg1 + SPC$ + getNotLike() + SPC$ + startsWithArgument(1);
            break;
        case IS_NULL_OR_EQ:
            expression = getIsNullOr(arg1) + SPC$ + getEQ() + SPC$ + arg2;
            break;
        case IS_NULL_OR_NEQ:
            expression = getIsNullOr(arg1) + SPC$ + getNEQ() + SPC$ + arg2;
            break;
        case IS_NULL_OR_GT:
            expression = getIsNullOr(arg1) + SPC$ + getGT() + SPC$ + arg2;
            break;
        case IS_NULL_OR_LTEQ:
            expression = getIsNullOr(arg1) + SPC$ + getLTEQ() + SPC$ + arg2;
            break;
        case IS_NULL_OR_GTEQ:
            expression = getIsNullOr(arg1) + SPC$ + getGTEQ() + SPC$ + arg2;
            break;
        case IS_NULL_OR_LT:
            expression = getIsNullOr(arg1) + SPC$ + getLT() + SPC$ + arg2;
            break;
        case IS_NULL_OR_LIKE:
            expression = getIsNullOr(arg1) + SPC$ + getLike() + SPC$ + startsWithArgument(1);
            break;
        case IS_NULL_OR_NOT_LIKE:
            expression = getIsNullOr(arg1) + SPC$ + getNotLike() + SPC$ + startsWithArgument(1);
            break;
        default:
            expression = arg1 + SPC$ + getEQ() + SPC$ + arg2;
            break;
        }
        return expression;
    }

    private String like(int likeness, String unlimited) {
        String like1 = getLikeString();
        String like2 = getDelimitedString(like1);
        switch (likeness) {
        case 1:
            return getDelimitedString(unlimited + like1);
        case 2:
            return unlimited + getConcat() + like2;
        case 3:
            return getDelimitedString(like1 + unlimited + like1);
        case 4:
            return like2 + getConcat() + unlimited + getConcat() + like2;
        case 5:
            return getDelimitedString(like1 + unlimited);
        case 6:
            return like2 + getConcat() + unlimited;
        }
        return unlimited;
    }

    private String startsWithValue(Object valor) {
        return getString(valor) + getLikeString();
    }

    private String containsValue(Object valor) {
        return getLikeString() + getString(valor) + getLikeString();
    }

    private String endsWithValue(Object valor) {
        return getLikeString() + getString(valor);
    }

    private String startsWithArgument(int index) {
        String argx = LCB$ + index + RCB$;
        String like = SQM$ + SQM$ + getLikeString() + SQM$ + SQM$;
        return argx + getConcat() + like;
    }

    private String containsArgument(int index) {
        String argx = LCB$ + index + RCB$;
        String like = SQM$ + SQM$ + getLikeString() + SQM$ + SQM$;
        return like + getConcat() + argx + getConcat() + like;
    }

    private String endsWithArgument(int index) {
        String argx = LCB$ + index + RCB$;
        String like = SQM$ + SQM$ + getLikeString() + SQM$ + SQM$;
        return like + getConcat() + argx;
    }

    private String average(String... strings) {
        String case$ = getAverageCasePattern();
        String arg0$ = join(EMPTY, getAdd(), strings);
        String arg1$ = count(strings);
        String[] placeHolders = new String[] { ARG0, ARG1 };
        String[] argumentList = new String[] { arg0$, arg1$ };
        return StringUtils.replaceEach(case$, placeHolders, argumentList);
    }

    protected String getAverageCasePattern() {
        return "case when {1} = 0 then null else {0} / {1} end";
    }

    private String count(String... strings) {
        String case$ = getCountCasePattern();
        int i = 0;
        String[] strings1 = new String[strings.length];
        for (String expresion : strings) {
            if (StringUtils.isNotBlank(expresion)) {
                strings1[i++] = case$.replace(ARG0, singleQuotedOrInParentheses(expresion));
            }
        }
        return join(EMPTY, getAdd(), strings1);
    }

    protected String getCountCasePattern() {
        return "case when {0} is null then 0 else 1 end";
    }

    private String maximum(String... strings) {
        return imum(getGTEQ(), strings);
    }

    private String minimum(String... strings) {
        return imum(getLTEQ(), strings);
    }

    private String imum(String comparador, String... strings) {
        String case$ = "case ";
        String when$ = "when {1} then {0} ";
        String else$ = "else null end";
        String arg0$;
        String arg1$;
        String conector = getAnd();
        String[] placeHolders = new String[] { ARG0, ARG1 };
        //      String[] argumentList = new String[]{arg0$, arg1$};
        String[] strings1 = new String[strings.length];
        int i1;
        for (int i = 0; i < strings.length; i++) {
            arg0$ = strings[i];
            if (StringUtils.isNotBlank(arg0$)) {
                Arrays.fill(strings1, EMPTY);
                i1 = 0;
                for (int j = 0; j < strings.length; j++) {
                    if (i != j) {
                        arg1$ = strings[j];
                        if (StringUtils.isNotBlank(arg1$)) {
                            strings1[i1++] = join(EMPTY, comparador, arg0$, arg1$);
                        }
                    }
                }
                arg1$ = join(EMPTY, conector, strings1);
                case$ += StringUtils.replaceEach(when$, placeHolders, new String[] { arg0$, arg1$ });
            }
        }
        return case$ + else$;
    }

    protected String bind(String op1, String string) {
        String op1$ = StringUtils.trimToEmpty(op1);
        String expresion = StringUtils.trimToEmpty(string);
        if (StringUtils.isNotBlank(op1$) && StringUtils.isNotBlank(expresion)) {
            return op1$ + inParentheses(expresion);
        }
        return expresion;
    }

    protected String call(Operator operator, int arguments) {
        String function = operator.name().toLowerCase();
        return call(function, arguments);
    }

    protected String call(String function, int arguments) {
        String[] placeHolders = null;
        if (arguments > 0) {
            placeHolders = new String[arguments];
            for (int i = 0; i < arguments; i++) {
                placeHolders[i] = LCB$ + i + RCB$;
            }
        }
        return call(function, placeHolders);
    }

    protected String call(Operator operator, String... arguments) {
        String function = operator.name().toLowerCase();
        return call(function, arguments);
    }

    protected String call(String function, String... arguments) {
        String string = arguments == null || arguments.length == 0 ? LRB$ + RRB$
                : StrUtils.enclose(StringUtils.join(arguments, SEP$));
        return function + string;
    }

    protected String join(String op1, String op2, String... strings) {
        String op1$ = StringUtils.equals(op1, op2) ? EMPTY : StringUtils.trimToEmpty(op1);
        String op2$ = SPC$ + StringUtils.trimToEmpty(op2) + SPC$;
        String expresion;
        String expresiones = EMPTY;
        boolean b = op1$.equals(getCast()) || op1$.equals(getCoalesce());
        int n = 0;
        for (String string : strings) {
            if (StringUtils.isNotBlank(string)) {
                expresion = b ? StringUtils.trimToEmpty(string) : caseSingleQuotedOrInParentheses(string);
                expresiones += op2$ + expresion;
                n++;
            }
        }
        if (n > 0) {
            expresiones = StringUtils.removeStart(expresiones, op2$);
            if (StringUtils.isNotBlank(op1$)) {
                return op1$ + inParentheses(expresiones);
            }
        }
        return inParentheses(expresiones);
    }

    protected String and(String... arguments) {
        return connect(getAnd(), arguments);
    }

    protected String or(String... arguments) {
        return connect(getOr(), arguments);
    }

    protected String xor(String... arguments) {
        int l = arguments.length;
        int m = l - 1;
        int n = 0;
        for (int i = 1; i < l; i++) {
            n += i;
        }
        String[] ands = new String[n];
        int index = 0;
        for (int i = 0; i < m; i++) {
            for (int j = i + 1; j < l; j++) {
                ands[index++] = and(arguments[i], arguments[j]);
            }
        }
        return and(or(arguments), not(or(ands)));
    }

    protected String not(String argument) {
        return getNot() + StrUtils.enclose(argument);
    }

    protected String connect(char connective, String... arguments) {
        return StrUtils.enclose(StringUtils.join(arguments, connective + SPC$));
    }

    protected String connect(String connective, String... arguments) {
        return StrUtils.enclose(StringUtils.join(arguments, SPC$ + connective + SPC$));
    }

    private boolean isCase(String string) {
        if (StringUtils.isBlank(string)) {
            return false;
        }
        String case$ = getCase() + SPC$;
        String end$ = SPC$ + getEnd();
        String s = StringUtils.trimToEmpty(string);
        return StringUtils.startsWithIgnoreCase(s, case$) && StringUtils.endsWithIgnoreCase(s, end$);
    }

    private boolean isSingle(String string) {
        if (StringUtils.isBlank(string)) {
            return false;
        }
        String s = StringUtils.trimToEmpty(string);
        return !s.contains(SPC$);
    }

    private boolean isQuoted(String string) {
        if (StringUtils.isBlank(string)) {
            return false;
        }
        String s = StringUtils.trimToEmpty(string);
        return StrUtils.isDelimited(s, SQM);
    }

    private boolean isInParentheses(String string) {
        if (StringUtils.isBlank(string)) {
            return false;
        }
        String[] searchStrings = (String[]) ArrayUtils.add(stringsOperadorExpresionUnario(), getCoalesce());
        String s = removeStart(string, searchStrings);
        return StrUtils.isDelimited(s, LRB, RRB);
    }

    private String[] stringsOperadorExpresionUnario() {
        ScalarOp[] enums = ScalarOp.values();
        String[] strings = null;
        String string;
        for (ScalarOp sop : enums) {
            string = getString(sop);
            if (StringUtils.isNotBlank(string)) {
                strings = (String[]) ArrayUtils.add(strings, StringUtils.trimToEmpty(string));
            }
        }
        return strings;
    }

    private String removeStart(String string, String[] searchStrings) {
        String s = StringUtils.trimToEmpty(string);
        String p;
        for (String searchString : searchStrings) {
            if (StringUtils.isNotBlank(searchString)) {
                p = StringUtils.trimToEmpty(searchString);
                if (StringUtils.startsWithIgnoreCase(s, p)) {
                    return StringUtils.trimToEmpty(StringUtils.removeStartIgnoreCase(s, p));
                }
            }
        }
        return s;
    }

    private String caseSingleQuotedOrInParentheses(String string) {
        String s = StringUtils.trimToEmpty(string);
        boolean b = StringUtils.isBlank(s) || isCase(s);
        return b ? s : singleQuotedOrInParentheses(s);
    }

    private String singleQuotedOrInParentheses(String string) {
        String s = StringUtils.trimToEmpty(string);
        boolean b = StringUtils.isBlank(s) || isSingle(s);
        return b ? s : quotedOrInParentheses(s);
    }

    private String quotedOrInParentheses(String string) {
        String s = StringUtils.trimToEmpty(string);
        boolean b = StringUtils.isBlank(s) || isQuoted(s);
        return b ? s : inParentheses(s);
    }

    private String inParentheses(String string) {
        String s = StringUtils.trimToEmpty(string);
        boolean b = StringUtils.isBlank(s) || isInParentheses(s);
        return b ? s : LRB$ + s + RRB$;
    }

}