/*******************************************************************************
 * Copyright (c) 2012, 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;

import java.util.List;
import java.util.Map;
import org.eclipse.persistence.jpa.jpql.parser.Expression;
import org.eclipse.persistence.jpa.jpql.parser.IdentificationVariable;
import org.eclipse.persistence.jpa.jpql.parser.JPQLGrammar;
import org.eclipse.persistence.jpa.jpql.parser.SimpleSelectStatement;

/**
 * This helper is used by {@link AbstractSemanticValidator} in order to retrieve JPA information.
 * This helper allows third party adopter to write an instance of this helper that directly access
 * the JPA information without having to implement Hermes SPI, which can improve performance.
 * <p>
 * {@link org.eclipse.persistence.jpa.jpql.tools.GenericSemanticValidatorHelper
 * GenericSemanticValidatorHelper} is a default implementation that uses Hermes SPI.
 * <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.
 *
 * @version 2.5
 * @since 2.4
 * @author Pascal Filion
 */
public interface SemanticValidatorHelper {

	/**
	 * Collects the identification variables that are defined in the <code>FROM</code> clause of the
	 * current query and from the parent queries.
	 *
	 * @param identificationVariables The {@link Map} used to store the variables
	 */
	void collectAllDeclarationIdentificationVariables(Map<String, List<IdentificationVariable>> identificationVariables);

	/**
	 * Collects the identification variables that are defined in the <code>FROM</code> clause of the
	 * current query.
	 *
	 * @param identificationVariables The {@link Map} used to store the variables
	 */
	void collectLocalDeclarationIdentificationVariables(Map<String, List<IdentificationVariable>> identificationVariables);

	/**
	 * Disposes this context, which is the current context being used by a subquery. Once it is
	 * disposed, any information retrieved will be for the subquery's parent query.
	 */
	void disposeSubqueryContext();

	/**
	 * Returns the name of the all entities that are present in the context of a persistence unit.
	 *
	 * @return The list of entity names
	 */
	String[] entityNames();

	/**
	 * Returns the ordered list of {@link JPQLQueryDeclaration}, which contain the information
	 * contained in the query's <code>FROM</code> clause.
	 *
	 * @return The list of {@link JPQLQueryDeclaration} of the current query that was parsed and from
	 * the parent queries
	 */
	List<JPQLQueryDeclaration> getAllDeclarations();

	/**
	 * Returns the constructors for the given type. All public, protected, default (package) access,
	 * and private constructors should be included.
	 * <p>
	 * If it was going through Hermes SPI, the type of the argument would be {@link
     * org.eclipse.persistence.jpa.jpql.tools.spi.IType IType} and the return type would be {@link
     * org.eclipse.persistence.jpa.jpql.tools.spi.IConstructor IConstructor}.
	 *
	 * @return The declared constructors
	 */
	Object[] getConstructors(Object type);

	/**
	 * Returns the ordered list of {@link JPQLQueryDeclaration}, which contain the information
	 * contained in the query's <code>FROM</code> clause.
	 *
	 * @return The list of {@link JPQLQueryDeclaration} of the current query that was parsed
	 */
	List<JPQLQueryDeclaration> getDeclarations();

	/**
	 * Retrieves the embeddable with the given type.
	 *
	 * @param type The Java type of the embeddable to retrieve
	 * @return The embeddable for the given type if it's representing an embeddable; <code>null</code>
	 * otherwise
	 */
	Object getEmbeddable(Object type);

	/**
	 * Retrieves the entity with the given entity name.
	 *
	 * @param entityName The abstract schema name of the entity to retrieve
	 * @return The entity with the given name; <code>null</code> otherwise
	 */
	Object getEntityNamed(String entityName);

	/**
	 * Returns the constant names for the given {@link Enum} type.
	 * <p>
	 * If it was going through Hermes SPI, the type of the argument would be
     * {@link org.eclipse.persistence.jpa.jpql.tools.spi.IType IType}.
	 *
	 * @param type The {@link Enum} type
	 * @return The list of constant names
	 */
	String[] getEnumConstants(Object type);

