me.hurel.hqlbuilder.builder.HQBQueryStringVisitor.java Source code

Java tutorial

Introduction

Here is the source code for me.hurel.hqlbuilder.builder.HQBQueryStringVisitor.java

Source

/**
 * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. 
 * If a copy of the MPL was not distributed with this file, 
 * You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * Contributors:
 *     Nathan Hurel - initial API and implementation
 */
package me.hurel.hqlbuilder.builder;

import me.hurel.hqlbuilder.functions.Function;
import me.hurel.hqlbuilder.functions.Function.FUNCTION;
import me.hurel.hqlbuilder.functions.MultiParameterFunction;
import me.hurel.hqlbuilder.internal.ProxyUtil;
import org.apache.commons.lang3.StringUtils;

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

public class HQBQueryStringVisitor implements HQBVisitor {

    private final StringBuilder query = new StringBuilder();
    private final Map<Object, String> aliases;
    private final Map<Object, String> paths;
    private final Map<Object, Object> parentEntities;
    private final List<Object> joinedEntities;
    private List<Object> parameters;
    private boolean from = false;
    private int params = 1;
    private boolean acceptParameters = false;

    public HQBQueryStringVisitor(Map<Object, String> aliases, Map<Object, String> paths,
            Map<Object, Object> parentEntities, List<Object> joinedEntities) {
        this.aliases = aliases;
        this.paths = paths;
        this.parentEntities = parentEntities;
        this.joinedEntities = joinedEntities;
    }

    public void visit(SelectHibernateQueryBuilder select) {
        acceptParameters = false;
        query.append("SELECT ");
        if (select.distinct) {
            query.append("DISTINCT ");
        }
        int i = select.map.size();
        for (Map.Entry<Object, String> object : select.map.entrySet()) {
            if (object.getKey() instanceof CaseWhenHibernateQueryBuilder) {
                appendCaseWhenClause((CaseWhenHibernateQueryBuilder) object.getKey());
            } else {
                appendAliasOrPath(object.getKey());
            }
            if (object.getValue() != null && object.getValue().trim().length() > 0) {
                query.append(" AS ").append(object.getValue());
            }
            if (--i > 0) {
                query.append(',');
            }
            query.append(' ');
        }
    }

    public void visit(OrderByHibernateQueryBuilder order) {
        acceptParameters = false;
        query.append(order.separator).append(' ');
        int i = order.aliases.length;
        for (Object alias : order.aliases) {
            appendAliasOrPath(alias);
            if (--i > 0) {
                query.append(',');
            }
            query.append(' ');
        }
        if (order.priority != null) {
            query.append(order.priority.priority).append(' ');
        }
    }

    public void visit(ExistsHibernateQueryBuilder exists) {
        acceptParameters = false;
        query.append(exists.separator).append(' ');
        if (exists.not) {
            query.append("NOT ");
        }
        query.append("EXISTS ( ");
        visit((SelectHibernateQueryBuilder) exists);
        from = false;
    }

    public void visit(FromHibernateQueryBuilder fromClause) {
        acceptParameters = false;
        query.append(from ? ',' : fromClause.join).append(' ')
                .append(ProxyUtil.getActualClass(fromClause.object.getClass()).getSimpleName()).append(' ')
                .append(aliases.get(fromClause.object)).append(' ');
        from = true;
    }

    public void visit(JoinQueryBuilder join) {
        acceptParameters = false;
        query.append(join.join).append(join.fetch ? " FETCH " : ' ').append(getReducedPath(join.object, false))
                .append(' ').append(aliases.get(join.object)).append(' ');
    }

    public void visit(WhereHibernateQueryBuilder<?> builder) {
        acceptParameters = false;
        query.append(builder.operator).append(' ');
        if (builder.group) {
            query.append("( ");
        }
        if (builder.value instanceof CaseWhenHibernateQueryBuilder) {
            appendCaseWhenClause((CaseWhenHibernateQueryBuilder) builder.value);
        } else {
            appendReducedPath(builder.value);
        }
        query.append(' ');
    }

    public void visit(NullConditionHibernateQueryBuilder<?> builder) {
        acceptParameters = false;
        query.append(builder.operator).append(' ');
        closeGroup(builder);
    }

    public void visit(InConditionHibernateQueryBuilder<?> builder) {
        acceptParameters = true;
        query.append(builder.operator);
        visitIn(builder.values);
        closeGroup(builder);
    }

    private void visitIn(Object[] values) {
        acceptParameters = true;
        query.append(" (");
        if (values != null) {
            int i = 1;
            for (Object value : values) {
                if (parentEntities.containsKey(value)) {
                    appendAliasOrPath(value);
                } else {
                    query.append('?').append(params++);
                    addParameter(value);
                }
                if (i < values.length) {
                    query.append(", ");
                }
                i++;
            }
        }
        query.append(") ");
    }

    public void visit(ConditionHibernateQueryBuilder<?> builder) {
        acceptParameters = true;
        query.append(builder.operator).append(' ');
        if (parentEntities.containsKey(builder.value) || builder.value instanceof Function<?>) {
            appendAliasOrPath(builder.value);
        } else {
            query.append('?').append(params++);
            addParameter(builder.value);
        }
        query.append(' ');
        closeGroup(builder);
    }

    private void closeGroup(ConditionHibernateQueryBuilder<?> builder) {
        acceptParameters = false;
        int group = builder.closeGroup + builder.closeExists;
        while (group-- > 0) {
            query.append(") ");
        }
    }

