/*
 * Copyright (c) 2006, 2018 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0,
 * or the Eclipse Distribution License v. 1.0 which is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
 */

// Contributors:
//     Oracle - initial API and implementation
//
package org.eclipse.persistence.jpa.jpql.tools.model;

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

import org.eclipse.persistence.jpa.jpql.LiteralType;
import org.eclipse.persistence.jpa.jpql.LiteralVisitor;
import org.eclipse.persistence.jpa.jpql.parser.AbsExpression;
import org.eclipse.persistence.jpa.jpql.parser.AbstractExpression;
import org.eclipse.persistence.jpa.jpql.parser.AbstractExpressionVisitor;
import org.eclipse.persistence.jpa.jpql.parser.AbstractSchemaName;
import org.eclipse.persistence.jpa.jpql.parser.AbstractTraverseChildrenVisitor;
import org.eclipse.persistence.jpa.jpql.parser.AdditionExpression;
import org.eclipse.persistence.jpa.jpql.parser.AllOrAnyExpression;
import org.eclipse.persistence.jpa.jpql.parser.AndExpression;
import org.eclipse.persistence.jpa.jpql.parser.AnonymousExpressionVisitor;
import org.eclipse.persistence.jpa.jpql.parser.ArithmeticFactor;
import org.eclipse.persistence.jpa.jpql.parser.AvgFunction;
import org.eclipse.persistence.jpa.jpql.parser.BadExpression;
import org.eclipse.persistence.jpa.jpql.parser.BetweenExpression;
import org.eclipse.persistence.jpa.jpql.parser.CaseExpression;
import org.eclipse.persistence.jpa.jpql.parser.CoalesceExpression;
import org.eclipse.persistence.jpa.jpql.parser.CollectionExpression;
import org.eclipse.persistence.jpa.jpql.parser.CollectionMemberDeclaration;
import org.eclipse.persistence.jpa.jpql.parser.CollectionMemberExpression;
import org.eclipse.persistence.jpa.jpql.parser.CollectionValuedPathExpression;
import org.eclipse.persistence.jpa.jpql.parser.ComparisonExpression;
import org.eclipse.persistence.jpa.jpql.parser.ConcatExpression;
import org.eclipse.persistence.jpa.jpql.parser.ConstructorExpression;
import org.eclipse.persistence.jpa.jpql.parser.CountFunction;
import org.eclipse.persistence.jpa.jpql.parser.DateTime;
import org.eclipse.persistence.jpa.jpql.parser.DeleteClause;
import org.eclipse.persistence.jpa.jpql.parser.DeleteStatement;
import org.eclipse.persistence.jpa.jpql.parser.DivisionExpression;
import org.eclipse.persistence.jpa.jpql.parser.EmptyCollectionComparisonExpression;
import org.eclipse.persistence.jpa.jpql.parser.EntityTypeLiteral;
import org.eclipse.persistence.jpa.jpql.parser.EntryExpression;
import org.eclipse.persistence.jpa.jpql.parser.ExistsExpression;
import org.eclipse.persistence.jpa.jpql.parser.Expression;
import org.eclipse.persistence.jpa.jpql.parser.FromClause;
import org.eclipse.persistence.jpa.jpql.parser.FunctionExpression;
import org.eclipse.persistence.jpa.jpql.parser.GroupByClause;
import org.eclipse.persistence.jpa.jpql.parser.HavingClause;
import org.eclipse.persistence.jpa.jpql.parser.IdentificationVariable;
import org.eclipse.persistence.jpa.jpql.parser.IdentificationVariableDeclaration;
import org.eclipse.persistence.jpa.jpql.parser.InExpression;
import org.eclipse.persistence.jpa.jpql.parser.IndexExpression;
import org.eclipse.persistence.jpa.jpql.parser.InputParameter;
import org.eclipse.persistence.jpa.jpql.parser.JPQLExpression;
import org.eclipse.persistence.jpa.jpql.parser.Join;
import org.eclipse.persistence.jpa.jpql.parser.KeyExpression;
import org.eclipse.persistence.jpa.jpql.parser.KeywordExpression;
import org.eclipse.persistence.jpa.jpql.parser.LengthExpression;
import org.eclipse.persistence.jpa.jpql.parser.LikeExpression;
import org.eclipse.persistence.jpa.jpql.parser.LocateExpression;
import org.eclipse.persistence.jpa.jpql.parser.LowerExpression;
import org.eclipse.persistence.jpa.jpql.parser.MaxFunction;
import org.eclipse.persistence.jpa.jpql.parser.MinFunction;
import org.eclipse.persistence.jpa.jpql.parser.ModExpression;
import org.eclipse.persistence.jpa.jpql.parser.MultiplicationExpression;
import org.eclipse.persistence.jpa.jpql.parser.NotExpression;
import org.eclipse.persistence.jpa.jpql.parser.NullComparisonExpression;
import org.eclipse.persistence.jpa.jpql.parser.NullExpression;
import org.eclipse.persistence.jpa.jpql.parser.NullIfExpression;
import org.eclipse.persistence.jpa.jpql.parser.NumericLiteral;
import org.eclipse.persistence.jpa.jpql.parser.ObjectExpression;
import org.eclipse.persistence.jpa.jpql.parser.OnClause;
import org.eclipse.persistence.jpa.jpql.parser.OrExpression;
import org.eclipse.persistence.jpa.jpql.parser.OrderByClause;
import org.eclipse.persistence.jpa.jpql.parser.OrderByItem;
import org.eclipse.persistence.jpa.jpql.parser.RangeVariableDeclaration;
import org.eclipse.persistence.jpa.jpql.parser.ResultVariable;
import org.eclipse.persistence.jpa.jpql.parser.SelectClause;
import org.eclipse.persistence.jpa.jpql.parser.SelectStatement;
import org.eclipse.persistence.jpa.jpql.parser.SimpleFromClause;
import org.eclipse.persistence.jpa.jpql.parser.SimpleSelectClause;
import org.eclipse.persistence.jpa.jpql.parser.SimpleSelectStatement;
import org.eclipse.persistence.jpa.jpql.parser.SizeExpression;
import org.eclipse.persistence.jpa.jpql.parser.SqrtExpression;
import org.eclipse.persistence.jpa.jpql.parser.StateFieldPathExpression;
import org.eclipse.persistence.jpa.jpql.parser.StringLiteral;
import org.eclipse.persistence.jpa.jpql.parser.SubExpression;
import org.eclipse.persistence.jpa.jpql.parser.SubstringExpression;
import org.eclipse.persistence.jpa.jpql.parser.SubtractionExpression;
import org.eclipse.persistence.jpa.jpql.parser.SumFunction;
import org.eclipse.persistence.jpa.jpql.parser.TreatExpression;
import org.eclipse.persistence.jpa.jpql.parser.TrimExpression;
import org.eclipse.persistence.jpa.jpql.parser.TypeExpression;
import org.eclipse.persistence.jpa.jpql.parser.UnknownExpression;
import org.eclipse.persistence.jpa.jpql.parser.UpdateClause;
import org.eclipse.persistence.jpa.jpql.parser.UpdateItem;
import org.eclipse.persistence.jpa.jpql.parser.UpdateStatement;
import org.eclipse.persistence.jpa.jpql.parser.UpperExpression;
import org.eclipse.persistence.jpa.jpql.parser.ValueExpression;
import org.eclipse.persistence.jpa.jpql.parser.WhenClause;
import org.eclipse.persistence.jpa.jpql.parser.WhereClause;
import org.eclipse.persistence.jpa.jpql.tools.model.query.AbsExpressionStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.AbstractFromClauseStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.AbstractIdentificationVariableDeclarationStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.AbstractRangeVariableDeclarationStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.AbstractSchemaNameStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.AbstractSelectStatementStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.AbstractStateObjectVisitor;
import org.eclipse.persistence.jpa.jpql.tools.model.query.AdditionExpressionStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.AllOrAnyExpressionStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.AndExpressionStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.ArithmeticFactorStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.AvgFunctionStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.BadExpressionStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.BetweenExpressionStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.CaseExpressionStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.CoalesceExpressionStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.CollectionExpressionStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.CollectionMemberDeclarationStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.CollectionMemberExpressionStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.CollectionValuedPathExpressionStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.ComparisonExpressionStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.ConcatExpressionStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.ConstructorExpressionStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.CountFunctionStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.DateTimeStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.DeleteStatementStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.DerivedPathIdentificationVariableDeclarationStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.DivisionExpressionStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.EmptyCollectionComparisonExpressionStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.EntityTypeLiteralStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.EntryExpressionStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.ExistsExpressionStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.FromClauseStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.FunctionExpressionStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.GroupByClauseStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.HavingClauseStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.IdentificationVariableDeclarationStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.IdentificationVariableStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.InExpressionStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.IndexExpressionStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.InputParameterStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.JPQLQueryStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.JoinStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.KeyExpressionStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.KeywordExpressionStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.LengthExpressionStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.LikeExpressionStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.LocateExpressionStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.LowerExpressionStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.MaxFunctionStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.MinFunctionStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.ModExpressionStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.MultiplicationExpressionStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.NotExpressionStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.NullComparisonExpressionStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.NullIfExpressionStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.NumericLiteralStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.ObjectExpressionStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.OrExpressionStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.OrderByClauseStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.OrderByItemStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.ResultVariableStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.SelectClauseStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.SelectStatementStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.SimpleFromClauseStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.SimpleSelectClauseStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.SimpleSelectStatementStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.SizeExpressionStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.SqrtExpressionStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.StateFieldPathExpressionStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.StateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.StateObjectVisitor;
import org.eclipse.persistence.jpa.jpql.tools.model.query.StringLiteralStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.SubExpressionStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.SubstringExpressionStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.SubtractionExpressionStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.SumFunctionStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.TreatExpressionStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.TrimExpressionStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.TypeExpressionStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.UnknownExpressionStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.UpdateClauseStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.UpdateItemStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.UpdateStatementStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.UpperExpressionStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.ValueExpressionStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.WhenClauseStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.WhereClauseStateObject;
import org.eclipse.persistence.jpa.jpql.tools.spi.IManagedTypeProvider;
import org.eclipse.persistence.jpa.jpql.utility.CollectionTools;

