/*******************************************************************************
 * Copyright (c) 2006, 2014 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 v1.0 and Eclipse Distribution License v. 1.0
 * which accompanies this distribution.
 * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
 * and the Eclipse Distribution License is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * Contributors:
 *     Oracle - initial API and implementation
 *
 ******************************************************************************/
package org.eclipse.persistence.jpa.jpql.parser;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import org.eclipse.persistence.jpa.jpql.ExpressionTools;
import org.eclipse.persistence.jpa.jpql.JPAVersion;
import org.eclipse.persistence.jpa.jpql.WordParser;
import org.eclipse.persistence.jpa.jpql.WordParser.WordType;
import org.eclipse.persistence.jpa.jpql.utility.iterable.ListIterable;
import org.eclipse.persistence.jpa.jpql.utility.iterable.SnapshotCloneListIterable;

/**
 * This is the abstract definition of all the parts used to create the tree hierarchy representing
 * the parsed JPQL query.
 *
 * @see ExpressionFactory
 * @see JPQLGrammar
 *
 * @version 2.6
 * @since 2.3
 * @author Pascal Filion
 */
@SuppressWarnings("nls")
public abstract class AbstractExpression implements Expression {

	/**
	 * The string representation of this {@link AbstractExpression} (which includes its children).
	 * The string includes characters that are considered virtual, i.e. that was parsed when the
	 * query is incomplete and is needed for functionality like content assist.
	 *
	 * @see #toActualText()
	 */
	private String actualText;

	/**
	 * The children of this {@link AbstractExpression}.
	 *
	 * @see #children()
	 */
	private List<Expression> children;

	/**
	 * The position of this {@link AbstractExpression} in relation to its parent hierarchy by
	 * calculating the length of the string representation of what comes before.
	 */
	private int offset;

	/**
	 * The string representation of this {@link AbstractExpression}.
	 *
	 * @see #orderedChildren()
	 */
	private List<Expression> orderedChildren;

	/**
	 * The parent of this {@link AbstractExpression} or <code>null</code> if this object is {@link
	 * JPQLExpression} - the root of the parsed tree hierarchy.
	 */
	private AbstractExpression parent;

	/**
	 * The string representation of this {@link AbstractExpression} (which includes its children).
	 * The string does not include characters that are considered virtual, i.e. that was parsed when
	 * the query is incomplete.
	 *
	 * @see #toParsedText()
	 */
	private String parsedText;

	/**
	 * This attribute can be used to store the {@link AbstractExpression}'s JPQL identifier or a literal.
	 */
	private String text;

	/**
	 * The constant for ','.
	 */
	public static final char COMMA = ',';

	/**
	 * The constant for '.'.
	 */
	public static final char DOT = '.';

	/**
	 * The constant for '"'.
	 */
	public static final char DOUBLE_QUOTE = '\"';

	/**
	 * The constant for '{'.
	 */
	public static final char LEFT_CURLY_BRACKET = '{';

	/**
	 * The constant for '('.
	 */
	public static final char LEFT_PARENTHESIS = '(';

	/**
	 * The constant for a character that is not defined.
	 */
	public static final char NOT_DEFINED = '\0';

	/**
	 * The constant for '}'.
	 */
	public static final char RIGHT_CURLY_BRACKET = '}';

	/**
	 * The constant for ')'.
	 */
	public static final char RIGHT_PARENTHESIS = ')';

	/**
	 * The constant for '''.
	 */
	public static final char SINGLE_QUOTE = '\'';

	/**
	 * The constant for ' '.
	 */
	public static final char SPACE = ' ';

	/**
	 * The constant for '_'.
	 */
	public static final char UNDERSCORE = '_';

	/**
	 * Creates a new <code>AbstractExpression</code>.
	 *
	 * @param parent The parent of this expression
	 */
	protected AbstractExpression(AbstractExpression parent) {
		this(parent, ExpressionTools.EMPTY_STRING);
	}

	/**
	 * Creates a new <code>AbstractExpression</code>.
	 *
	 * @param parent The parent of this expression
	 * @param text The text to be stored in this expression, <code>null</code> cannot be passed
	 */
	protected AbstractExpression(AbstractExpression parent, String text) {
		super();
		this.offset = -1;
		this.text   = text;
		this.parent = parent;
	}