    public void visit(GroupByHibernateQueryBuilder builder) {
        acceptParameters = false;
        query.append("GROUP BY ");
        int i = builder.properties.length;
        for (Object property : builder.properties) {
            if (property instanceof CaseWhenHibernateQueryBuilder) {
                appendCaseWhenClause((CaseWhenHibernateQueryBuilder) property);
            } else if (property instanceof MultiParameterFunction) {
                appendAliasOrPath(property);
            } else {
                query.append(getAliasOrPath(property));
            }
            if (--i > 0) {
                query.append(',');
            }
            query.append(' ');
        }
    }

    public void visit(UnfinishedCaseWhenQueryBuilder builder) {
        acceptParameters = false;
        query.append("THEN ");
        if (builder.value instanceof String || builder.value instanceof Character) {
            query.append('\'').append(builder.value).append('\'');
        } else {
            query.append(builder.value);
        }
        query.append(' ');
    }

    public void visit(CaseWhenHibernateQueryBuilder builder) {
        acceptParameters = false;
        query.append("ELSE ");
        if (builder.value instanceof String || builder.value instanceof Character) {
            query.append('\'').append(builder.value).append('\'');
        } else {
            query.append(builder.value);
        }
        query.append(" END");
    }

    private void appendCaseWhenClause(CaseWhenHibernateQueryBuilder builder) {
        acceptParameters = false;
        query.append('(');
        for (HibernateQueryBuilder caseWhenBuilder : builder.chain) {
            caseWhenBuilder.accept(this);
        }
        query.append(')');
    }

    private void appendAliasOrPath(Object entity) {
        if (entity instanceof Function<?>) {
            Function<?> function = (Function<?>) entity;
            query.append(function.getName()).append('(');
            if (function.getEntity() instanceof Function<?>) {
                appendAliasOrPath(function.getEntity());
            } else {
                // FIXME : any more elegant way to do this ?
                if (function.getName().equals(FUNCTION.COUNT.getFunction()) && function.getEntity().equals("*")) {
                    query.append(function.getEntity());
                } else if (function instanceof MultiParameterFunction) {
                    appendMultiParameters((MultiParameterFunction) function);
                } else {
                    query.append(getAliasOrPath(function.getEntity()));
                }
            }
            query.append(')');
        } else {
            query.append(getAliasOrPath(entity));
        }
    }

    private void appendMultiParameters(MultiParameterFunction function) {
        Object[] properties = ((MultiParameterFunction) function).getEntity();
        int i = properties.length;
        for (Object property : properties) {
            if (property instanceof CaseWhenHibernateQueryBuilder) {
                appendCaseWhenClause((CaseWhenHibernateQueryBuilder) property);
            } else {
                query.append(getAliasOrPath(property));
            }
            if (--i > 0) {
                query.append(", ");
            }
        }
    }

    private String getAliasOrPath(Object entity) {
        String result = null;
        if (joinedEntities.contains(entity)) {
            result = aliases.get(entity);
        } else if (!parentEntities.containsKey(entity) && entity instanceof String) {
            if (acceptParameters) {
                addParameter(entity);
                result = "?" + (params++);
            } else {
                result = (String) entity;
            }
        }
        if (result == null) {
            Object knownJoinParent = entity;
            while (knownJoinParent != null && !joinedEntities.contains(knownJoinParent)) {
                knownJoinParent = parentEntities.get(knownJoinParent);
            }
            if (!joinedEntities.contains(knownJoinParent)) {
                throw new RuntimeException("Failed to continue query after [" + query.toString()
                        + "] because an entity was used in clause but neither it nor its parent appears explicitly in the from clause");
            }
            if (!aliases.containsKey(knownJoinParent)) {
                throw new RuntimeException("Failed to continue query after [" + query.toString()
                        + "] because alias of the parent joined entity is unknown");
            }
            result = paths.get(entity);
            if (entity != knownJoinParent) {
                String joinedParentPath = paths.get(knownJoinParent);
                String end = StringUtils.difference(joinedParentPath, result);
                result = new StringBuilder(aliases.get(knownJoinParent)).append(end.startsWith(".") ? "" : '.')
                        .append(end).toString();
            }
        }
        return result;
    }

    private void appendReducedPath(Object entity) {
        if (entity instanceof Function<?>) {
            Function<?> function = (Function<?>) entity;
            query.append(function.getName()).append('(');
            if (function.getEntity() instanceof Function<?>) {
                appendAliasOrPath(function.getEntity());
            } else if (function instanceof MultiParameterFunction) {
                appendMultiParameters((MultiParameterFunction) function);
            } else {
                // FIXME : any more elegant way to do this ?
                if (function.getName().equals(FUNCTION.COUNT.getFunction()) && function.getEntity().equals("*")) {
                    query.append(function.getEntity());
                } else {
                    query.append(getReducedPath(function.getEntity(), true));
                }
            }
            query.append(')');
        } else {
            query.append(getReducedPath(entity, true));
        }
    }

    private String getReducedPath(Object entity, boolean shortest) {
        String result;
        if (shortest && joinedEntities.contains(entity)) {
            return aliases.get(entity);
        }
        result = paths.get(entity);
        Object knownJoinParent = parentEntities.get(entity);
        Object previousKnownParent = null;
        while (knownJoinParent != null && previousKnownParent == null) {
            if (joinedEntities.contains(knownJoinParent)) {
                previousKnownParent = knownJoinParent;
            }
            knownJoinParent = parentEntities.get(knownJoinParent);
        }
        if (previousKnownParent != null) {
            String parentPath = paths.get(previousKnownParent);
            String end = StringUtils.difference(parentPath, result);
            result = new StringBuilder(aliases.get(previousKnownParent)).append(end).toString();
        }
        return result;
    }

    private void addParameter(Object parameter) {
        if (parameters == null) {
            parameters = new ArrayList<Object>();
        }
        parameters.add(parameter);
    }

    public String getQuery() {
        return query.toString();
    }

    public List<Object> getParameters() {
        return parameters;
    }
}