/**
 * The default implementation of a {@link IBuilder}, which creates a {@link StateObject}
 * representation of the {@link org.eclipse.persistence.jpa.jpql.parser.Expression Expression} being
 * visited.
 * <p>
 * Provisional API: This interface is part of an interim API that is still under development and
 * expected to change significantly before reaching stability. It is available at this early stage
 * to solicit feedback from pioneering adopters on the understanding that any code that uses this
 * API will almost certainly be broken (repeatedly) as the API evolves.
 *
 * @see Expression
 * @see StateObject
 *
 * @version 2.5
 * @since 2.4
 * @author Pascal Filion
 */
@SuppressWarnings("nls")
public abstract class BasicStateObjectBuilder extends AbstractExpressionVisitor {

    /**
     *
     */
    private IBuilder<CollectionMemberDeclarationStateObject, AbstractFromClauseStateObject> collectionDeclarationBuilder;

    /**
     *
     */
    private CollectionExpressionVisitor collectionExpressionVisitor;

    /**
     *
     */
    private IBuilder<DeleteStatementStateObject, JPQLQueryStateObject> deleteStatementBuilder;

    /**
     * This {@link IBuilder} is responsible to build an <code><b>DELETE</b></code> statement,
     * which populates the <code><b>DELETE</b></code> clause and the optional <code><b>WHERE</b></code>
     * clause by converting the information contained in the parsed JPQL query.
     */
    private IBuilder<JoinStateObject, AbstractIdentificationVariableDeclarationStateObject> joinBuilder;

    /**
     *
     */
    IJPQLQueryBuilder jpqlQueryBuilder;

    /**
     * This visitor is used to retrieve a variable name from various type of an {@link Expression}.
     */
    private LiteralVisitor literalVisitor;

    /**
     *
     */
    protected IManagedTypeProvider managedTypeProvider;

    /**
     * The parent {@link StateObject} of the {@link StateObject} to create.
     */
    protected JPQLQueryStateObject parent;

    /**
     *
     */
    private IBuilder<AbstractIdentificationVariableDeclarationStateObject, FromClauseStateObject> rangeDeclarationBuilder;

    /**
     *
     */
    private IBuilder<StateObject, SelectClauseStateObject> selectItemBuilder;

    /**
     * This {@link IBuilder} is responsible to build a top-level <code><b>SELECT</b></code> statement,
     * which populates the various clauses by converting the information contained in the parsed JPQL
     * query.
     */
    private IBuilder<SelectStatementStateObject, JPQLQueryStateObject> selectStatementBuilder;

    /**
     *
     */
    private IBuilder<AbstractIdentificationVariableDeclarationStateObject, SimpleFromClauseStateObject> simpleRangeDeclarationBuilder;

    /**
     * This {@link IBuilder} is responsible to build a subquery <code><b>SELECT</b></code> statement,
     * which populates the various clauses by converting the information contained in the parsed JPQL
     * query.
     */
    private IBuilder<SimpleSelectStatementStateObject, StateObject> simpleSelectStatementBuilder;

    /**
     * The {@link StateObject} that was created based on the visited {@link Expression}.
     */
    protected StateObject stateObject;

    /**
     * This {@link IBuilder} is responsible to build an <code><b>UPDATE</b></code> statement,
     * which populates the <code><b>UPDATE</b></code> clause and the optional <code><b>WHERE</b></code>
     * clause by converting the information contained in the parsed JPQL query.
     */
    private IBuilder<UpdateStatementStateObject, JPQLQueryStateObject> updateStatementBuilder;

    /**
     * This {@link IBuilder} is responsible to build a <code><b>WHEN</b></code> clause for the
     * <code><b>CASE</b></code> expression by converting the information contained in the parsed
     * JPQL query.
     */
    private IBuilder<CaseExpressionStateObject, CaseExpressionStateObject> whenClauseBuilder;

    /**
     * Creates a new <code>StateObjectBuilder</code>.
     */
    protected BasicStateObjectBuilder() {
        super();
    }

    @SuppressWarnings("unchecked")
    protected <T extends StateObject> List<T> buildChildren(Expression expression) {

        StateObject oldStateObject = stateObject;
        List<T> stateObjects = new ArrayList<T>();

        for (Expression child : children(expression)) {
            child.accept(this);
            stateObjects.add((T) stateObject);
            stateObject = oldStateObject;
        }

        return stateObjects;
    }

    protected IBuilder<CollectionMemberDeclarationStateObject, AbstractFromClauseStateObject> buildCollectionDeclarationBuilder() {
        return new CollectionMemberDeclarationBuilder();
    }

    protected IBuilder<DeleteStatementStateObject, JPQLQueryStateObject> buildDeleteStatementBuilder() {
        return new DeleteStatementBuilder();
    }

    protected IBuilder<JoinStateObject, AbstractIdentificationVariableDeclarationStateObject> buildJoinBuilder() {
        return new JoinBuilder();
    }

    /**
     * Creates the visitor that can retrieve the "literal" value from a given {@link Expression}
     * based on the desired {@link LiteralType}.
     *
     * @return A new concrete instance of {@link LiteralVisitor}
     */
    protected abstract LiteralVisitor buildLiteralVisitor();

    protected IBuilder<AbstractIdentificationVariableDeclarationStateObject, FromClauseStateObject> buildRangeDeclarationBuilder() {
        return new RangeDeclarationBuilder();
    }

    protected IBuilder<StateObject, SelectClauseStateObject> buildSelectItemBuilder() {
        return new SelectItemBuilder();
    }

    protected IBuilder<SelectStatementStateObject, JPQLQueryStateObject> buildSelectStatementBuilder() {
        return new SelectStatementBuilder();
    }

    protected IBuilder<AbstractIdentificationVariableDeclarationStateObject, SimpleFromClauseStateObject> buildSimpleRangeDeclarationBuilder() {
        return new SimpleRangeDeclarationBuilder();
    }