	/**
	 * The given {@link ExpressionVisitor} needs to visit this class but it is defined by a third-
	 * party provider. This method will programmatically invoke the <b>visit</b> method defined on
	 * the visitor. The method signature should be:
	 *
	 * <div><code>{public|protected|private} void visit(ThirdPartyExpression expression)</code></div>
	 * <p>
	 * or
	 *
	 * <div><code>{public|protected|private} void visit(Expression expression)</code></div>
	 * <p>
	 * <b>Note:</b> The package protected visibility (default) should be used with care, if the code
	 * is running inside OSGi, then the method will not be accessible, even through reflection.
	 *
	 * @param visitor The {@link ExpressionVisitor} to visit this {@link Expression} programmatically
	 * @return <code>true</code> if the call was successfully executed; <code>false</code> otherwise
	 * @since 2.4
	 */
	protected boolean acceptUnknownVisitor(ExpressionVisitor visitor) {
		try {
			try {
				acceptUnknownVisitor(visitor, visitor.getClass(), getClass());
			}
			catch (NoSuchMethodException e) {
				// Try with Expression as the parameter type
				acceptUnknownVisitor(visitor, visitor.getClass(), Expression.class);
			}
			return true;
		}
		catch (NoSuchMethodException e) {
			// Ignore, just do nothing
			return false;
		}
		catch (IllegalAccessException e) {
			// Ignore, just do nothing
			return false;
		}
		catch (InvocationTargetException e) {
			Throwable cause = e.getCause();
			RuntimeException actual;
			if (cause instanceof RuntimeException) {
				actual = (RuntimeException) cause;
			}
			else {
				actual = new RuntimeException(cause);
			}
			throw actual;
		}
	}

	/**
	 * The given {@link ExpressionVisitor} needs to visit this class but it is defined by a third-
	 * party provider. This method will programmatically invoke the <b>visit</b> method defined on
	 * the visitor. The method signature should be:
	 *
	 * <div><code>{public|protected|private} void visit(ThirdPartyExpression expression)</code></div>
	 * <p>
	 * or
	 *
	 * <div><code>{public|protected|private} void visit(Expression expression)</code></div>
	 * <p>
	 *
	 * @param visitor The {@link ExpressionVisitor} to visit this {@link Expression} programmatically
	 * @param type The type found in the hierarchy of the given {@link ExpressionVisitor} that will
	 * be used to retrieve the visit method
	 * @param parameterType The parameter type of the visit method
	 * @see #acceptUnknownVisitor(ExpressionVisitor)
	 * @since 2.4
	 */
	protected void acceptUnknownVisitor(ExpressionVisitor visitor,
	                                    Class<?> type,
	                                    Class<?> parameterType) throws NoSuchMethodException,
	                                                                   IllegalAccessException,
	                                                                   InvocationTargetException{

		try {
			Method visitMethod = type.getDeclaredMethod("visit", parameterType);
			visitMethod.setAccessible(true);
			visitMethod.invoke(visitor, this);
		}
		catch (NoSuchMethodException e) {
			type = type.getSuperclass();
			if (type == Object.class) {
				throw e;
			}
			else {
				acceptUnknownVisitor(visitor, type, parameterType);
			}
		}
	}

	/**
	 * Adds the children of this {@link AbstractExpression} to the given collection.
	 *
	 * @param children The collection used to store the children
	 */
	protected void addChildrenTo(Collection<Expression> children) {
	}

	/**
	 * Adds the children of this {@link AbstractExpression} to the given list.
	 *
	 * @param children The list used to store the string representation of this {@link AbstractExpression}
	 */
	protected void addOrderedChildrenTo(List<Expression> children) {
	}

	/**
	 * No factories were found to create an {@link Expression} with the content of {@link WordParser},
	 * this method will retrieve the fallback {@link ExpressionFactory} defined in the given {@link
	 * JPQLQueryBNF BNF}.
	 *
	 * @param wordParser The text to parse based on the current position of the cursor
	 * @param word The word that was retrieved from the given text, which is the first word in the text
	 * @param queryBNF The {@link JPQLQueryBNF} used to determine how to parse from the current
	 * position of the cursor within the JPQL query
	 * @param expression The {@link Expression} that has just been parsed or <code>null</code>
	 * @param tolerant Determines whether the parsing system should be tolerant, meaning if it should
	 * try to parse invalid or incomplete queries
	 * @return The {@link Expression} representing the given sub-query
	 */
	protected final AbstractExpression buildExpressionFromFallingBack(WordParser wordParser,
	                                                                  String word,
	                                                                  JPQLQueryBNF queryBNF,
	                                                                  AbstractExpression expression,
	                                                                  boolean tolerant) {

		ExpressionFactory factory = findFallBackExpressionFactory(queryBNF);

		if (factory == null) {
			return null;
		}

		// When parsing an invalid or incomplete query, it is possible two literals would be parsed
		// but in some cases, a CollectionExpression should not be created and the parsing should
		// actually stop here. Example: BETWEEN 10 20, when parsing 20, it should not be parsed as
		// part of the lower bound expression
		if (tolerant &&
		    (factory.getId() == LiteralExpressionFactory.ID) &&
		    shouldSkipLiteral(expression)) {

			return null;
		}

		return factory.buildExpression(this, wordParser, word, queryBNF, expression, tolerant);
	}

