| /* |
| * 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.internal.jpa.jpql; |
| |
| import java.util.Collections; |
| import java.util.LinkedList; |
| import java.util.List; |
| import org.eclipse.persistence.config.PersistenceUnitProperties; |
| import org.eclipse.persistence.dynamic.DynamicClassLoader; |
| import org.eclipse.persistence.jpa.jpql.parser.AbstractEclipseLinkExpressionVisitor; |
| import org.eclipse.persistence.jpa.jpql.parser.AbstractPathExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.CollectionExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.CollectionValuedPathExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.ConstructorExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.JPQLExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.JPQLGrammar; |
| import org.eclipse.persistence.jpa.jpql.parser.SelectClause; |
| import org.eclipse.persistence.jpa.jpql.parser.SelectStatement; |
| import org.eclipse.persistence.jpa.jpql.parser.StateFieldPathExpression; |
| import org.eclipse.persistence.mappings.DatabaseMapping; |
| import org.eclipse.persistence.queries.DatabaseQuery; |
| |
| /** |
| * This object scans a {@link DatabaseQuery} and checks if it's a constructor query. If it is one, |
| * then the fully qualified class name and the persistent fields will be available. |
| * |
| * @version 2.5 |
| * @since 2.5 |
| * @author Pascal Filion |
| */ |
| public final class ConstructorQueryMappings { |
| |
| /** |
| * The fully qualified class name defined in the constructor expression if one is defined, |
| * otherwise <code>null</code>. |
| */ |
| private String className; |
| |
| /** |
| * The list of {@link DatabaseMapping} representing the parameter arguments if the JPQL query |
| * does contain a constructor expression. |
| */ |
| private List<DatabaseMapping> mappings; |
| |
| /** |
| * The {@link DatabaseQuery} for which its JPQL query was scanned to check if it's constructor |
| * query and if so, to gather the information. |
| */ |
| private DatabaseQuery query; |
| |
| /** |
| * Creates a new <code>ConstructorQueryMappings</code>. |
| * |
| * @param query The {@link DatabaseQuery} for which its JPQL query was scanned to check if it's |
| * constructor query and if so, to gather the information. |
| */ |
| ConstructorQueryMappings(DatabaseQuery query) { |
| super(); |
| this.query = query; |
| this.mappings = new LinkedList<>(); |
| } |
| |
| /** |
| * Returns the class name used to define the constructor expression. |
| * |
| * @return Either the fully qualified class name used in the constructor expression or |
| * <code>null</code> if the query is not a constructor query |
| */ |
| public String getClassName() { |
| return className; |
| } |
| |
| /** |
| * Returns the {@link DatabaseQuery} for which its JPQL query was scanned to check if it's |
| * constructor query and if so, to gather the information. |
| * |
| * @return The {@link DatabaseQuery} that was scanned |
| */ |
| public DatabaseQuery getQuery() { |
| return query; |
| } |
| |
| /** |
| * Determines whether the JPQL query is a constructor query, i.e. the select expression is a |
| * constructor expression. |
| * <p> |
| * Example: <code>SELECT new test.example.Employee(e.name, e.id) FROM Employee e</code> |
| * |
| * @return <code>true</code> if the <code><b>SELECT</b></code> clause has a single select item |
| * and it's a constructor expression; <code>false</code> otherwise |
| */ |
| public boolean isConstructorQuery() { |
| return className != null; |
| } |
| |
| /** |
| * Returns a non-<code>null</code> {@link Iterable} over the ordered list of {@link DatabaseMapping} |
| * objects that represents the parameter arguments defined in the constructor expression. |
| * |
| * @return The list contains the persistent fields that were passed to the constructor |
| */ |
| public Iterable<DatabaseMapping> mappings() { |
| |
| if (mappings.isEmpty()) { |
| return Collections.emptyList(); |
| } |
| |
| return new LinkedList<>(mappings); |
| } |
| |
| /** |
| * Populates this object by scanning the JPQL query using the given JPQL grammar. |
| * |
| * @param jpqlGrammar The {@link JPQLGrammar} how the JPQL query is parsed |
| */ |
| void populate(JPQLGrammar jpqlGrammar) { |
| |
| JPQLQueryContext queryContext = new JPQLQueryContext(query, jpqlGrammar); |
| |
| ConstructorVisitor visitor = new ConstructorVisitor(queryContext); |
| queryContext.getJPQLExpression().accept(visitor); |
| } |
| |
| /** |
| * <p> |
| * This visitor visits the constructor items and adds the attribute type mapped to the name. |
| * </p> |
| * <p>Example:</p> |
| * <p>e.name -{@literal >} "name" : String</p> |
| * <p>e.address.zipcode -{@literal >} "zipcode" : int</p> |
| */ |
| private class ConstructorItemVisitor extends AbstractEclipseLinkExpressionVisitor { |
| |
| private final JPQLQueryContext queryContext; |
| |
| ConstructorItemVisitor(JPQLQueryContext queryContext) { |
| super(); |
| this.queryContext = queryContext; |
| } |
| |
| @Override |
| public void visit(CollectionExpression expression) { |
| expression.acceptChildren(this); |
| } |
| |
| @Override |
| public void visit(CollectionValuedPathExpression expression) { |
| visitPathExpression(expression); |
| } |
| |
| @Override |
| public void visit(StateFieldPathExpression expression) { |
| visitPathExpression(expression); |
| } |
| |
| private void visitPathExpression(AbstractPathExpression expression) { |
| |
| DatabaseMapping mapping = queryContext.resolveMapping(expression); |
| |
| if (mapping != null) { |
| mappings.add(mapping); |
| } |
| } |
| } |
| |
| /** |
| * Visits the JPQL query and gather the information contained in the constructor expressions. |
| */ |
| private class ConstructorVisitor extends AbstractEclipseLinkExpressionVisitor { |
| |
| private final JPQLQueryContext queryContext; |
| |
| ConstructorVisitor(JPQLQueryContext queryContext) { |
| super(); |
| this.queryContext = queryContext; |
| } |
| |
| private DynamicClassLoader classLoader() { |
| return (DynamicClassLoader) queryContext.getSession().getProperty(PersistenceUnitProperties.CLASSLOADER); |
| } |
| |
| @Override |
| public void visit(ConstructorExpression expression) { |
| |
| ConstructorQueryMappings.this.className = expression.getClassName(); |
| |
| // Visit the constructor items and retrieve the persistent fields |
| ConstructorItemVisitor visitor = new ConstructorItemVisitor(queryContext); |
| expression.getConstructorItems().accept(visitor); |
| } |
| |
| @Override |
| public void visit(JPQLExpression expression) { |
| expression.getQueryStatement().accept(this); |
| } |
| |
| @Override |
| public void visit(SelectClause expression) { |
| expression.getSelectExpression().accept(this); |
| } |
| |
| @Override |
| public void visit(SelectStatement expression) { |
| expression.getSelectClause().accept(this); |
| } |
| } |
| } |