    protected IBuilder<SimpleSelectStatementStateObject, StateObject> buildSimpleSelectStatementBuilder() {
        return new SimpleSelectStatementBuilder();
    }

    /**
     * Visits the given {@link Expression} and returned its {@link StateObject}.
     *
     * @param expression The {@link Expression} to be visited by this builder
     * @return The {@link StateObject} representation of the given {@link Expression} or <code>null</code>
     * if nothing could be created
     */
    protected final StateObject buildStateObjectImp(Expression expression) {
        expression.accept(this);
        return stateObject;
    }

    protected IBuilder<UpdateStatementStateObject, JPQLQueryStateObject> buildUpdateStatementBuilder() {
        return new UpdateStatementBuilder();
    }

    protected IBuilder<CaseExpressionStateObject, CaseExpressionStateObject> buildWhenClauseBuilder() {
        return new WhenClauseBuilder();
    }

    @SuppressWarnings("unchecked")
    protected <T extends Expression> List<T> children(Expression expression) {
        CollectionExpressionVisitor visitor = getCollectionExpressionVisitor();

        try {
            expression.accept(visitor);
            return (List<T>) visitor.children;
        }
        finally {
            visitor.reset();
        }
    }

    protected IBuilder<CollectionMemberDeclarationStateObject, AbstractFromClauseStateObject> getCollectionDeclarationBuilder() {
        if (collectionDeclarationBuilder == null) {
            collectionDeclarationBuilder = buildCollectionDeclarationBuilder();
        }
        return collectionDeclarationBuilder;
    }

    protected CollectionExpressionVisitor getCollectionExpressionVisitor() {
        if (collectionExpressionVisitor == null) {
            collectionExpressionVisitor = new CollectionExpressionVisitor();
        }
        return collectionExpressionVisitor;
    }

    protected IBuilder<DeleteStatementStateObject, JPQLQueryStateObject> getDeleteStatementBuilder() {
        if (deleteStatementBuilder == null) {
            deleteStatementBuilder = buildDeleteStatementBuilder();
        }
        return deleteStatementBuilder;
    }

    protected IBuilder<JoinStateObject, AbstractIdentificationVariableDeclarationStateObject> getJoinBuilder() {
        if (joinBuilder == null) {
            joinBuilder = buildJoinBuilder();
        }
        return joinBuilder;
    }

    protected LiteralVisitor getLiteralVisitor() {
        if (literalVisitor == null) {
            literalVisitor = buildLiteralVisitor();
        }
        return literalVisitor;
    }

    protected IBuilder<AbstractIdentificationVariableDeclarationStateObject, FromClauseStateObject> getRangeDeclarationBuilder() {
        if (rangeDeclarationBuilder == null) {
            rangeDeclarationBuilder = buildRangeDeclarationBuilder();
        }
        return rangeDeclarationBuilder;
    }

    protected IBuilder<StateObject, SelectClauseStateObject> getSelectItemBuilder() {
        if (selectItemBuilder == null) {
            selectItemBuilder = buildSelectItemBuilder();
        }
        return selectItemBuilder;
    }

    protected IBuilder<SelectStatementStateObject, JPQLQueryStateObject> getSelectStatementBuilder() {
        if (selectStatementBuilder == null) {
            selectStatementBuilder = buildSelectStatementBuilder();
        }
        return selectStatementBuilder;
    }

    protected IBuilder<AbstractIdentificationVariableDeclarationStateObject, SimpleFromClauseStateObject> getSimpleRangeDeclarationBuilder() {
        if (simpleRangeDeclarationBuilder == null) {
            simpleRangeDeclarationBuilder = buildSimpleRangeDeclarationBuilder();
        }
        return simpleRangeDeclarationBuilder;
    }

    protected IBuilder<SimpleSelectStatementStateObject, StateObject> getSimpleSelectStatementBuilder() {
        if (simpleSelectStatementBuilder == null) {
            simpleSelectStatementBuilder = buildSimpleSelectStatementBuilder();
        }
        return simpleSelectStatementBuilder;
    }

    /**
     * Returns the {@link StateObject} that was created based on the visited {@link Expression}.
     *
     * @return The {@link StateObject} that was created based on the visited {@link Expression}
     */
    public StateObject getStateObject() {
        return stateObject;
    }

    protected IBuilder<UpdateStatementStateObject, JPQLQueryStateObject> getUpdateStatementBuilder() {
        if (updateStatementBuilder == null) {
            updateStatementBuilder = buildUpdateStatementBuilder();
        }
        return updateStatementBuilder;
    }