	/**
	 * Creates a new <code>null</code>-{@link Expression} parented with this one.
	 *
	 * @return A new <code>null</code> version of an {@link Expression}
	 */
	protected final AbstractExpression buildNullExpression() {
		return new NullExpression(this);
	}

	/**
	 * Creates a new {@link Expression} wrapping the given character value.
	 *
	 * @param value The character to wrap as a {@link Expression}
	 * @return The {@link Expression} representation of the given identifier where the owning
	 * {@link Expression} is this one
	 */
	protected final Expression buildStringExpression(char value) {
		return buildStringExpression(String.valueOf(value));
	}

	/**
	 * Creates a new {@link Expression} wrapping the given string value.
	 *
	 * @param value The string to wrap as a <code>Expression</code>
	 * @return The {@link Expression} representation of the given identifier where the owning
	 * {@link Expression} is this one
	 */
	protected final Expression buildStringExpression(String value) {
		return new DefaultStringExpression(this, value);
	}

	/**
	 * Creates an {@link Expression} that contains a malformed expression.
	 *
	 * @param text The text causing the expression to be malformed
	 * @return A new {@link Expression} where {@link #toActualText()} returns the given text
	 */
	protected final AbstractExpression buildUnknownExpression(String text) {
		return new UnknownExpression(this, text);
	}

	/**
	 * Calculates the position of the given {@link Expression} by calculating the length of what is before.
	 *
	 * @param expression The {@link Expression} for which its position within the parsed tree needs
	 * to be determined
	 * @param length The current cursor position within the JPQL query while digging into the tree
	 * until the search reaches the expression
	 * @return The length of the string representation for what is coming before the given {@link Expression}
	 * @since 2.4
	 */
	protected final int calculatePosition(Expression expression, int length) {

		Expression parent = expression.getParent();

		// Reach the root
		if (parent == null) {
			return length;
		}

		// Traverse the child expression until the expression
		for (Expression childExpression : parent.orderedChildren()) {

			// Continue to calculate the position by going up the hierarchy
			if (childExpression == expression) {
				return calculatePosition(parent, length);
			}

			length += childExpression.getLength();
		}

		// It should never reach this
		throw new RuntimeException("The position of the Expression could not be calculated: " + expression);
	}

	/**
	 * {@inheritDoc}
	 */
	public final ListIterable<Expression> children() {
		if (children == null) {
			children = new LinkedList<Expression>();
			addChildrenTo(children);
		}
		return new SnapshotCloneListIterable<Expression>(children);
	}

	/**
	 * Retrieve the {@link ExpressionFactory} from the given {@link JPQLQueryBNF} by following the
	 * path of fallback {@link JPQLQueryBNF JPQLQueryBNFs} and then returns the {@link ExpressionFactory}
	 * from the leaf {@link JPQLQueryBNF}.
	 *
	 * @param queryBNF The {@link JPQLQueryBNF} for which its associated fallback {@link ExpressionFactory}
	 * will be searched
	 * @return Either the fallback {@link ExpressionFactory} linked to the given {@link JPQLQueryBNF}
	 * or <code>null</code> if none was declared
	 */
	protected final ExpressionFactory findFallBackExpressionFactory(JPQLQueryBNF queryBNF) {

		String fallBackBNFId = queryBNF.getFallbackBNFId();

		// No fall back BNF is defined, then nothing can be done
		if (fallBackBNFId == null) {
			return null;
		}

		JPQLQueryBNF fallBackQueryBNF = getQueryBNF(fallBackBNFId);

		// Traverse the fall back BNF because it has its own fall back BNF
		if (fallBackQueryBNF != queryBNF &&
		    fallBackQueryBNF.getFallbackBNFId() != null) {

			return findFallBackExpressionFactory(fallBackQueryBNF);
		}

		// Retrieve the factory associated with the fall back BNF
		return getExpressionRegistry().getExpressionFactory(fallBackQueryBNF.getFallbackExpressionFactoryId());
	}

	/**
	 * {@inheritDoc}
	 */
	public JPQLQueryBNF findQueryBNF(Expression expression) {
		return getQueryBNF();
	}

	/**
	 * Retrieves the registered {@link ExpressionFactory} that was registered for the given unique
	 * identifier.
	 *
	 * @param expressionFactoryId The unique identifier of the {@link ExpressionFactory} to retrieve
	 * @return The {@link ExpressionFactory} mapped with the given unique identifier
	 * @see ExpressionRegistry#getExpressionFactory(String)
	 */
	protected final ExpressionFactory getExpressionFactory(String expressionFactoryId) {
		return getExpressionRegistry().getExpressionFactory(expressionFactoryId);
	}