	/**
	 * Returns the {@link JPQLGrammar} that defines how the JPQL query was parsed.
	 *
	 * @return The {@link JPQLGrammar} that was used to parse the JPQL query
	 */
	JPQLGrammar getGrammar();

	/**
	 * Returns the managed type by resolving the given {@link Expression}.
	 * <p>
	 * If it was going through Hermes SPI, the return type would be
     * {@link org.eclipse.persistence.jpa.jpql.tools.spi.IManagedType IManagedType}.
	 */
	Object getManagedType(Expression expression);

	/**
	 * Returns the mapping with the given name.
	 * <p>
	 * If it was going through Hermes SPI, the type of the argument would be
     * {@link org.eclipse.persistence.jpa.jpql.tools.spi.IManagedType IManagedType}
     * and the return type would be {@link
	 * org.eclipse.persistence.jpa.jpql.tools.spi.IMapping IMapping}.
	 *
	 * @param managedType The managed type that has a mapping with the given name
	 * @param name The name of the mapping to retrieve
	 * @return Either the mapping or <code>null</code> if it could not be found
	 */
	Object getMappingNamed(Object managedType, String name);

	/**
	 * Returns the type of the given mapping object.
	 * <p>
	 * If it was going through Hermes SPI, the type of the argument would be {@link
     * org.eclipse.persistence.jpa.jpql.tools.spi.IMapping IMapping} and the return type
     * would be {@link org.eclipse.persistence.jpa.jpql.tools.spi.IType IType}.
	 *
	 * @param mapping The mapping object
	 * @return The type of the given mapping
	 */
	Object getMappingType(Object mapping);

	/**
	 * Returns the list of type declarations representing the given constructor's parameter types.
	 * If this is the default constructor, then an empty array should be returned.
	 * <p>
	 * If it was going through Hermes SPI, the type of the argument would be {@link
     * org.eclipse.persistence.jpa.jpql.tools.spi.IConstructor IConstructor} and the return type would be
     * {@link org.eclipse.persistence.jpa.jpql.tools.spi.ITypeDeclaration ITypeDeclaration}.
	 *
	 * @param constructor The constructor to return its parameter types
	 * @return The list of parameter types or an empty list
	 */
	Object[] getMethodParameterTypeDeclarations(Object constructor);

	/**
	 * Returns the reference managed type from the given relationship mapping.
	 * <p>
	 * If it was going through Hermes SPI, the type of the argument would be {@link
     * org.eclipse.persistence.jpa.jpql.tools.spi.IMapping IMapping} and the return type would be {@link
	 * org.eclipse.persistence.jpa.jpql.tools.spi.IManagedType IManagedType}.
	 *
	 * @param relationshipMapping The relationship mapping
	 * @return The managed type referenced by the given relationship mapping
	 */
	Object getReferenceManagedType(Object relationshipMapping);

	/**
	 * Returns the type by resolving the given {@link Expression}.
	 * <p>
	 * If it was going through Hermes SPI, the return type would be
     * {@link org.eclipse.persistence.jpa.jpql.tools.spi.IType IType}.
	 *
	 * @param expression The {@link Expression} to resolve
	 * @return The type of the given {@link Expression} or <code>null</code> if it could not be
	 * validated
	 */
	Object getType(Expression expression);

	/**
	 * Returns the type defined for the Java member.
	 * <p>
	 * If it was going through Hermes SPI, the type of the argument would be {@link
     * org.eclipse.persistence.jpa.jpql.tools.spi.ITypeDeclaration ITypeDeclaration} and the return type would be
	 * {@link org.eclipse.persistence.jpa.jpql.tools.spi.IType IType}.
	 *
	 * @return The type defined for the Java member
	 */
	Object getType(Object typeDeclaration);

	/**
	 * Retrieves the class with the given fully qualified name.
	 * <p>
	 * If it was going through Hermes SPI, an {@link org.eclipse.persistence.jpa.jpql.tools.spi.IType
	 * IType} would be returned.
	 *
	 * @param typeName The fully qualified  name of the class to retrieve
	 * @return The class to retrieve
	 */
	Object getType(String typeName);