    /**
     * Retrieves the "literal" from the given {@link Expression}. The literal to retrieve depends on
     * the given {@link LiteralType type}. The literal is basically a string value like an
     * identification variable name, an input parameter, a path expression, an abstract schema name,
     * etc.
     *
     * @param expression The {@link Expression} to visit
     * @param type The {@link LiteralType} helps to determine what to retrieve from the visited
     * {@link Expression}
     * @return A value from the given {@link Expression} or an empty string if the given {@link
     * Expression} and the {@link LiteralType} do not match
     */
    protected String literal(Expression expression, LiteralType type) {
        LiteralVisitor visitor = getLiteralVisitor();
        try {
            visitor.setType(type);
            visitor.literal = null;
            expression.accept(visitor);
            return visitor.literal;
        }
        finally {
            visitor.literal = null;
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(AbsExpression expression) {

        expression.getExpression().accept(this);

        AbsExpressionStateObject stateObject = new AbsExpressionStateObject(parent, this.stateObject);
        stateObject.setExpression(expression);
        this.stateObject = stateObject;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(AbstractSchemaName expression) {

        AbstractSchemaNameStateObject stateObject = new AbstractSchemaNameStateObject(
            parent,
            expression.getText()
        );

        stateObject.setExpression(expression);
        this.stateObject = stateObject;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(AdditionExpression expression) {

        expression.getLeftExpression().accept(this);
        StateObject leftStateObject = stateObject;

        expression.getRightExpression().accept(this);
        StateObject rightStateObject = stateObject;

        AdditionExpressionStateObject stateObject = new AdditionExpressionStateObject(
            parent,
            leftStateObject,
            rightStateObject
        );

        stateObject.setExpression(expression);
        this.stateObject = stateObject;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(AllOrAnyExpression expression) {

        expression.getExpression().accept(this);

        AllOrAnyExpressionStateObject stateObject = new AllOrAnyExpressionStateObject(
            parent,
            expression.getIdentifier(),
            this.stateObject
        );

        stateObject.setExpression(expression);
        this.stateObject = stateObject;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(AndExpression expression) {

        expression.getLeftExpression().accept(this);
        StateObject leftStateObject = stateObject;

        expression.getRightExpression().accept(this);
        StateObject rightStateObject = stateObject;

        AndExpressionStateObject stateObject = new AndExpressionStateObject(
            parent,
            leftStateObject,
            rightStateObject
        );

        stateObject.setExpression(expression);
        this.stateObject = stateObject;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(ArithmeticFactor expression) {

        expression.getExpression().accept(this);

        ArithmeticFactorStateObject stateObject = new ArithmeticFactorStateObject(
            parent,
            expression.isPositive(),
            this.stateObject
        );

        stateObject.setExpression(expression);
        this.stateObject = stateObject;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(AvgFunction expression) {

        expression.getExpression().accept(this);

        AvgFunctionStateObject stateObject = new AvgFunctionStateObject(
            parent,
            expression.hasDistinct(),
            this.stateObject
        );

        stateObject.setExpression(expression);
        this.stateObject = stateObject;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(BadExpression expression) {

        BadExpressionStateObject stateObject = new BadExpressionStateObject(
            parent,
            expression.getExpression().toActualText()
        );

        stateObject.setExpression(expression);
        this.stateObject = stateObject;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(BetweenExpression expression) {

        expression.getExpression().accept(this);
        StateObject betweenStateObject = stateObject;

        expression.getLowerBoundExpression().accept(this);
        StateObject lowerBoundStateObject = stateObject;

        expression.getUpperBoundExpression().accept(this);
        StateObject upperBoundStateObject = stateObject;

        BetweenExpressionStateObject stateObject = new BetweenExpressionStateObject(
            parent,
            betweenStateObject,
            expression.hasNot(),
            lowerBoundStateObject,
            upperBoundStateObject
        );

        stateObject.setExpression(expression);
        this.stateObject = stateObject;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(CaseExpression expression) {

        expression.getCaseOperand().accept(this);
        StateObject caseOperand = stateObject;

        expression.getElseExpression().accept(this);
        StateObject elseStateObject = stateObject;

        CaseExpressionStateObject caseExpressionStateObject = new CaseExpressionStateObject(
            parent,
            caseOperand,
            Collections.<WhenClauseStateObject>emptyList(),
            elseStateObject
        );

        whenClauseBuilder().buildStateObject(caseExpressionStateObject, expression);

        caseExpressionStateObject.setExpression(expression);
        this.stateObject = caseExpressionStateObject;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(CoalesceExpression expression) {

        CoalesceExpressionStateObject stateObject = new CoalesceExpressionStateObject(
            parent,
            buildChildren(expression.getExpression())
        );

        stateObject.setExpression(expression);
        this.stateObject = stateObject;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(CollectionExpression expression) {

        List<StateObject> stateObjects = buildChildren(expression);

        CollectionExpressionStateObject stateObject = new CollectionExpressionStateObject(
            parent,
            stateObjects
        );

        stateObject.setExpression(expression);
        this.stateObject = stateObject;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(CollectionMemberDeclaration expression) {
        // Not done here
        stateObject = null;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(CollectionMemberExpression expression) {

        expression.getEntityExpression().accept(this);
        StateObject entityExpression = stateObject;

        CollectionMemberExpressionStateObject stateObject = new CollectionMemberExpressionStateObject(
            parent,
            entityExpression,
            expression.hasNot(),
            expression.hasOf(),
            literal(expression.getCollectionValuedPathExpression(), LiteralType.PATH_EXPRESSION_ALL_PATH)
        );

        stateObject.setExpression(expression);
        this.stateObject = stateObject;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(CollectionValuedPathExpression expression) {

        CollectionValuedPathExpressionStateObject stateObject = new CollectionValuedPathExpressionStateObject(
            parent,
            expression.toActualText()
        );

        stateObject.setExpression(expression);
        this.stateObject = stateObject;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(ComparisonExpression expression) {

        expression.getLeftExpression().accept(this);
        StateObject leftStateObject = stateObject;

        expression.getRightExpression().accept(this);
        StateObject rightStateObject = stateObject;

        ComparisonExpressionStateObject stateObject = new ComparisonExpressionStateObject(
            parent,
            leftStateObject,
            expression.getComparisonOperator(),
            rightStateObject
        );

        stateObject.setExpression(expression);
        this.stateObject = stateObject;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(ConcatExpression expression) {

        ConcatExpressionStateObject stateObject = new ConcatExpressionStateObject(
            parent,
            buildChildren(expression.getExpression())
        );

        stateObject.setExpression(expression);
        this.stateObject = stateObject;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(ConstructorExpression expression) {

        ConstructorExpressionStateObject stateObject = new ConstructorExpressionStateObject(
            parent,
            expression.getClassName(),
            buildChildren(expression.getConstructorItems())
        );

        stateObject.setExpression(expression);
        this.stateObject = stateObject;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(CountFunction expression) {

        expression.getExpression().accept(this);

        CountFunctionStateObject stateObject = new CountFunctionStateObject(
            parent,
            expression.hasDistinct(),
            this.stateObject
        );

        stateObject.setExpression(expression);
        this.stateObject = stateObject;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(DateTime expression) {
        DateTimeStateObject stateObject = new DateTimeStateObject(parent, expression.getText());
        stateObject.setExpression(expression);
        this.stateObject = stateObject;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(DeleteClause expression) {
        // Done via DeleteStatementBuilder
        stateObject = null;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(DeleteStatement expression) {
        stateObject = getDeleteStatementBuilder().buildStateObject(parent, expression);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(DivisionExpression expression) {

        expression.getLeftExpression().accept(this);
        StateObject leftStateObject = stateObject;

        expression.getRightExpression().accept(this);
        StateObject rightStateObject = stateObject;

        DivisionExpressionStateObject stateObject = new DivisionExpressionStateObject(
            parent,
            leftStateObject,
            rightStateObject
        );

        stateObject.setExpression(expression);
        this.stateObject = stateObject;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(EmptyCollectionComparisonExpression expression) {

        EmptyCollectionComparisonExpressionStateObject stateObject = new EmptyCollectionComparisonExpressionStateObject(
            parent,
            expression.hasNot(),
            literal(expression.getExpression(), LiteralType.PATH_EXPRESSION_ALL_PATH)
        );

        stateObject.setExpression(expression);
        this.stateObject = stateObject;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(EntityTypeLiteral expression) {

        EntityTypeLiteralStateObject stateObject = new EntityTypeLiteralStateObject(
            parent,
            expression.getEntityTypeName()
        );

        stateObject.setExpression(expression);
        this.stateObject = stateObject;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(EntryExpression expression) {

        EntryExpressionStateObject stateObject = new EntryExpressionStateObject(
            parent,
            literal(expression.getExpression(), LiteralType.IDENTIFICATION_VARIABLE)
        );

        stateObject.setExpression(expression);
        this.stateObject = stateObject;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(ExistsExpression expression) {

        expression.getExpression().accept(this);

        ExistsExpressionStateObject stateObject = new ExistsExpressionStateObject(
            parent,
            expression.hasNot(),
            this.stateObject
        );

        stateObject.setExpression(expression);
        this.stateObject = stateObject;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final void visit(FromClause expression) {
        // Not done here
        stateObject = null;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(FunctionExpression expression) {

        FunctionExpressionStateObject stateObject = new FunctionExpressionStateObject(
            parent,
            expression.getIdentifier(),
            expression.getUnquotedFunctionName(),
            buildChildren(expression.getExpression())
        );

        stateObject.setExpression(expression);
        this.stateObject = stateObject;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final void visit(GroupByClause expression) {
        // Not done here
        stateObject = null;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(HavingClause expression) {
        expression.getConditionalExpression().accept(this);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(IdentificationVariable expression) {

        IdentificationVariableStateObject stateObject = new IdentificationVariableStateObject(
            parent,
            expression.getText()
        );

        stateObject.setVirtual(expression.isVirtual());
        stateObject.setExpression(expression);
        this.stateObject = stateObject;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final void visit(final IdentificationVariableDeclaration expression) {

        StateObjectVisitor visitor = new AbstractStateObjectVisitor() {
            @Override
            public void visit(FromClauseStateObject stateObject) {
                getRangeDeclarationBuilder().buildStateObject(stateObject, expression);
            }
            @Override
            public void visit(SimpleFromClauseStateObject stateObject) {
                getSimpleRangeDeclarationBuilder().buildStateObject(stateObject, expression);
            }
        };

        stateObject.accept(visitor);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(IndexExpression expression) {

        IndexExpressionStateObject stateObject = new IndexExpressionStateObject(
            parent,
            literal(expression.getExpression(), LiteralType.IDENTIFICATION_VARIABLE)
        );

        stateObject.setExpression(expression);
        this.stateObject = stateObject;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(InExpression expression) {

        expression.getExpression().accept(this);

        InExpressionStateObject stateObject = new InExpressionStateObject(
            parent,
            this.stateObject,
            expression.hasNot(),
            buildChildren(expression.getInItems())
        );

        stateObject.setSingleInputParameter(expression.isSingleInputParameter());
        stateObject.setExpression(expression);
        this.stateObject = stateObject;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(InputParameter expression) {

        InputParameterStateObject stateObject = new InputParameterStateObject(
            parent,
            expression.getParameter()
        );

        stateObject.setExpression(expression);
        this.stateObject = stateObject;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final void visit(Join expression) {
        stateObject = getJoinBuilder().buildStateObject(
            (IdentificationVariableDeclarationStateObject) stateObject,
            expression
        );
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(JPQLExpression expression) {

        parent = new JPQLQueryStateObject(jpqlQueryBuilder, managedTypeProvider);
        parent.setExpression(expression);

        expression.getQueryStatement().accept(this);
        parent.setQueryStatement(stateObject);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(KeyExpression expression) {

        KeyExpressionStateObject stateObject = new KeyExpressionStateObject(
            parent,
            literal(expression.getExpression(), LiteralType.IDENTIFICATION_VARIABLE)
        );

        stateObject.setExpression(expression);
        this.stateObject = stateObject;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(KeywordExpression expression) {

        KeywordExpressionStateObject stateObject = new KeywordExpressionStateObject(
            parent,
            expression.getText()
        );

        stateObject.setExpression(expression);
        this.stateObject = stateObject;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(LengthExpression expression) {

        expression.getExpression().accept(this);

        LengthExpressionStateObject stateObject = new LengthExpressionStateObject(
            parent,
            this.stateObject
        );

        stateObject.setExpression(expression);
        this.stateObject = stateObject;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(LikeExpression expression) {

        expression.getStringExpression().accept(this);
        StateObject stringStateObject = stateObject;

        expression.getPatternValue().accept(this);
        StateObject patternValue = stateObject;

        LikeExpressionStateObject stateObject = new LikeExpressionStateObject(
            parent,
            stringStateObject,
            expression.hasNot(),
            patternValue,
            literal(expression.getEscapeCharacter(), LiteralType.STRING_LITERAL)
        );

        stateObject.setExpression(expression);
        this.stateObject = stateObject;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(LocateExpression expression) {

        expression.getFirstExpression().accept(this);
        StateObject firstStateObject = stateObject;

        expression.getSecondExpression().accept(this);
        StateObject secondStateObject = stateObject;

        expression.getThirdExpression().accept(this);
        StateObject thirdStateObject = stateObject;

        LocateExpressionStateObject stateObject = new LocateExpressionStateObject(
            parent,
            firstStateObject,
            secondStateObject,
            thirdStateObject
        );

        stateObject.setExpression(expression);
        this.stateObject = stateObject;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(LowerExpression expression) {

        expression.getExpression().accept(this);

        LowerExpressionStateObject stateObject = new LowerExpressionStateObject(
            parent,
            this.stateObject
        );

        stateObject.setExpression(expression);
        this.stateObject = stateObject;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(MaxFunction expression) {

        expression.getExpression().accept(this);

        MaxFunctionStateObject stateObject = new MaxFunctionStateObject(
            parent,
            expression.hasDistinct(),
            this.stateObject
        );

        stateObject.setExpression(expression);
        this.stateObject = stateObject;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(MinFunction expression) {

        expression.getExpression().accept(this);

        MinFunctionStateObject stateObject = new MinFunctionStateObject(
            parent,
            expression.hasDistinct(),
            this.stateObject
        );

        stateObject.setExpression(expression);
        this.stateObject = stateObject;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(ModExpression expression) {

        expression.getFirstExpression().accept(this);
        StateObject firstStateObject = stateObject;

        expression.getSecondExpression().accept(this);
        StateObject secondStateObject = stateObject;

        ModExpressionStateObject stateObject = new ModExpressionStateObject(
            parent,
            firstStateObject,
            secondStateObject
        );

        stateObject.setExpression(expression);
        this.stateObject = stateObject;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(MultiplicationExpression expression) {

        expression.getLeftExpression().accept(this);
        StateObject leftStateObject = stateObject;

        expression.getRightExpression().accept(this);
        StateObject rightStateObject = stateObject;

        MultiplicationExpressionStateObject stateObject = new MultiplicationExpressionStateObject(
            parent,
            leftStateObject,
            rightStateObject
        );

        stateObject.setExpression(expression);
        this.stateObject = stateObject;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(NotExpression expression) {

        expression.getExpression().accept(this);

        NotExpressionStateObject stateObject = new NotExpressionStateObject(
            parent,
            this.stateObject
        );

        stateObject.setExpression(expression);
        this.stateObject = stateObject;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(NullComparisonExpression expression) {

        expression.getExpression().accept(this);

        NullComparisonExpressionStateObject stateObject = new NullComparisonExpressionStateObject(
            parent,
            expression.hasNot(),
            this.stateObject
        );

        stateObject.setExpression(expression);
        this.stateObject = stateObject;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(NullExpression expression) {
        stateObject = null;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(NullIfExpression expression) {

        expression.getFirstExpression().accept(this);
        StateObject firstStateObject = stateObject;

        expression.getSecondExpression().accept(this);
        StateObject secondStateObject = stateObject;

        NullIfExpressionStateObject stateObject = new NullIfExpressionStateObject(
            parent,
            firstStateObject,
            secondStateObject
        );

        stateObject.setExpression(expression);
        this.stateObject = stateObject;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(NumericLiteral expression) {

        NumericLiteralStateObject stateObject = new NumericLiteralStateObject(
            parent,
            expression.getText()
        );

        stateObject.setExpression(expression);
        this.stateObject = stateObject;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(ObjectExpression expression) {

        ObjectExpressionStateObject stateObject = new ObjectExpressionStateObject(
            parent,
            literal(expression.getExpression(), LiteralType.IDENTIFICATION_VARIABLE)
        );

        stateObject.setExpression(expression);
        this.stateObject = stateObject;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(OnClause expression) {
        expression.getConditionalExpression().accept(this);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final void visit(OrderByClause expression) {
        // Not done here
        stateObject = null;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final void visit(OrderByItem expression) {

        OrderByClauseStateObject orderByClause = (OrderByClauseStateObject) stateObject;

        OrderByItemStateObject orderByItem = orderByClause.addItem(expression.getOrdering());
        orderByItem.setExpression(expression);

        expression.getExpression().accept(this);
        orderByItem.setStateObject(stateObject);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(OrExpression expression) {

        expression.getLeftExpression().accept(this);
        StateObject leftStateObject = stateObject;

        expression.getRightExpression().accept(this);
        StateObject rightStateObject = stateObject;

        OrExpressionStateObject stateObject = new OrExpressionStateObject(
            parent,
            leftStateObject,
            rightStateObject
        );

        stateObject.setExpression(expression);
        this.stateObject = stateObject;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final void visit(RangeVariableDeclaration expression) {
        // Not done here
        stateObject = null;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final void visit(final ResultVariable expression) {

        StateObjectVisitor visitor = new AbstractStateObjectVisitor() {
            @Override
            public void visit(SelectClauseStateObject stateObject) {
                BasicStateObjectBuilder.this.stateObject = getSelectItemBuilder().buildStateObject(
                    stateObject,
                    expression
                );
            }
        };

        stateObject.accept(visitor);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final void visit(SelectClause expression) {
        // Not done here
        stateObject = null;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(SelectStatement expression) {
        stateObject = getSelectStatementBuilder().buildStateObject(parent, expression);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final void visit(SimpleFromClause expression) {
        // Done via SimpleSelectStatementBuilder
        stateObject = null;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final void visit(SimpleSelectClause expression) {
        // Done via SimpleSelectStatementBuilder
        stateObject = null;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(SimpleSelectStatement expression) {
        stateObject = getSimpleSelectStatementBuilder().buildStateObject(parent, expression);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(SizeExpression expression) {

        expression.getExpression().accept(this);

        SizeExpressionStateObject stateObject = new SizeExpressionStateObject(
            parent,
            this.stateObject
        );

        stateObject.setExpression(expression);
        this.stateObject = stateObject;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(SqrtExpression expression) {

        expression.getExpression().accept(this);

        SqrtExpressionStateObject stateObject = new SqrtExpressionStateObject(
            parent,
            this.stateObject
        );

        stateObject.setExpression(expression);
        this.stateObject = stateObject;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(StateFieldPathExpression expression) {

        StateFieldPathExpressionStateObject stateObject = new StateFieldPathExpressionStateObject(
            parent,
            expression.toActualText()
        );

        stateObject.setExpression(expression);
        this.stateObject = stateObject;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(StringLiteral expression) {

        StringLiteralStateObject stateObject = new StringLiteralStateObject(
            parent,
            expression.getText()
        );

        stateObject.setExpression(expression);
        this.stateObject = stateObject;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(SubExpression expression) {

        expression.getExpression().accept(this);

        SubExpressionStateObject stateObject = new SubExpressionStateObject(
            parent,
            this.stateObject
        );

        stateObject.setExpression(expression);
        this.stateObject = stateObject;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(SubstringExpression expression) {

        expression.getFirstExpression().accept(this);
        StateObject firstExpression = stateObject;

        expression.getSecondExpression().accept(this);
        StateObject secondExpression = stateObject;

        expression.getThirdExpression().accept(this);
        StateObject thirdExpression = stateObject;

        SubstringExpressionStateObject stateObject = new SubstringExpressionStateObject(
            parent,
            firstExpression,
            secondExpression,
            thirdExpression
        );

        stateObject.setExpression(expression);
        this.stateObject = stateObject;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(SubtractionExpression expression) {

        expression.getLeftExpression().accept(this);
        StateObject leftStateObject = stateObject;

        expression.getRightExpression().accept(this);
        StateObject rightStateObject = stateObject;

        SubtractionExpressionStateObject stateObject = new SubtractionExpressionStateObject(
            parent,
            leftStateObject,
            rightStateObject
        );

        stateObject.setExpression(expression);
        this.stateObject = stateObject;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(SumFunction expression) {

        expression.getExpression().accept(this);

        SumFunctionStateObject stateObject = new SumFunctionStateObject(
            parent,
            expression.hasDistinct(),
            this.stateObject
        );

        stateObject.setExpression(expression);
        this.stateObject = stateObject;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(TreatExpression expression) {
        // Done by JoinBuilder
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(TrimExpression expression) {

        expression.getExpression().accept(this);
        StateObject trimStateObject = stateObject;

        expression.getTrimCharacter().accept(this);
        StateObject trimCharacter = stateObject;

        TrimExpressionStateObject stateObject = new TrimExpressionStateObject(
            parent,
            expression.getSpecification(),
            trimCharacter,
            trimStateObject
        );

        stateObject.setExpression(expression);
        this.stateObject = stateObject;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(TypeExpression expression) {

        expression.getExpression().accept(this);

        TypeExpressionStateObject stateObject = new TypeExpressionStateObject(
            parent,
            this.stateObject
        );

        stateObject.setExpression(expression);
        this.stateObject = stateObject;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(UnknownExpression expression) {

        UnknownExpressionStateObject stateObject = new UnknownExpressionStateObject(
            parent,
            expression.getText()
        );

        stateObject.setExpression(expression);
        this.stateObject = stateObject;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final void visit(UpdateClause expression) {
        // Done via UpdateStatementBuilder
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final void visit(UpdateItem expression) {
        // Done via UpdateStatementBuilder
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(UpdateStatement expression) {
        stateObject = getUpdateStatementBuilder().buildStateObject(parent, expression);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(UpperExpression expression) {

        expression.getExpression().accept(this);

        UpperExpressionStateObject stateObject = new UpperExpressionStateObject(
            parent,
            this.stateObject
        );

        stateObject.setExpression(expression);
        this.stateObject = stateObject;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(ValueExpression expression) {

        ValueExpressionStateObject stateObject = new ValueExpressionStateObject(
            parent,
            literal(expression.getExpression(), LiteralType.IDENTIFICATION_VARIABLE)
        );

        stateObject.setExpression(expression);
        this.stateObject = stateObject;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final void visit(WhenClause expression) {
        // Done throw WhenClauseBuilder
        stateObject = null;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(WhereClause expression) {
        expression.getConditionalExpression().accept(this);
    }

    /**
     * Returns the {@link IBuilder} that is responsible to visit each {@link WhenClause} and to
     * create the corresponding {@link WhenClauseStateObject}. The new state objects have to be added
     * to {@link CaseExpressionStateObject} by the builder.
     *
     * @return The builder of the <code><b>WHEN</b></code> clauses
     */
    protected IBuilder<CaseExpressionStateObject, CaseExpressionStateObject> whenClauseBuilder() {
        if (whenClauseBuilder == null) {
            whenClauseBuilder = buildWhenClauseBuilder();
        }
        return whenClauseBuilder;
    }

    /**
     * This builder is responsible to create a new identification variable declaration and to add it
     * to the state object representing the <code><b>FROM</b></code> clause.
     */
    protected abstract class AbstractRangeDeclarationBuilder<S extends AbstractFromClauseStateObject>
              extends AbstractTraverseChildrenVisitor
              implements IBuilder<AbstractIdentificationVariableDeclarationStateObject, S> {

        /**
         * The concrete instance of {@link AbstractFromClauseStateObject} where the new identification
         * variable declaration is added.
         */
        protected S parent;

        /**
         * The concrete instance of {@link IdentificationVariableDeclarationStateObject}
         * that represents the visited {@link IdentificationVariableDeclaration}.
         */
        protected AbstractIdentificationVariableDeclarationStateObject stateObject;

        /**
         * Creates the concrete instance of an {@link AbstractIdentificationVariableDeclarationStateObject}
         * for the given {@link IdentificationVariableDeclaration}.
         *
         * @param expression The {@link IdentificationVariableDeclaration} to convert into a
         * {@link StateObject}
         * @return A new {@link StateObject} representing an identification variable declaration
         */
        protected abstract AbstractIdentificationVariableDeclarationStateObject addRangeDeclaration(IdentificationVariableDeclaration expression);

        /**
         * {@inheritDoc}
         */
        @Override
        public AbstractIdentificationVariableDeclarationStateObject buildStateObject(S parent, Expression expression) {
            try {
                this.parent = parent;
                expression.accept(this);
                return stateObject;
            }
            finally {
                this.parent      = null;
                this.stateObject = null;
            }
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void visit(AbstractSchemaName expression) {
            stateObject.setRootPath(expression.getText());
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void visit(IdentificationVariable expression) {

            stateObject.setIdentificationVariable(expression.getText());

            IdentificationVariableStateObject variable = stateObject.getRangeVariableDeclaration().getIdentificationVariableStateObject();
            variable.setVirtual(expression.isVirtual());
            variable.setExpression(expression);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void visit(IdentificationVariableDeclaration expression) {

            stateObject = addRangeDeclaration(expression);
            stateObject.setExpression(expression);

            getJoinBuilder().buildStateObject(stateObject, expression.getJoins());

            expression.getRangeVariableDeclaration().accept(this);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void visit(RangeVariableDeclaration expression) {
            AbstractRangeVariableDeclarationStateObject declaration = stateObject.getRangeVariableDeclaration();
            declaration.setAs(expression.hasAs());
            declaration.setExpression(expression);
            super.visit(expression);
        }
    }

    /**
     * The abstract definition of the builder that is responsible to create the {@link StateObject}
     * representation of the <code><b>SELECT</b></code> statement.
     */
    protected abstract class AbstractSelectStatementBuilder<T extends AbstractSelectStatementStateObject, P extends StateObject>
                       extends    AbstractTraverseChildrenVisitor
                       implements IBuilder<T, P> {

        /**
         * The parent of the <code><b>SELECT</b></code> statement.
         */
        protected P parent;

        /**
         * The concrete class of {@link AbstractSelectStatementStateObject}.
         */
        protected T stateObject;

        /**
         * {@inheritDoc}
         */
        @Override
        public T buildStateObject(P parent, Expression expression) {

            try {
                this.parent = parent;
                expression.accept(this);
                return stateObject;
            }
            finally {
                this.parent = null;
            }
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void visit(CollectionMemberDeclaration expression) {
            getCollectionDeclarationBuilder().buildStateObject(stateObject.getFromClause(), expression);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void visit(GroupByClause expression) {
            GroupByClauseStateObject groupByClause = stateObject.addGroupByClause();
            groupByClause.setExpression(expression);
            groupByClause.addItems(buildChildren(expression.getGroupByItems()));
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void visit(HavingClause expression) {
            HavingClauseStateObject havingClause = stateObject.addHavingClause();
            havingClause.setConditional(buildStateObjectImp(expression));
            havingClause.setExpression(expression);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public abstract void visit(IdentificationVariableDeclaration expression);

        /**
         * {@inheritDoc}
         */
        @Override
        public void visit(WhereClause expression) {
            WhereClauseStateObject whereClause = stateObject.addWhereClause();
            whereClause.setConditional(buildStateObjectImp(expression));
            whereClause.setExpression(expression);
        }
    }

    protected static class CollectionExpressionVisitor extends AnonymousExpressionVisitor {

        List<Expression> children;

        CollectionExpressionVisitor() {
            super();
            reset();
        }

        void reset() {
            children = new ArrayList<Expression>();
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void visit(CollectionExpression expression) {
            CollectionTools.addAll(children, expression.children());
        }

        /**
         * {@inheritDoc}
         */
        @Override
        protected void visit(Expression expression) {
            children.add(expression);
        }
    }

    protected static class CollectionMemberDeclarationBuilder extends AbstractTraverseChildrenVisitor
                                                       implements IBuilder<CollectionMemberDeclarationStateObject, AbstractFromClauseStateObject> {

        protected AbstractFromClauseStateObject parent;
        protected CollectionMemberDeclarationStateObject stateObject;

        /**
         * {@inheritDoc}
         */
        @Override
        public CollectionMemberDeclarationStateObject buildStateObject(AbstractFromClauseStateObject parent,
                                                                       Expression expression) {

            try {
                this.parent = parent;
                expression.accept(this);
                return stateObject;
            }
            finally {
                this.parent      = null;
                this.stateObject = null;
            }
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void visit(CollectionMemberDeclaration expression) {
            stateObject = parent.addCollectionDeclaration();
            stateObject.setAs(expression.hasAs());
            stateObject.setExpression(expression);
            stateObject.setDerived(expression.isDerived());
            super.visit(expression);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void visit(CollectionValuedPathExpression expression) {
            CollectionValuedPathExpressionStateObject path = stateObject.getCollectionValuedPath();
            path.setPaths(expression.paths().iterator());
            path.setExpression(expression);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void visit(IdentificationVariable expression) {
            IdentificationVariableStateObject variable = stateObject.getIdentificationVariable();
            variable.setExpression(expression);
            variable.setText(expression.getText());
            variable.setVirtual(expression.isVirtual());
        }
    }

    /**
     * This builder is responsible to create the {@link StateObject} representation of the
     * <code><b>DELETE</b></code> query statement.
     */
    protected class DeleteStatementBuilder extends AbstractTraverseChildrenVisitor
                                           implements IBuilder<DeleteStatementStateObject, JPQLQueryStateObject> {

        protected JPQLQueryStateObject parent;
        protected DeleteStatementStateObject stateObject;

        /**
         * {@inheritDoc}
         */
        @Override
        public DeleteStatementStateObject buildStateObject(JPQLQueryStateObject parent,
                                                           Expression expression) {

            try {
                this.parent = parent;
                expression.accept(this);
                return stateObject;
            }
            finally {
                this.parent = null;
            }
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void visit(AbstractSchemaName expression) {
            AbstractSchemaNameStateObject entityStateObject = stateObject.getAbstractSchemaNameStateObject();
            entityStateObject.setText(expression.getText());
            entityStateObject.setExpression(expression);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void visit(DeleteClause expression) {
            stateObject.getModifyClause().setExpression(expression);
            super.visit(expression);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void visit(DeleteStatement expression) {
            stateObject = parent.addDeleteStatement();
            stateObject.setExpression(expression);
            super.visit(expression);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void visit(IdentificationVariable expression) {
            IdentificationVariableStateObject variable = stateObject.getIdentificationVariableStateObject();
            variable.setExpression(expression);
            variable.setText(expression.getText());
            variable.setVirtual(expression.isVirtual());
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void visit(RangeVariableDeclaration expression) {
            stateObject.getModifyClause().getRangeVariableDeclaration().setExpression(expression);
            super.visit(expression);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void visit(WhereClause expression) {

            WhereClauseStateObject whereClause = stateObject.addWhereClause();
            whereClause.setExpression(expression);

            expression.getConditionalExpression().accept(BasicStateObjectBuilder.this);
            StateObject conditionalStateObject = BasicStateObjectBuilder.this.stateObject;

            whereClause.setConditional(conditionalStateObject);
        }
    }

    protected class JoinBuilder extends AbstractExpressionVisitor
                                implements IBuilder<JoinStateObject, AbstractIdentificationVariableDeclarationStateObject> {

        protected AbstractIdentificationVariableDeclarationStateObject parent;
        protected JoinStateObject stateObject;

        /**
         * {@inheritDoc}
         */
        @Override
        public JoinStateObject buildStateObject(AbstractIdentificationVariableDeclarationStateObject parent,
                                                Expression expression) {

            try {
                this.parent = parent;
                expression.accept(this);
                return stateObject;
            }
            finally {
                this.parent      = null;
                this.stateObject = null;
            }
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void visit(CollectionExpression expression) {
            expression.acceptChildren(this);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void visit(IdentificationVariableDeclaration expression) {
            expression.getRangeVariableDeclaration().accept(this);
            expression.getJoins().accept(this);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void visit(Join expression) {

            stateObject = parent.addJoin(
                expression.getIdentifier(),
                literal(expression.getJoinAssociationPath(),    LiteralType.PATH_EXPRESSION_ALL_PATH),
                literal(expression.getIdentificationVariable(), LiteralType.IDENTIFICATION_VARIABLE)
            );

            stateObject.setAs(expression.hasAs());
            stateObject.setExpression(expression);

            expression.getJoinAssociationPath().accept(this);
            expression.getIdentificationVariable().accept(this);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void visit(TreatExpression expression) {

            TreatExpressionStateObject treatStateObject = new TreatExpressionStateObject(
                stateObject,
                expression.hasAs(),
                literal(expression.getEntityType(), LiteralType.ENTITY_TYPE)
            );

            treatStateObject.setExpression(expression);
            stateObject.getJoinAssociationPathStateObject().decorate(treatStateObject);
            expression.getCollectionValuedPathExpression().accept(this);
        }
    }

    /**
     * This builder is responsible to create a new identification variable declaration and to add it
     * to the state object representing the <code><b>FROM</b></code> clause of the top-level query.
     */
    protected class RangeDeclarationBuilder extends AbstractRangeDeclarationBuilder<FromClauseStateObject> {

        /**
         * {@inheritDoc}
         */
        @Override
        protected AbstractIdentificationVariableDeclarationStateObject addRangeDeclaration(IdentificationVariableDeclaration expression) {
            return parent.addRangeDeclaration();
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void visit(AbstractSchemaName expression) {
            super.visit(expression);
            IdentificationVariableDeclarationStateObject stateObject = (IdentificationVariableDeclarationStateObject) this.stateObject;
            stateObject.getRootStateObject().setExpression(expression);
        }
    }

    /**
     * This builder is responsible to create the items owned by the top-level
     * <code><b>SELECT</b></code> clause.
     */
    protected class SelectItemBuilder extends AnonymousExpressionVisitor
                                      implements IBuilder<StateObject, SelectClauseStateObject> {

        protected SelectClauseStateObject parent;
        protected StateObject stateObject;

        /**
         * {@inheritDoc}
         */
        @Override
        public StateObject buildStateObject(SelectClauseStateObject parent, Expression expression) {

            try {
                this.parent = parent;
                expression.accept(this);
                return this.stateObject;
            }
            finally {
                this.parent      = null;
                this.stateObject = null;
            }
        }

        /**
         * {@inheritDoc}
         */
        @Override
        protected void visit(Expression expression) {
            expression.accept(BasicStateObjectBuilder.this);
            stateObject = BasicStateObjectBuilder.this.stateObject;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void visit(ResultVariable expression) {

            expression.getSelectExpression().accept(BasicStateObjectBuilder.this);

            ResultVariableStateObject stateObject = new ResultVariableStateObject(
                parent,
                BasicStateObjectBuilder.this.stateObject,
                expression.hasAs(),
                literal(expression.getResultVariable(), LiteralType.RESULT_VARIABLE)
            );

            stateObject.setExpression(expression);
            this.stateObject = stateObject;
        }
    }

    /**
     * This builder is responsible to create the {@link StateObject} representation of the
     * <code><b>SELECT</b></code> query statement.
     */
    protected class SelectStatementBuilder extends AbstractSelectStatementBuilder<SelectStatementStateObject, JPQLQueryStateObject> {

        /**
         * {@inheritDoc}
         */
        @Override
        public void visit(FromClause expression) {

            stateObject.getFromClause().setExpression(expression);

            for (Expression child : children(expression.getDeclaration())) {
                child.accept(this);
            }
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void visit(IdentificationVariableDeclaration expression) {
            getRangeDeclarationBuilder().buildStateObject(stateObject.getFromClause(), expression);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void visit(OrderByClause expression) {

            OrderByClauseStateObject orderByClause = stateObject.addOrderByClause();
            orderByClause.setExpression(expression);

            super.visit(expression);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void visit(OrderByItem expression) {

            OrderByClauseStateObject orderByClause = stateObject.getOrderByClause();

            OrderByItemStateObject orderByItem = orderByClause.addItem(expression.getOrdering());
            orderByItem.setExpression(expression);

            expression.getExpression().accept(BasicStateObjectBuilder.this);
            orderByItem.setStateObject(BasicStateObjectBuilder.this.stateObject);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void visit(SelectClause expression) {

            SelectClauseStateObject selectClause = stateObject.getSelectClause();
            selectClause.setExpression(expression);
            selectClause.setDistinct(expression.hasDistinct());

            for (Expression child : children(expression.getSelectExpression())) {

                StateObject stateObject = getSelectItemBuilder().buildStateObject(selectClause, child);

                if (stateObject != null) {
                    selectClause.addItem(stateObject);
                }
            }
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void visit(SelectStatement expression) {

            SelectStatementStateObject stateObject = parent.addSelectStatement();
            stateObject.setExpression(expression);
            this.stateObject = stateObject;

            super.visit(expression);
        }
    }

    /**
     * This builder is responsible to create a new identification variable declaration and to add it
     * to the state object representing the <code><b>FROM</b></code> clause of a subquery.
     */
    protected class SimpleRangeDeclarationBuilder extends AbstractRangeDeclarationBuilder<SimpleFromClauseStateObject> {

        /**
         * {@inheritDoc}
         */
        @Override
        protected AbstractIdentificationVariableDeclarationStateObject addRangeDeclaration(IdentificationVariableDeclaration expression) {

            String root = expression.getRangeVariableDeclaration().toActualText();
            int index = root.indexOf(AbstractExpression.SPACE);

            if (index > 0) {
                root = root.substring(0, index);
            }

            if (root.indexOf(AbstractExpression.DOT) > 0) {
                return parent.addDerivedPathDeclaration();
            }

            return parent.addRangeDeclaration();
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void visit(CollectionValuedPathExpression expression) {
            DerivedPathIdentificationVariableDeclarationStateObject stateObject = (DerivedPathIdentificationVariableDeclarationStateObject) this.stateObject;
            stateObject.setRootPath(expression.toActualText());
            stateObject.getRootStateObject().setExpression(expression);
        }
    }

    /**
     * This builder is responsible to create the {@link StateObject} representation of the
     * <code><b>SELECT</b></code> subquery.
     */
    protected class SimpleSelectStatementBuilder extends AbstractSelectStatementBuilder<SimpleSelectStatementStateObject, StateObject> {

        /**
         * {@inheritDoc}
         */
        @Override
        public void visit(IdentificationVariableDeclaration expression) {
            getSimpleRangeDeclarationBuilder().buildStateObject(stateObject.getFromClause(), expression);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void visit(SimpleFromClause expression) {

            stateObject.getFromClause().setExpression(expression);

            for (Expression child : children(expression.getDeclaration())) {
                child.accept(this);
            }
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void visit(SimpleSelectClause expression) {

            SimpleSelectClauseStateObject selectClause = stateObject.getSelectClause();
            selectClause.setDistinct(expression.hasDistinct());
            selectClause.setExpression(expression);

            List<StateObject> children = buildChildren(expression.getSelectExpression());

            if (!children.isEmpty()) {
                selectClause.setSelectItem(children.get(0));
            }
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void visit(SimpleSelectStatement expression) {

            stateObject = new SimpleSelectStatementStateObject(parent);
            stateObject.setExpression(expression);
            stateObject.setParent(parent);

            super.visit(expression);
        }
    }

    /**
     * This builder is responsible to create the {@link StateObject} representation of the
     * <code><b>UPDATE</b></code> query statement.
     */
    protected class UpdateStatementBuilder extends AbstractTraverseChildrenVisitor
                                           implements IBuilder<UpdateStatementStateObject, JPQLQueryStateObject> {

        protected JPQLQueryStateObject parent;
        protected UpdateStatementStateObject stateObject;
        protected UpdateItemStateObject updateItem;

        /**
         * {@inheritDoc}
         */
        @Override
        public UpdateStatementStateObject buildStateObject(JPQLQueryStateObject parent,
                                                           Expression expression) {

            try {
                this.parent = parent;
                expression.accept(this);
                return stateObject;
            }
            finally {
                this.parent      = null;
                this.stateObject = null;
            }
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void visit(AbstractSchemaName expression) {
            stateObject.setEntityName(expression.getText());
            stateObject.getModifyClause().getAbstractSchemaNameStateObject().setExpression(expression);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void visit(IdentificationVariable expression) {

            if (updateItem == null) {

                IdentificationVariableStateObject variable = stateObject.getIdentificationVariableStateObject();

                variable.setText(expression.getText());
                variable.setVirtual(expression.isVirtual());
                variable.setExpression(expression);
            }
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void visit(RangeVariableDeclaration expression) {
            stateObject.getModifyClause().getRangeVariableDeclaration().setExpression(expression);
            super.visit(expression);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void visit(StateFieldPathExpression expression) {
            updateItem.getStateFieldPath().setPaths(expression.paths().iterator());
            updateItem.getStateFieldPath().setExpression(expression);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void visit(UpdateClause expression) {
            stateObject.getModifyClause().setExpression(expression);
            super.visit(expression);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void visit(UpdateItem expression) {

            UpdateClauseStateObject updateClause = stateObject.getModifyClause();
            String identificationVariable = updateClause.getIdentificationVariable();

            updateItem = updateClause.addItem();
            updateItem.setExpression(expression);

            try {
                // Retrieve the state field path expression
                String path = literal(
                    expression.getStateFieldPathExpression(),
                    LiteralType.PATH_EXPRESSION_ALL_PATH
                );

                if (!path.startsWith(identificationVariable + ".")) {
                    updateItem.setPath(identificationVariable + "." + path);
                }
                else {
                    updateItem.setPath(path);
                }

                // Set the virtual identification variable
                if (!updateClause.isIdentificationVariableDefined()) {
                    updateItem.setVirtualIdentificationVariable(updateClause.getIdentificationVariable());
                }

                // Create the new value
                StateObject stateObject = buildStateObjectImp(expression.getNewValue());
                updateItem.setNewValue(stateObject);
            }
            finally {
                updateItem = null;
            }
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void visit(UpdateStatement expression) {

            UpdateStatementStateObject stateObject = parent.addUpdateStatement();
            stateObject.setExpression(expression);
            this.stateObject = stateObject;

            super.visit(expression);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void visit(WhereClause expression) {

            WhereClauseStateObject whereClause = stateObject.addWhereClause();
            whereClause.setExpression(expression);

            StateObject stateObject = buildStateObjectImp(expression.getConditionalExpression());
            whereClause.setConditional(stateObject);
        }
    }

    /**
     * This builder is responsible to create the <code><b>WHEN</b></code> clauses for a
     * <code><b>CASE</b></code> expression.
     */
    protected class WhenClauseBuilder extends AbstractTraverseChildrenVisitor
                                      implements IBuilder<CaseExpressionStateObject, CaseExpressionStateObject> {

        /**
         * The {@link CaseExpressionStateObject} for which its {@link WhenClauseStateObject
         * WhenClauseStateObjects} are created by this builder.
         */
        private CaseExpressionStateObject parent;

        /**
         * {@inheritDoc}
         */
        @Override
        public CaseExpressionStateObject buildStateObject(CaseExpressionStateObject parent,
                                                          Expression expression) {

            try {
                this.parent = parent;
                expression.accept(this);
                return parent;
            }
            finally {
                this.parent = null;
            }
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void visit(WhenClause expression) {

            StateObject whenStateObject = buildStateObjectImp(expression.getWhenExpression());
            StateObject thenStateObject = buildStateObjectImp(expression.getThenExpression());

            WhenClauseStateObject stateObject = parent.addWhenClause(whenStateObject, thenStateObject);
            stateObject.setExpression(expression);
        }
    }
}