	/**
	 * Returns the registry containing the {@link JPQLQueryBNF JPQLQueryBNFs} and the {@link
	 * org.eclipse.persistence.jpa.jpql.parser.ExpressionFactory ExpressionFactories} that are used
	 * to properly parse a JPQL query.
	 *
	 * @return The registry containing the information related to the JPQL grammar
	 */
	protected final ExpressionRegistry getExpressionRegistry() {
		return getGrammar().getExpressionRegistry();
	}

	/**
	 * {@inheritDoc}
	 */
	public JPQLGrammar getGrammar() {
		return getRoot().getGrammar();
	}

	/**
	 * Retrieves the JPA version in which the identifier was first introduced.
	 *
	 * @return The version in which the identifier was introduced
	 */
	public JPAVersion getIdentifierVersion(String identifier) {
		return getRoot().getIdentifierVersion(identifier);
	}

	/**
	 * Returns the version of the Java Persistence to support.
	 *
	 * @return The JPA version supported by the grammar
	 * @see JPQLGrammar
	 */
	protected JPAVersion getJPAVersion() {
		return getRoot().getJPAVersion();
	}

	/**
	 * {@inheritDoc}
	 */
	public final int getLength() {
		return toActualText().length();
	}

	/**
	 * {@inheritDoc}
	 */
	public final int getOffset() {
		if (offset == -1) {
			offset = calculatePosition(this, 0);
		}
		return offset;
	}

	/**
	 * {@inheritDoc}
	 */
	public final AbstractExpression getParent() {
		return parent;
	}

	/**
	 * Retrieves the BNF object that was registered for the given unique identifier.
	 *
	 * @param queryBNFID The unique identifier of the {@link JPQLQueryBNF} to retrieve
	 * @return The {@link JPQLQueryBNF} representing a section of the grammar
	 */
	public JPQLQueryBNF getQueryBNF(String queryBNFID) {
		return getExpressionRegistry().getQueryBNF(queryBNFID);
	}

	/**
	 * {@inheritDoc}
	 */
	public final JPQLExpression getRoot() {
		return (parent == null) ? (JPQLExpression) this : parent.getRoot();
	}

	/**
	 * Returns the encapsulated text of this {@link AbstractExpression}, which can be used in various
	 * ways, it can be a keyword, a literal, etc.
	 *
	 * @return Either the JPQL identifier for this {@link AbstractExpression}, the literal it
	 * encapsulates or an empty string
	 */
	protected String getText() {
		return text;
	}

	/**
	 * Determines whether the given {@link JPQLQueryBNF} handles aggregate expressions.
	 *
	 * @param queryBNF The {@link JPQLQueryBNF} used to determine if the parsing should handle
	 * aggregate expressions
	 * @return <code>true</code> if the given BNF handles aggregate expressions; <code>false</code>
	 * otherwise
	 */
	protected boolean handleAggregate(JPQLQueryBNF queryBNF) {
		return queryBNF.handleAggregate();
	}

	/**
	 * Determines whether the given {@link JPQLQueryBNF} handles a collection of sub-expressions that
	 * are separated by commas.
	 *
	 * @param queryBNF The {@link JPQLQueryBNF} used to determine if the parsing should handle
	 * collection of sub-expressions
	 * @return <code>true</code> if the sub-expression to parse might have several sub-expressions
	 * separated by commas; <code>false</code> otherwise
	 */
	protected boolean handleCollection(JPQLQueryBNF queryBNF) {
		return queryBNF.handleCollection();
	}

	/**
	 * {@inheritDoc}
	 */
	public boolean isAncestor(Expression expression) {

		if (expression == this) {
			return true;
		}

		if (expression == null) {
			return false;
		}

		return isAncestor(expression.getParent());
	}

	/**
	 * Determines if the given word is a JPQL identifier. The check is case insensitive.
	 *
	 * @param word The word to test if it is a JPQL identifier
	 * @return <code>true</code> if the word is an identifier, <code>false</code> otherwise
	 * @see ExpressionRegistry#isIdentifier(String)
	 */
	protected final boolean isIdentifier(String word) {
		return getExpressionRegistry().isIdentifier(word);
	}

	/**
	 * Determines whether this expression is a <code>null</code> {@link Expression} or any other subclass.
	 *
	 * @return <code>false</code> by default
	 */
	protected boolean isNull() {
		return false;
	}