	/**
	 * Returns the type declaration for the given {@link Expression}'s type.
	 * <p>
	 * If it was going through Hermes SPI, the type of the argument would be
     * {@link org.eclipse.persistence.jpa.jpql.tools.spi.ITypeDeclaration ITypeDeclaration}.
	 *
	 * @param expression The {@link Expression} to resolve
	 * @return Either the type declaration that was resolved for the given {@link Expression}
	 */
	Object getTypeDeclaration(Expression expression);

	/**
	 * Returns the helper that gives access to the most common class metadata.
	 *
	 * @return A helper containing a collection of methods related to class metadata
	 */
	ITypeHelper getTypeHelper();

	/**
	 * Returns the fully qualified class name of the given type.
	 * <p>
	 * If it was going through Hermes SPI, the type of the argument would be
     * {@link org.eclipse.persistence.jpa.jpql.tools.spi.IType IType}.
	 *
	 * @param type The type to retrieve its name
	 * @return The name of the class represented by this one
	 */
	String getTypeName(Object type);

	/**
	 * Determines whether type 1 is an instance of type 2.
	 * <p>
	 * If it was going through Hermes SPI, the type of the arguments would be
     * {@link org.eclipse.persistence.jpa.jpql.tools.spi.IType IType}.
	 *
	 * @param type1 The type to check if it is an instance of type 2
	 * @param type2 The type used to determine if the class represented by type 1 is an instance
	 * of with one
	 * @return <code>true</code> if type 1 is an instance of the type 2; <code>false</code> otherwise
	 */
	boolean isAssignableTo(Object type1, Object type2);

	/**
	 * Determines whether the given identification variable is defining a join or a collection member
	 * declaration expressions.
	 *
	 * @param variableName The identification variable to check for what it maps
	 * @return <code>true</code> if the given identification variable maps a collection-valued field
	 * defined in a <code>JOIN</code> or <code>IN</code> expression; <code>false</code> if it's not
	 * defined or it's mapping an abstract schema name
	 */
	boolean isCollectionIdentificationVariable(String variableName);

	/**
	 * Determines whether the given mapping is a collection type mapping.
	 * <p>
	 * If it was going through Hermes SPI, the type of the arguments would be
     * {@link org.eclipse.persistence.jpa.jpql.tools.spi.IMapping IMapping}.
	 *
	 * @param mapping The mapping object to verify if it represents a collection mapping
	 * @return <code>true</code> if the given mapping is a collection mapping; <code>false</code>
	 * otherwise
	 */
	boolean isCollectionMapping(Object mapping);

	/**
	 * Determines whether the given type represents an {@link Enum}.
	 * <p>
	 * If it was going through Hermes SPI, the type of the argument would be
     * {@link org.eclipse.persistence.jpa.jpql.tools.spi.IType IType}.
	 *
	 * @return <code>true</code> if the given type is an {@link Enum}; <code>false</code> otherwise
	 */
	boolean isEnumType(Object type);

	/**
	 * Determines whether an identification variable can be used in a comparison expression when the
	 * operator is either {@literal '<', '<=', '>', '>='}.
	 *
	 * @param expression The {@link IdentificationVariable} that is mapped to either an entity, a
	 * singled-object value field, a collection-valued object field
	 * @return <code>true</code> if it can be used in a ordering comparison expression; <code>false</code>
	 * if it can't
	 */
	boolean isIdentificationVariableValidInComparison(IdentificationVariable expression);

	/**
	 * Determines whether the given managed type actually exists.
	 * <p>
	 * If it was going through Hermes SPI, the type of the argument would be
     * {@link org.eclipse.persistence.jpa.jpql.tools.spi.IManagedType IManagedType}.
	 *
	 * @return <code>true</code> if the given managed type can be located; <code>false</code> if it
	 * could not be found
	 */
	boolean isManagedTypeResolvable(Object managedType);

