| /* |
| * Copyright (c) 2012, 2021 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.resolver; |
| |
| import org.eclipse.persistence.jpa.jpql.BaseDeclarationIdentificationVariableFinder; |
| import org.eclipse.persistence.jpa.jpql.parser.AbstractSchemaName; |
| import org.eclipse.persistence.jpa.jpql.tools.JPQLQueryContext; |
| 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; |
| |
| /** |
| * This {@link Resolver} retrieves the type for an abstract schema name (entity name) if it can |
| * be resolved otherwise a derived path will be assumed. This {@link Resolver} is used within a |
| * subquery and it handles the following two cases. |
| * <p> |
| * The "root" object in the subquery is an unqualified derived path: |
| * <pre><code> UPDATE Employee SET name = 'JPQL' |
| * WHERE (SELECT a FROM addr a)</code></pre> |
| * and |
| * The "root" object in the subquery is an entity: |
| * <pre><code> UPDATE Employee SET name = 'JPQL' |
| * WHERE (SELECT a FROM Address a)</code></pre> |
| * |
| * @version 2.5 |
| * @since 2.5 |
| * @author Pascal Filion |
| */ |
| public class SubqueryEntityResolver extends Resolver { |
| |
| /** |
| * The {@link AbstractSchemaName} holding onto the the name of the entity. |
| */ |
| private AbstractSchemaName abstractSchemaName; |
| |
| /** |
| * The actual {@link Resolver} when the abstract schema name is actually an unqualified derived |
| * path expression. |
| */ |
| private Resolver derivedPathResolver; |
| |
| /** |
| * The name of the entity or the unqualified derived path. |
| */ |
| private String entityName; |
| |
| /** |
| * The {@link IManagedType} with the same abstract schema name. |
| */ |
| private IManagedType managedType; |
| |
| /** |
| * Determines whether the managed type was resolved or not. |
| */ |
| private boolean managedTypeResolved; |
| |
| /** |
| * The {@link JPQLQueryContext} for the subquery. |
| */ |
| private JPQLQueryContext queryContext; |
| |
| /** |
| * Creates a new <code>DerivedPathResolver</code>. |
| * |
| * @param parent The parent {@link Resolver}, which is never <code>null</code> |
| * @param queryContext The {@link JPQLQueryContext} for the subquery |
| * @param abstractSchemaName The {@link AbstractSchemaName} holding onto the the name of the entity |
| */ |
| public SubqueryEntityResolver(Resolver parent, |
| JPQLQueryContext queryContext, |
| AbstractSchemaName abstractSchemaName) { |
| |
| super(parent); |
| this.queryContext = queryContext; |
| this.abstractSchemaName = abstractSchemaName; |
| this.entityName = abstractSchemaName.getText(); |
| } |
| |
| @Override |
| protected IType buildType() { |
| IManagedType entity = getManagedType(); |
| return (entity != null) ? entity.getType() : getTypeHelper().objectType(); |
| } |
| |
| @Override |
| protected ITypeDeclaration buildTypeDeclaration() { |
| return getType().getTypeDeclaration(); |
| } |
| |
| /** |
| * Returns the name of the entity to resolve. |
| * |
| * @return The entity name, which is never <code>null</code> |
| */ |
| public String getAbstractSchemaName() { |
| return entityName; |
| } |
| |
| @Override |
| public IManagedType getManagedType() { |
| |
| if ((managedType == null) && !managedTypeResolved) { |
| |
| managedTypeResolved = true; |
| managedType = getProvider().getEntityNamed(entityName); |
| |
| if (managedType == null) { |
| Resolver resolver = resolveDerivePathResolver(); |
| managedType = resolver.getManagedType(); |
| } |
| } |
| |
| return managedType; |
| } |
| |
| @Override |
| public IMapping getMapping() { |
| |
| if (derivedPathResolver != null) { |
| return derivedPathResolver.getMapping(); |
| } |
| |
| // The managed type cannot be resolved and this is for a subquery, |
| // attempt to resolve an unqualified derived path expression |
| if ((getManagedType() == null) && queryContext.isSubquery()) { |
| Resolver resolver = resolveDerivePathResolver(); |
| return resolver.getMapping(); |
| } |
| |
| return super.getMapping(); |
| } |
| |
| /** |
| * Creates the {@link Resolver} for a unqualified derived path expression. |
| * |
| * @return A non-<code>null</code> {@link Resolver} |
| */ |
| protected Resolver resolveDerivePathResolver() { |
| |
| if (derivedPathResolver == null) { |
| |
| // Retrieve the identification variable of the top-level query's base declaration |
| BaseDeclarationIdentificationVariableFinder finder = new BaseDeclarationIdentificationVariableFinder(); |
| abstractSchemaName.accept(finder); |
| |
| if (finder.expression != null) { |
| |
| // Retrieve the Resolver for the identification variable |
| DeclarationResolver parent = queryContext.getParent().getActualDeclarationResolver(); |
| Resolver resolver = parent.getResolver(finder.expression.getVariableName()); |
| |
| // Retrieve the Resolver for the unqualified path expression |
| Resolver childResolver = resolver.getChild(entityName); |
| |
| if (childResolver == null) { |
| childResolver = new StateFieldResolver(resolver, entityName); |
| } |
| |
| derivedPathResolver = childResolver; |
| } |
| |
| // Invalid query/subquery |
| if (derivedPathResolver == null) { |
| derivedPathResolver = new NullResolver(this); |
| } |
| } |
| |
| return derivedPathResolver; |
| } |
| |
| @Override |
| public String toString() { |
| return entityName; |
| } |
| } |