	/**
	 * Determines whether the parsing is complete based on what is left in the given text. The text
	 * is never empty.
	 *
	 * @param wordParser The text to parse based on the current position of the cursor
	 * @param word The word that was retrieved from the given text, which is the first word in the text
	 * @param expression The {@link Expression} that has already been parsed
	 * @return <code>true</code> if the text no longer can't be parsed by the current expression;
	 * <code>false</code> if more can be parsed
	 */
	protected boolean isParsingComplete(WordParser wordParser, String word, Expression expression) {
		// TODO: MAYBE MOVE THIS TO THE JPQL GRAMMAR SO GENERIC JPA DOES
		//       NOT HAVE KNOWLEDGE OF ECLIPSELINK SPECIFIC FUNCTIONS
		return word.equalsIgnoreCase(FROM)                        ||
		       word.equalsIgnoreCase(WHERE)                       ||
		       word.equalsIgnoreCase(HAVING)                      ||
		       wordParser.startsWithIdentifier(GROUP_BY)          ||
		       wordParser.startsWithIdentifier(ORDER_BY)          ||
		       wordParser.startsWithIdentifier(AS_OF)             ||
		       wordParser.startsWithIdentifier(START_WITH)        ||
		       wordParser.startsWithIdentifier(CONNECT_BY)        ||
		       wordParser.startsWithIdentifier(ORDER_SIBLINGS_BY) ||
		       word.equalsIgnoreCase(UNION)                       ||
		       word.equalsIgnoreCase(INTERSECT)                   ||
		       word.equalsIgnoreCase(EXCEPT);
	}

	/**
	 * Determines if the parser is in tolerant mode or is in fast mode. When the tolerant is turned
	 * on, it means the parser will attempt to parse incomplete or invalid queries.
	 *
	 * @return <code>true</code> if the parsing system should parse invalid or incomplete queries;
	 * <code>false</code> when the query is well-formed and valid
	 */
	protected boolean isTolerant() {
		return getRoot().isTolerant();
	}

	/**
	 * Determines whether this expression is an unknown {@link Expression} or any other subclass.
	 *
	 * @return <code>false</code> by default
	 */
	protected boolean isUnknown() {
		return false;
	}

	/**
	 * Determines whether this {@link AbstractExpression} is virtual, meaning it's not part of the
	 * query but is required for proper navigability.
	 *
	 * @return <code>true</code> if this {@link AbstractExpression} was virtually created to fully
	 * qualify path expression; <code>false</code> if it was parsed
	 */
	protected boolean isVirtual() {
		return false;
	}

	/**
	 * {@inheritDoc}
	 */
	public final ListIterable<Expression> orderedChildren() {
		if (orderedChildren == null) {
			orderedChildren = new LinkedList<Expression>();
			addOrderedChildrenTo(orderedChildren);
		}
		return new SnapshotCloneListIterable<Expression>(orderedChildren);
	}

	/**
	 * Parses the query by starting at the current position, which is part of the given {@link WordParser}.
	 *
	 * @param wordParser The text to parse based on the current position of the cursor
	 * @param tolerant Determines whether the parsing system should be tolerant, meaning if it should
	 * try to parse invalid or incomplete queries
	 */
	protected abstract void parse(WordParser wordParser, boolean tolerant);

