blob: 4748806582b7c77c503b58a6085219fcb3e3d20b [file] [log] [blame]
/*
* Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021 IBM Corporation. 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;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.eclipse.persistence.jpa.jpql.Assert;
import org.eclipse.persistence.jpa.jpql.ExpressionTools;
import org.eclipse.persistence.jpa.jpql.ITypeHelper;
import org.eclipse.persistence.jpa.jpql.JPQLQueryDeclaration;
import org.eclipse.persistence.jpa.jpql.SemanticValidatorHelper;
import org.eclipse.persistence.jpa.jpql.parser.AbstractExpressionVisitor;
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.Join;
import org.eclipse.persistence.jpa.jpql.parser.SimpleSelectStatement;
import org.eclipse.persistence.jpa.jpql.tools.resolver.Declaration;
import org.eclipse.persistence.jpa.jpql.tools.resolver.DeclarationResolver;
import org.eclipse.persistence.jpa.jpql.tools.resolver.Resolver;
import org.eclipse.persistence.jpa.jpql.tools.resolver.StateFieldResolver;
import org.eclipse.persistence.jpa.jpql.tools.spi.IConstructor;
import org.eclipse.persistence.jpa.jpql.tools.spi.IEntity;
import org.eclipse.persistence.jpa.jpql.tools.spi.IManagedType;
import org.eclipse.persistence.jpa.jpql.tools.spi.IMapping;
import org.eclipse.persistence.jpa.jpql.tools.spi.IType;
import org.eclipse.persistence.jpa.jpql.tools.spi.ITypeDeclaration;
import org.eclipse.persistence.jpa.jpql.utility.CollectionTools;
/**
* An implementation of {@link SemanticValidatorHelper} that uses {@link JPQLQueryContext} to return
* the required information and 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
*/
@SuppressWarnings("nls")
public class GenericSemanticValidatorHelper implements SemanticValidatorHelper {
private IdentificationVariableVisitor identificationVariableVisitor;
/**
* The context used to query information about the JPQL query.
*/
private final JPQLQueryContext queryContext;
/**
* The concrete instance of {@link ITypeHelper} that simply wraps {@link TypeHelper}.
*/
private ITypeHelper typeHelper;
/**
* Creates a new <code>GenericSemanticValidatorHelper</code>.
*
* @param queryContext The context used to query information about the JPQL query
* @exception NullPointerException The given {@link JPQLQueryContext} cannot be <code>null</code>
*/
public GenericSemanticValidatorHelper(JPQLQueryContext queryContext) {
super();
Assert.isNotNull(queryContext, "The JPQLQueryContext cannot be null");
this.queryContext = queryContext;
}
protected void addIdentificationVariable(IdentificationVariable identificationVariable,
Map<String, List<IdentificationVariable>> identificationVariables) {
String variableName = (identificationVariable != null) ? identificationVariable.getVariableName() : null;
if (ExpressionTools.stringIsNotEmpty(variableName)) {
// Add the IdentificationVariable to the list
List<IdentificationVariable> variables = identificationVariables.get(variableName);
if (variables == null) {
variables = new ArrayList<IdentificationVariable>();
identificationVariables.put(variableName, variables);
}
variables.add(identificationVariable);
}
}
protected IdentificationVariableVisitor buildIdentificationVariableVisitor() {
return new IdentificationVariableVisitor();
}
@Override
public void collectAllDeclarationIdentificationVariables(Map<String, List<IdentificationVariable>> identificationVariables) {
JPQLQueryContext currentContext = queryContext.getCurrentContext();
while (currentContext != null) {
collectLocalDeclarationIdentificationVariables(currentContext, identificationVariables);
currentContext = currentContext.getParent();
}
}
protected void collectLocalDeclarationIdentificationVariables(JPQLQueryContext queryContext,
Map<String, List<IdentificationVariable>> identificationVariables) {
DeclarationResolver declarationResolver = queryContext.getDeclarationResolverImp();
for (Declaration declaration : declarationResolver.getDeclarations()) {
// Register the identification variable from the base expression
IdentificationVariable identificationVariable = declaration.getIdentificationVariable();
addIdentificationVariable(identificationVariable, identificationVariables);
// Register the identification variable from the JOIN expressions
for (Join join : declaration.getJoins()) {
IdentificationVariable joinIdentificationVariable = getIdentificationVariable(join.getIdentificationVariable());
addIdentificationVariable(joinIdentificationVariable, identificationVariables);
}
}
if (queryContext.getParent() == null) {
for (IdentificationVariable identificationVariable : declarationResolver.getResultVariablesMap().keySet()) {
addIdentificationVariable(identificationVariable, identificationVariables);
}
}
}
@Override
public void collectLocalDeclarationIdentificationVariables(Map<String, List<IdentificationVariable>> identificationVariables) {
collectLocalDeclarationIdentificationVariables(queryContext.getCurrentContext(), identificationVariables);
}
@Override
public void disposeSubqueryContext() {
queryContext.disposeSubqueryContext();
}
@Override
public String[] entityNames() {
List<String> names = new ArrayList<String>();
for (IEntity entity : queryContext.getProvider().entities()) {
names.add(entity.getName());
}
return names.toArray(new String[names.size()]);
}
@Override
public List<JPQLQueryDeclaration> getAllDeclarations() {
List<JPQLQueryDeclaration> declarations = new ArrayList<JPQLQueryDeclaration>();
JPQLQueryContext context = queryContext.getCurrentContext();
while (context != null) {
declarations.addAll(context.getDeclarationResolverImp().getDeclarations());
context = context.getParent();
}
return declarations;
}
@Override
public IConstructor[] getConstructors(Object type) {
return CollectionTools.array(IConstructor.class, ((IType) type).constructors());
}
@Override
@SuppressWarnings({ "rawtypes", "unchecked" })
public List getDeclarations() {
return queryContext.getDeclarations();
}
@Override
public IManagedType getEmbeddable(Object type) {
return queryContext.getProvider().getEmbeddable((IType) type);
}
@Override
public IEntity getEntityNamed(String entityName) {
return queryContext.getProvider().getEntityNamed(entityName);
}
@Override
public String[] getEnumConstants(Object type) {
return ((IType) type).getEnumConstants();
}
@Override
public JPQLGrammar getGrammar() {
return queryContext.getGrammar();
}
protected IdentificationVariable getIdentificationVariable(Expression expression) {
IdentificationVariableVisitor visitor = getIdentificationVariableVisitor();
expression.accept(visitor);
try {
return visitor.identificationVariable;
}
finally {
visitor.identificationVariable = null;
}
}
protected IdentificationVariableVisitor getIdentificationVariableVisitor() {
if (identificationVariableVisitor == null) {
identificationVariableVisitor = buildIdentificationVariableVisitor();
}
return identificationVariableVisitor;
}
@Override
public IManagedType getManagedType(Expression expression) {
return queryContext.getResolver(expression).getManagedType();
}
@Override
public IMapping getMappingNamed(Object managedType, String path) {
return ((IManagedType) managedType).getMappingNamed(path);
}
@Override
public IType getMappingType(Object mapping) {
return (mapping != null) ? ((IMapping) mapping).getType() : queryContext.getTypeHelper().unknownType();
}
@Override
public ITypeDeclaration[] getMethodParameterTypeDeclarations(Object constructor) {
return ((IConstructor) constructor).getParameterTypes();
}
/**
* Returns the context used to query information about the JPQL query.
*
* @return The context used to query information about the JPQL query
*/
public JPQLQueryContext getQueryContext() {
return queryContext;
}
@Override
public IManagedType getReferenceManagedType(Object relationshipMapping) {
if (relationshipMapping == null) {
return null;
}
IMapping mapping = (IMapping) relationshipMapping;
return mapping.getParent().getProvider().getManagedType(mapping.getType());
}
@Override
public IType getType(Expression expression) {
return queryContext.getType(expression);
}
@Override
public IType getType(Object typeDeclaration) {
return ((ITypeDeclaration) typeDeclaration).getType();
}
@Override
public IType getType(String typeName) {
return queryContext.getType(typeName);
}
@Override
public ITypeDeclaration getTypeDeclaration(Expression expression) {
return queryContext.getTypeDeclaration(expression);
}
@Override
public ITypeHelper getTypeHelper() {
if (typeHelper == null) {
typeHelper = new GenericTypeHelper(queryContext.getTypeHelper());
}
return typeHelper;
}
@Override
public String getTypeName(Object type) {
return ((IType) type).getName();
}
@Override
public boolean isAssignableTo(Object type1, Object type2) {
return ((IType) type1).isAssignableTo((IType) type2) ;
}
@Override
public boolean isCollectionIdentificationVariable(String variableName) {
return queryContext.isCollectionIdentificationVariable(variableName);
}
@Override
public boolean isCollectionMapping(Object mapping) {
return (mapping != null) && ((IMapping) mapping).isCollection();
}
@Override
public boolean isEmbeddableMapping(Object mapping) {
return (mapping != null) && ((IMapping) mapping).isEmbeddable();
}
@Override
public boolean isEnumType(Object type) {
return ((IType) type).isEnum();
}
@Override
public boolean isIdentificationVariableValidInComparison(IdentificationVariable expression) {
return false;
}
@Override
public boolean isManagedTypeResolvable(Object managedType) {
return ((IManagedType) managedType).getType().isResolvable();
}
@Override
public boolean isPropertyMapping(Object mapping) {
return (mapping != null) && ((IMapping) mapping).isProperty();
}
@Override
public boolean isRelationshipMapping(Object mapping) {
return (mapping != null) && ((IMapping) mapping).isRelationship();
}
@Override
public boolean isResultVariable(String variableName) {
return queryContext.isResultVariable(variableName);
}
@Override
public boolean isTransient(Object mapping) {
return (mapping != null) && ((IMapping) mapping).isTransient();
}
@Override
public boolean isTypeDeclarationAssignableTo(Object typeDeclaration1, Object typeDeclaration2) {
ITypeDeclaration declaration1 = (ITypeDeclaration) typeDeclaration1;
ITypeDeclaration declaration2 = (ITypeDeclaration) typeDeclaration2;
// One is an array but not the other one
if (declaration1.isArray() && !declaration2.isArray() ||
!declaration1.isArray() && declaration2.isArray()) {
return false;
}
// Check the array dimensionality
if (declaration1.isArray()) {
return declaration1.getDimensionality() == declaration2.getDimensionality();
}
return isAssignableTo(declaration1.getType(), declaration2.getType());
}
@Override
public boolean isTypeResolvable(Object type) {
return ((IType) type).isResolvable();
}
@Override
public void newSubqueryContext(SimpleSelectStatement expression) {
queryContext.newSubqueryContext(expression);
}
@Override
public IMapping resolveMapping(Expression expression) {
return queryContext.getResolver(expression).getMapping();
}
@Override
public IMapping resolveMapping(String variableName, String name) {
Resolver parent = queryContext.getResolver(variableName);
Resolver resolver = parent.getChild(name);
if (resolver == null) {
resolver = new StateFieldResolver(parent, name);
parent.addChild(variableName, resolver);
}
return resolver.getMapping();
}
protected static class IdentificationVariableVisitor extends AbstractExpressionVisitor {
/**
*
*/
protected IdentificationVariable identificationVariable;
@Override
public void visit(IdentificationVariable expression) {
identificationVariable = expression;
}
}
}