	/**
	 * Determines whether the given mapping is a property type mapping.
	 * <p>
	 * If it was going through Hermes SPI, the type of the arguments would be
     * {@link org.eclipse.persistence.jpa.jpql.tools.spi.IMapping IMapping}.
	 *
	 * @param mapping The mapping object to verify if it represents a property mapping
	 * @return <code>true</code> if the given mapping is a property mapping; <code>false</code> otherwise
	 */
	boolean isPropertyMapping(Object mapping);

	/**
	 * Determines whether the given mapping is a relationship type mapping.
	 * <p>
	 * If it was going through Hermes SPI, the type of the arguments would be
     * {@link org.eclipse.persistence.jpa.jpql.tools.spi.IMapping IMapping}.
	 *
	 * @param mapping The mapping object to verify if it represents a relationship mapping
	 * @return <code>true</code> if the given mapping is a relationship mapping; <code>false</code> otherwise
	 */
	boolean isRelationshipMapping(Object mapping);

	/**
	 * Determines if the given variable is a result variable.
	 *
	 * @param variableName The variable to check if it's a result variable
	 * @return <code>true</code> if the given variable is defined as a result variable;
	 * <code>false</code> otherwise
	 */
	boolean isResultVariable(String variableName);

	/**
	 * Determines whether the given mapping is a transient attribute.
	 * <p>
	 * If it was going through Hermes SPI, the type of the arguments would be
     * {@link org.eclipse.persistence.jpa.jpql.tools.spi.IMapping IMapping}.
	 *
	 * @param mapping The mapping object to verify if it represents a transient attribute
	 * @return <code>true</code> if the given attribute is a transient mapping; <code>false</code> otherwise
	 */
	boolean isTransient(Object mapping);

	/**
	 * Determines whether type declaration 1 is an instance of type declaration 2.
	 * <p>
	 * If it was going through Hermes SPI, the type of the arguments would be
     * {@link org.eclipse.persistence.jpa.jpql.tools.spi.ITypeDeclaration ITypeDeclaration}.
	 *
	 * @param typeDeclaration1 The type declaration to check if it is an instance of type declaration 2
	 * @param typeDeclaration2 The type used to determine if the class represented by type
	 * declaration 1 is an instance of with one
	 * @return <code>true</code> if type declaration 1 is an instance of the type declaration 2;
	 * <code>false</code> otherwise
	 */
	boolean isTypeDeclarationAssignableTo(Object typeDeclaration1, Object typeDeclaration2);

	/**
	 * Determines whether the given type actually exists.
	 * <p>
	 * If it was going through Hermes SPI, the type of the argument would be
     * {@link org.eclipse.persistence.jpa.jpql.tools.spi.IType IType}.
	 *
	 * @return <code>true</code> if the actual class exists; <code>false</code> otherwise
	 */
	boolean isTypeResolvable(Object type);

	/**
	 * Changes the state of this helper to use the given subquery.
	 *
	 * @param expression The parsed tree representation of the subquery that will become the current query
	 * @see #disposeSubqueryContext()
	 */
	void newSubqueryContext(SimpleSelectStatement expression);

	/**
	 * Returns the mapping for the field represented by the given {@link Expression}.
	 * <p>
	 * If it was going through Hermes SPI, the return type would be
     * {@link org.eclipse.persistence.jpa.jpql.tools.spi.IMapping IMapping}.
	 *
	 * @param expression The {@link Expression} representing a state field path expression or a
	 * collection-valued path expression
	 * @return Either the mapping or <code>null</code> if none exists
	 */
	Object resolveMapping(Expression expression);

	/**
	 * Returns the mapping that should be a persistence field from the entity defined by the given
	 * identification variable.
	 * <p>
	 * If it was going through Hermes SPI, the return type would be
     * {@link org.eclipse.persistence.jpa.jpql.tools.spi.IMapping IMapping}.
	 *
	 * @param identificationVariable The identification variable that is defined in the <code>FROM</code>
	 * clause of the query (which can be in the current subquery) or in one of the parent queries.
	 * @param name The name of the persistent field to retrieve
	 * @return The persistence field with the given name or <code>null</code> if it could not be found
	 */
	Object resolveMapping(String identificationVariable, String name);
}