	/**
	 * Parses the given text by using the specified BNF.
	 *
	 * @param wordParser The text to parse based on the current position of the cursor
	 * @param queryBNFId The unique identifier of the {@link JPQLQueryBNF} that is used to determine
	 * how to parse the text at the current cursor position within the JPQL query
	 * @param tolerant Determines whether the parsing system should be tolerant, meaning if it should
	 * try to parse invalid or incomplete queries
	 * @return The {@link Expression} representing the given sub-query
	 */
	@SuppressWarnings("null")
	protected AbstractExpression parse(WordParser wordParser, String queryBNFId, boolean tolerant) {

		// Quick check so we don't create some objects for no reasons
		if (tolerant && wordParser.isTail()) {
			return null;
		}

		//
		// NOTE: This method could look better but for performance reason, it is a single method,
		//       which reduces the number of objects created and methods called.
		//

		JPQLQueryBNF queryBNF = getQueryBNF(queryBNFId);

		int count = 0;
		boolean beginning = !tolerant;
		char character = wordParser.character();

		AbstractExpression child;
		AbstractExpression expression = null;

		Info rootInfo = null;
		Info currentInfo = null;

		// Parse the string until the position of the cursor is at
		// the end of the string or until the parsing is complete
		while (!wordParser.isTail()) {

			child = null;

			//
			// Step 1
			//
			// Right away create a SubExpression and parse the encapsulated expression
			if (character == LEFT_PARENTHESIS) {

				// If the JPQLQueryBNF handles parsing the sub-expression, then delegate the parsing
				// to its fallback ExpressionFactory
				if (queryBNF.handleSubExpression()) {
					expression = buildExpressionFromFallingBack(
						wordParser,
						ExpressionTools.EMPTY_STRING,
						queryBNF,
						expression,
						tolerant
					);
				}
				else {
					expression = new SubExpression(this, queryBNF);
					expression.parse(wordParser, tolerant);

					// Make sure this is not the root and if the parent handles parsing the sub-
					// expression, then the Expression needs to be returned without further parsing
					if ((parent != null) && parent.getQueryBNF().handleSubExpression()) {
						return expression;
					}
				}

				// Something has been parsed, which means it's not the beginning anymore
				beginning = false;

				// Continue to the next character/word
				count     = wordParser.skipLeadingWhitespace();
				character = wordParser.character();

				// Store the SubExpression
				currentInfo = (rootInfo == null) ? (rootInfo = new Info()) : (currentInfo.next = new Info(currentInfo));
				currentInfo.expression = expression;
				currentInfo.space = count > 0;
			}

			// Retrieve the next word, including any arithmetic symbols
			String word = wordParser.word();

			// A word was parsed, attempt to parse it using first the factory, then the fallback factory
			if (word.length() > 0) {

				// Nothing more to parse
				if (!tolerant && !beginning && isParsingComplete(wordParser, word, expression) ||
				     tolerant &&               isParsingComplete(wordParser, word, expression)) {

					break;
				}

				//
				// Step 2
				//
				// Parse using the ExpressionFactory that is mapped with a JPQL identifier (word)
				if (shouldParseWithFactoryFirst() &&
				    (wordParser.getWordType() == WordType.WORD)) {

					ExpressionFactory factory = queryBNF.getExpressionFactory(word);

					if (factory != null) {
						child = factory.buildExpression(this, wordParser, word, queryBNF, expression, tolerant);

						if (child != null) {

							// The new expression is a child of the previous expression,
							// remove it from the collection since it's already parented
							if ((expression != null) && child.isAncestor(expression)) {
								if (currentInfo == rootInfo) {
									rootInfo = null;
									currentInfo = null;
								}
								else if (currentInfo != null) {
									currentInfo = currentInfo.previous;
								}
							}

							// Something has been parsed, which means it's not the beginning anymore
							beginning = false;

							// Continue with the next character/word
							count     = wordParser.skipLeadingWhitespace();
							character = wordParser.character();

							// The new expression becomes the previous expression
							expression = child;
						}
					}
				}

				//
				// Step 3
				//
				// No factories could be used, use the fall back ExpressionFactory
				if (child == null) {
					child = buildExpressionFromFallingBack(wordParser, word, queryBNF, expression, tolerant);

					if (child != null) {

						// The new expression is a child of the previous expression,
						// remove it from the collection since it's already parented
						if ((expression != null) && child.isAncestor(expression)) {
							if (currentInfo == rootInfo) {
								rootInfo = null;
								currentInfo = null;
							}
							else if (currentInfo != null) {
								currentInfo = currentInfo.previous;
							}
						}

						// Something has been parsed, which means it's not the beginning anymore
						beginning = false;

						// Continue with the next character/word
						count     = wordParser.skipLeadingWhitespace();
						character = wordParser.character();

						// The new expression becomes the previous expression
						expression = child;
					}
				}

				//
				// Step 4
				//
				// If nothing was parsed, then attempt to parse the fragment by retrieving the factory
				// directory from the JPQL grammar and not from the one registered with the current BNF
				if (tolerant && (child == null)) {

					ExpressionRegistry expressionRegistry = getExpressionRegistry();

					if (expressionRegistry.getIdentifierRole(word) != IdentifierRole.AGGREGATE) {

						ExpressionFactory factory = expressionRegistry.expressionFactoryForIdentifier(word);

						if (factory != null) {
							child = factory.buildExpression(this, wordParser, word, queryBNF, expression, tolerant);

							if (child != null) {
								child = new BadExpression(this, child);

								// The new expression is a child of the previous expression,
								// remove it from the collection since it's already parented
								if ((expression != null) && child.isAncestor(expression)) {
									if (currentInfo == rootInfo) {
										rootInfo = null;
										currentInfo = null;
									}
									else if (currentInfo != null) {
										currentInfo = currentInfo.previous;
									}
								}

								// Something has been parsed, which means it's not the beginning anymore
								beginning = false;

								// Continue with the next character/word
								count     = wordParser.skipLeadingWhitespace();
								character = wordParser.character();

								// The new expression becomes the previous expression
								expression = child;
							}
						}
					}
				}
			}

			// Nothing could be parsed, break here so the parent can continue parsing.
			// Example: AVG() and we're parsing what's inside the parenthesis (nothing).
			// But if it's (,), then we have to create a collection of "null" expressions
			// separated by a comma
			if ((child == null) && (character != COMMA)) {
				break;
			}

			// Store the child but skip a very special case, which happens when parsing
			// two subqueries in a collection expression. Example: (SELECT ... ), (SELECT ... )
			if ((expression == null) || (child != null)) {
				currentInfo = (rootInfo == null) ? (rootInfo = new Info()) : (currentInfo.next = new Info(currentInfo));
				currentInfo.expression = child;
				currentInfo.space = count > 1;
			}

			// Nothing else to parse
			if (wordParser.isTail()) {
				break;
			}

			// ','
			if (character == COMMA) {

				// The current expression does not handle collection, then stop the
				// parsing here so the parent can continue to parse
				if (!handleCollection(queryBNF) || tolerant && isParsingComplete(wordParser, word, expression)) {
					break;
				}

				// Skip the comma
				wordParser.moveForward(1);
				currentInfo.comma = true;

				// Remove leading whitespace
				count = wordParser.skipLeadingWhitespace();
				currentInfo.space = count > 0;

				character = wordParser.character();
				expression = null;

				// Special case: ((), (), ())
				if (character == LEFT_PARENTHESIS) {
					continue;
				}

				// No more text, the query ends with a comma
				word = wordParser.word();
				boolean stopParsing = tolerant && (word.length() == 0 || isParsingComplete(wordParser, word, null));

				if (wordParser.isTail() || stopParsing) {

					// Make sure the space is not re-added at the end of the query
					count = 0;

					// Add a null Expression since the expression ends with a comma
					currentInfo = (rootInfo == null) ? (rootInfo = new Info()) : (currentInfo.next = new Info(currentInfo));

					// Nothing else to parse
					if (stopParsing) {
						break;
					}
				}

				// Nothing more to parse
				if (character == RIGHT_PARENTHESIS) {
					break;
				}
			}
			else {

				// Continue parsing the collection expression
				if (character != RIGHT_PARENTHESIS &&
				    handleAggregate(queryBNF)) {

					currentInfo.space = count > 0;
				}
				// Nothing more to parse
				else {
					break;
				}
			}
		}

		if (count > 0) {
			currentInfo.space = currentInfo.comma;
			if (!currentInfo.comma) {
				wordParser.moveBackward(count);
			}
		}

		// Nothing was parsed
		if (currentInfo == null) {
			return null;
		}

		// Simply return the single expression
		if (currentInfo == rootInfo &&
		   !currentInfo.comma &&
		   !currentInfo.space) {

			return currentInfo.expression;
		}

		// Return a collection of expressions
		return new CollectionExpression(
			this,
			rootInfo.buildChildren(),
			rootInfo.buildCommas(),
			rootInfo.buildSpaces()
		);
	}

