| /* |
| * 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.internal.jpa.jpql; |
| |
| import java.lang.reflect.Constructor; |
| import java.lang.reflect.Type; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.Map; |
| import org.eclipse.persistence.descriptors.ClassDescriptor; |
| 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.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; |
| import org.eclipse.persistence.mappings.DatabaseMapping; |
| import org.eclipse.persistence.mappings.querykeys.QueryKey; |
| |
| /** |
| * The EclipseLink implementation of {@link SemanticValidatorHelper}, which directly accesses |
| * EclipseLink objects without using Hermes SPI. |
| * |
| * @version 2.5 |
| * @since 2.4 |
| * @author Pascal Filion |
| */ |
| final class EclipseLinkSemanticValidatorHelper implements SemanticValidatorHelper { |
| |
| /** |
| * The context used to query information about the JPQL query. |
| */ |
| private final JPQLQueryContext queryContext; |
| |
| /** |
| * Creates a new <code>EclipseLinkSemanticValidatorHelper</code>. |
| * |
| * @param queryContext The context used to query information about the JPQL query |
| */ |
| EclipseLinkSemanticValidatorHelper(JPQLQueryContext queryContext) { |
| super(); |
| this.queryContext = queryContext; |
| } |
| |
| private 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<>(); |
| identificationVariables.put(variableName, variables); |
| } |
| |
| variables.add(identificationVariable); |
| } |
| } |
| |
| @Override |
| public void collectAllDeclarationIdentificationVariables(Map<String, List<IdentificationVariable>> identificationVariables) { |
| |
| JPQLQueryContext currentContext = queryContext.getCurrentContext(); |
| |
| while (currentContext != null) { |
| collectLocalDeclarationIdentificationVariables(currentContext, identificationVariables); |
| currentContext = currentContext.getActualParent(); |
| } |
| } |
| |
| private void collectLocalDeclarationIdentificationVariables(JPQLQueryContext queryContext, |
| Map<String, List<IdentificationVariable>> identificationVariables) { |
| |
| DeclarationResolver declarationResolver = queryContext.getDeclarationResolverImp(); |
| |
| // Collect the identification variables from the declarations |
| for (Declaration declaration : declarationResolver.getDeclarations()) { |
| IdentificationVariable identificationVariable = declaration.identificationVariable; |
| addIdentificationVariable(identificationVariable, identificationVariables); |
| } |
| |
| // Collect the result variables |
| for (IdentificationVariable identificationVariable : declarationResolver.getResultVariables()) { |
| addIdentificationVariable(identificationVariable, identificationVariables); |
| } |
| } |
| |
| @Override |
| public void collectLocalDeclarationIdentificationVariables(Map<String, List<IdentificationVariable>> identificationVariables) { |
| collectLocalDeclarationIdentificationVariables(queryContext, identificationVariables); |
| } |
| |
| @Override |
| public void disposeSubqueryContext() { |
| queryContext.disposeSubqueryContext(); |
| } |
| |
| @Override |
| public String[] entityNames() { |
| |
| List<String> names = new ArrayList<>(); |
| |
| for (ClassDescriptor descriptor : queryContext.getSession().getDescriptors().values()) { |
| if (!descriptor.isAggregateDescriptor()) { |
| String name = descriptor.getAlias(); |
| if (ExpressionTools.stringIsEmpty(name)) { |
| name = descriptor.getJavaClass().getSimpleName(); |
| } |
| names.add(name); |
| } |
| } |
| |
| return names.toArray(new String[names.size()]); |
| } |
| |
| @Override |
| public List<JPQLQueryDeclaration> getAllDeclarations() { |
| |
| List<JPQLQueryDeclaration> declarations = new ArrayList<>(); |
| JPQLQueryContext context = queryContext.getCurrentContext(); |
| |
| while (context != null) { |
| declarations.addAll(context.getDeclarationResolverImp().getDeclarations()); |
| context = context.getActualParent(); |
| } |
| |
| return declarations; |
| } |
| |
| @Override |
| public Object[] getConstructors(Object type) { |
| return (type != null) ? ((Class<?>) type).getDeclaredConstructors() : ExpressionTools.EMPTY_ARRAY; |
| } |
| |
| @Override |
| @SuppressWarnings({ "rawtypes", "unchecked" }) |
| public List getDeclarations() { |
| return queryContext.getDeclarations(); |
| } |
| |
| @Override |
| public ClassDescriptor getEmbeddable(Object type) { |
| |
| ClassDescriptor descriptor = queryContext.getDescriptor((Class<?>) type); |
| |
| if ((descriptor != null) && descriptor.isAggregateDescriptor()) { |
| return descriptor; |
| } |
| |
| return null; |
| } |
| |
| @Override |
| public ClassDescriptor getEntityNamed(String entityName) { |
| return queryContext.getDescriptor(entityName); |
| } |
| |
| @Override |
| @SuppressWarnings("unchecked") |
| public String[] getEnumConstants(Object type) { |
| |
| Enum<?>[] constants = ((Class<Enum<?>>) type).getEnumConstants(); |
| String[] names = new String[constants.length]; |
| |
| for (int index = constants.length; --index >= 0; ) { |
| names[index] = constants[index].name(); |
| } |
| |
| return names; |
| } |
| |
| @Override |
| public JPQLGrammar getGrammar() { |
| return queryContext.getGrammar(); |
| } |
| |
| @Override |
| public Object getManagedType(Expression expression) { |
| return queryContext.resolveDescriptor(expression); |
| } |
| |
| @Override |
| public Object getReferenceManagedType(Object mapping) { |
| |
| if (mapping == null) { |
| return null; |
| } |
| |
| return ((DatabaseMapping) mapping).getReferenceDescriptor(); |
| } |
| |
| @Override |
| public Object getMappingNamed(Object entity, String name) { |
| |
| ClassDescriptor descriptor = (ClassDescriptor) entity; |
| Object mapping = descriptor.getObjectBuilder().getMappingForAttributeName(name); |
| |
| if (mapping == null) { |
| mapping = descriptor.getQueryKeyNamed(name); |
| } |
| |
| return mapping; |
| } |
| |
| @Override |
| public Class<?> getMappingType(Object mapping) { |
| |
| if (mapping == null) { |
| return null; |
| } |
| |
| try { |
| // We do a try/catch, it should be faster than doing an instance of |
| // since a QueryKey is used a lot less than a DatabaseMapping |
| return queryContext.calculateMappingType((DatabaseMapping) mapping); |
| } |
| catch (ClassCastException e) { |
| return queryContext.calculateQueryKeyType((QueryKey) mapping); |
| } |
| } |
| |
| @Override |
| public Type[] getMethodParameterTypeDeclarations(Object constructor) { |
| return ((Constructor<?>) constructor).getGenericParameterTypes(); |
| } |
| |
| @Override |
| public Class<?> getType(Expression expression) { |
| return queryContext.getType(expression); |
| } |
| |
| @Override |
| public Object getType(Object typeDeclaration) { |
| // Not used |
| return null; |
| } |
| |
| @Override |
| public Class<?> getType(String className) { |
| return queryContext.getType(className); |
| } |
| |
| @Override |
| public Object getTypeDeclaration(Expression expression) { |
| // Not used |
| return null; |
| } |
| |
| @Override |
| public ITypeHelper getTypeHelper() { |
| // Not used |
| return null; |
| } |
| |
| @Override |
| public String getTypeName(Object type) { |
| return ((Class<?>) type).getName(); |
| } |
| |
| @Override |
| public boolean isAssignableTo(Object type1, Object type2) { |
| return ((Class<?>) type2).isAssignableFrom((Class<?>) type1) ; |
| } |
| |
| @Override |
| public boolean isCollectionIdentificationVariable(String variableName) { |
| return queryContext.isCollectionIdentificationVariable(variableName); |
| } |
| |
| @Override |
| public boolean isCollectionMapping(Object mapping) { |
| |
| if (mapping == null) { |
| return false; |
| } |
| |
| try { |
| // We do a try/catch, it should be faster than doing an instance of |
| // since a QueryKey is used a lot less than a DatabaseMapping |
| return ((DatabaseMapping) mapping).isCollectionMapping(); |
| } |
| catch (ClassCastException e) { |
| return ((QueryKey) mapping).isCollectionQueryKey(); |
| } |
| } |
| |
| @Override |
| public boolean isEmbeddableMapping(Object mapping) { |
| return (mapping instanceof org.eclipse.persistence.mappings.EmbeddableMapping); |
| } |
| |
| @Override |
| public boolean isEnumType(Object type) { |
| return (type != null) && ((Class<?>) type).isEnum(); |
| } |
| |
| @Override |
| public boolean isIdentificationVariableValidInComparison(IdentificationVariable expression) { |
| |
| Declaration declaration = queryContext.findDeclaration(expression.getVariableName()); |
| |
| if (declaration == null) { |
| return false; |
| } |
| |
| DatabaseMapping mapping = declaration.getMapping(); |
| |
| if (mapping == null) { |
| return false; |
| } |
| |
| // Direct collection is not an object so it's valid |
| return mapping.isDirectCollectionMapping() || |
| mapping.isAbstractCompositeDirectCollectionMapping(); |
| } |
| |
| @Override |
| public boolean isManagedTypeResolvable(Object managedType) { |
| return managedType != null; |
| } |
| |
| @Override |
| public boolean isPropertyMapping(Object mapping) { |
| |
| if (mapping == null) { |
| return false; |
| } |
| |
| try { |
| // We do a try/catch, it should be faster than doing an instance of |
| // since a QueryKey is used a lot less than a DatabaseMapping |
| return ((DatabaseMapping) mapping).isDirectToFieldMapping(); |
| } |
| catch (ClassCastException e) { |
| return ((QueryKey) mapping).isDirectQueryKey(); |
| } |
| } |
| |
| @Override |
| public boolean isRelationshipMapping(Object mapping) { |
| |
| if (mapping == null) { |
| return false; |
| } |
| |
| if (mapping instanceof DatabaseMapping) { |
| |
| DatabaseMapping databaseMapping = (DatabaseMapping) mapping; |
| |
| return databaseMapping.isForeignReferenceMapping() || |
| databaseMapping.isAbstractCompositeCollectionMapping() || |
| databaseMapping.isAbstractCompositeDirectCollectionMapping(); |
| } |
| |
| return ((QueryKey) mapping).isForeignReferenceQueryKey(); |
| } |
| |
| @Override |
| public boolean isResultVariable(String variableName) { |
| return queryContext.isResultVariable(variableName); |
| } |
| |
| @Override |
| public boolean isTransient(Object mapping) { |
| return mapping == null; |
| } |
| |
| @Override |
| public boolean isTypeDeclarationAssignableTo(Object typeDeclaration1, Object typeDeclaration2) { |
| // Not used |
| return false; |
| } |
| |
| @Override |
| public boolean isTypeResolvable(Object type) { |
| return type != null; |
| } |
| |
| @Override |
| public void newSubqueryContext(SimpleSelectStatement expression) { |
| queryContext.newSubQueryContext(expression, null); |
| } |
| |
| @Override |
| public Object resolveMapping(Expression expression) { |
| return queryContext.resolveMappingObject(expression); |
| } |
| |
| @Override |
| public Object resolveMapping(String variableName, String name) { |
| |
| // Find the declaration associated with the identification variable |
| Declaration declaration = queryContext.findDeclaration(variableName); |
| |
| if (declaration == null) { |
| return null; |
| } |
| |
| // Retrieve the associated descriptor |
| ClassDescriptor descriptor = declaration.getDescriptor(); |
| |
| if (descriptor == null) { |
| return null; |
| } |
| |
| // Now, retrieve the mapping |
| Object mapping = descriptor.getObjectBuilder().getMappingForAttributeName(name); |
| |
| // No mapping was found, look for a query key |
| if (mapping == null) { |
| mapping = descriptor.getQueryKeyNamed(name); |
| } |
| |
| return mapping; |
| } |
| } |