	/**
	 * Right away parses the text by retrieving the {@link ExpressionFactory} for the first word that
	 * is extracted from {@link WordParser} at the current location.
	 *
	 * @param wordParser The text to parse based on the current position of the cursor
	 * @param queryBNFId The unique identifier of the {@link JPQLQueryBNF} that is used to determine
	 * how to parse the text at the current cursor position within the JPQL query
	 * @param tolerant Determines whether the parsing system should be tolerant, meaning if it should
	 * try to parse invalid or incomplete queries
	 * @return The {@link Expression} representing the given sub-query
	 */
	protected AbstractExpression parseUsingExpressionFactory(WordParser wordParser,
	                                                         String queryBNFId,
	                                                         boolean tolerant) {

		String word = wordParser.word();
		JPQLQueryBNF queryBNF = getQueryBNF(queryBNFId);
		ExpressionFactory factory = queryBNF.getExpressionFactory(word);

		if (factory == null) {
			return null;
		}

		return factory.buildExpression(this, wordParser, word, queryBNF, null, tolerant);
	}

	/**
	 * {@inheritDoc}
	 */
	public void populatePosition(QueryPosition queryPosition, int position) {

		queryPosition.addPosition(this, position);

		// The position is at the beginning of this expression
		if (position == 0) {
			queryPosition.setExpression(this);
		}
		else {
			// Traverse the children in order to find where the cursor is located
			for (Expression expression : orderedChildren()) {

				String expressionText = expression.toParsedText();

				// The position is in the Expression, traverse it
				if (position <= expressionText.length()) {
					expression.populatePosition(queryPosition, position);
					return;
				}

				// Continue with the next child by adjusting the position
				position -= expressionText.length();
			}

			throw new IllegalStateException("A problem was encountered while calculating the position.");
		}
	}

	/**
	 * Rebuilds the actual parsed text if it has been cached.
	 */
	protected final void rebuildActualText() {
		if (actualText != null) {
			toActualText();
		}
	}

	/**
	 * Rebuilds the parsed parsed text if it has been cached.
	 */
	protected final void rebuildParsedText() {
		if (parsedText != null) {
			toParsedText();
		}
	}

	/**
	 * Re-parents this {@link Expression} to be a child of the given {@link Expression}.
	 *
	 * @param parent The new parent of this object
	 */
	protected final void setParent(AbstractExpression parent) {
		this.parent = parent;
	}

	/**
	 * Sets the text of this {@link Expression}.
	 *
	 * @param text The immutable text wrapped by this {@link Expression}, which cannot be <code>null</code>
	 */
	protected final void setText(String text) {
		this.text = text;
	}

	/**
	 * Determines whether the parsing of the query should be performed using the {@link ExpressionFactory
	 * factories} first or it should automatically fallback to the fallback factory.
	 *
	 * @return <code>true</code> is returned by default so the factories are used before falling back
	 */
	protected boolean shouldParseWithFactoryFirst() {
		return true;
	}

	/**
	 * When parsing an invalid or incomplete query, it is possible two literals would be parsed but
	 * in some cases, a CollectionExpression should not be created and the parsing should actually
	 * stop here. Example: BETWEEN 10 20, when parsing 20, it should not be parsed as part of the
	 * lower bound expression.
	 *
	 * @param expression The {@link Expression} that has just been parsed or <code>null</code>
	 * @return <code>true</code>
	 */
	protected boolean shouldSkipLiteral(AbstractExpression expression) {
		return (expression != null);
	}

	/**
	 * {@inheritDoc}
	 */
	public String toActualText() {
		if (actualText == null) {
			StringBuilder writer = new StringBuilder();
			toParsedText(writer, true);
			actualText = writer.toString();
		}
		return actualText;
	}

	/**
	 * {@inheritDoc}
	 */
	public String toParsedText() {
		if (parsedText == null) {
			StringBuilder writer = new StringBuilder();
			toParsedText(writer, false);
			parsedText = writer.toString();
		}
		return parsedText;
	}

	/**
	 * Generates a string representation of this {@link Expression}, including its children,
	 * if it has any.
	 *
	 * @param writer The buffer used to append this {@link Expression}'s string representation
	 * @param actual Determines whether the string representation should represent what was parsed,
	 * i.e. include any "virtual" whitespace (such as ending whitespace) and the actual case of the
	 * JPQL identifiers
	 */
	protected abstract void toParsedText(StringBuilder writer, boolean actual);

	/**
	 * {@inheritDoc}
	 */
	@Override
	public final String toString() {
		// toString() should only be called during debugging, thus the cached parsed text
		// should always be recreated in order to reflect the current state while debugging
		parsedText = null;
		return toParsedText();
	}

	/**
	 * Rather than creating three lists when performing a single parsing operation ({@link
	 * AbstractExpression#parse(WordParser, String, boolean) parse(WordParser, String, boolean)}),
	 * this class will act as a simple chained list and postponing the creation of those three lists
	 * only when needed (which is when the parsed expression needs to be a collection of expressions).
	 */
	private static class Info {

		/**
		 * Flag indicating a comma follows the parsed {@link #expression}.
		 */
		boolean comma;

		/**
		 * The parsed {@link Expression}, which is the first one that was parsed if {@link #next} is
		 * <code>null</code>, meaning this <code>Info</code> object is the root of the chained list.
		 */
		AbstractExpression expression;

		/**
		 * When more than one expression needs to be parsed, it will be chained.
		 */
		Info next;

		/**
		 * The parent within the chain of this one.
		 */
		Info previous;

		/**
		 * Flag indicating a whitespace follows the parsed {@link #expression}.
		 */
		boolean space;

		/**
		 * Creates a new <code>Info</code>.
		 */
		Info() {
			super();
		}

		/**
		 * Creates a new <code>Info</code>.
		 *
		 * @param previous The parent within the chain of this one
		 */
		Info(Info previous) {
			super();
			this.previous = previous;
		}

		private void addChild(ArrayList<AbstractExpression> children) {
			children.add(expression);
			if (next != null) {
				next.addChild(children);
			}
		}

		private void addComma(ArrayList<Boolean> children) {
			children.add(comma);
			if (next != null) {
				next.addComma(children);
			}
		}

		private void addSpace(ArrayList<Boolean> children) {
			children.add(space);
			if (next != null) {
				next.addSpace(children);
			}
		}

		List<AbstractExpression> buildChildren() {
			ArrayList<AbstractExpression> children = new ArrayList<AbstractExpression>();
			addChild(children);
			return children;
		}

		List<Boolean> buildCommas() {
			ArrayList<Boolean> children = new ArrayList<Boolean>();
			addComma(children);
			return children;
		}

		List<Boolean> buildSpaces() {
			ArrayList<Boolean> children = new ArrayList<Boolean>();
			addSpace(children);
			return children;
		}
	}
}