blob: 73a8d141dd3da63d549088c50bb6afabe8d13b34 [file] [log] [blame]
/*
* Copyright (c) 2011, 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.model;
import org.eclipse.persistence.jpa.jpql.parser.*;
import org.eclipse.persistence.jpa.jpql.tools.model.query.JPQLQueryStateObject;
import org.eclipse.persistence.jpa.jpql.tools.model.query.StateObject;
import org.eclipse.persistence.jpa.jpql.tools.spi.IManagedTypeProvider;
/**
* An abstract implementation of {@link IJPQLQueryBuilder} that parses a JPQL query or any JPQL
* fragments and creates the {@link StateObject} representation by delegating the creation to an
* instance of {@link BasicStateObjectBuilder}.
*
* @see DefaultJPQLQueryBuilder
* @see EclipseLinkJPQLQueryBuilder
*
* @version 2.4
* @since 2.4
* @author Pascal Filion
*/
public abstract class AbstractJPQLQueryBuilder implements IJPQLQueryBuilder {
/**
* Keeps a reference for future use.
*/
private BasicStateObjectBuilder builder;
/**
* Creates a new <code>AbstractJPQLQueryBuilder</code>.
*/
protected AbstractJPQLQueryBuilder() {
super();
}
@Override
public ICaseExpressionStateObjectBuilder buildCaseExpressionStateObjectBuilder(StateObject parent) {
return new DefaultCaseExpressionStateObjectBuilder(parent);
}
@Override
public JPQLQueryStateObject buildStateObject(IManagedTypeProvider provider,
CharSequence jpqlQuery,
boolean tolerant) {
return buildStateObject(provider, jpqlQuery, JPQLStatementBNF.ID, tolerant);
}
@Override
public JPQLQueryStateObject buildStateObject(IManagedTypeProvider provider,
CharSequence jpqlQuery,
String queryBNFId,
boolean tolerant) {
// First parse the JPQL query
JPQLExpression jpqlExpression = parse(jpqlQuery, getGrammar(), tolerant);
// Now visit the parsed expression and create the state object hierarchy
BasicStateObjectBuilder builder = getStateObjectBuilder();
try {
builder.jpqlQueryBuilder = this;
builder.managedTypeProvider = provider;
jpqlExpression.accept(wrap(builder));
return builder.parent;
}
finally {
builder.jpqlQueryBuilder = null;
builder.managedTypeProvider = null;
builder.parent = null;
builder.stateObject = null;
}
}
@Override
public StateObject buildStateObject(StateObject parent,
CharSequence jpqlFragment,
String queryBNFId) {
// First parse the JPQL fragment
JPQLExpression jpqlExpression = parse(jpqlFragment, parent.getGrammar(), queryBNFId);
// We keep track of the old stuff because during the creation of a JPQLQueryStateObject,
// it is possible the state model uses this builder to create some objects
BasicStateObjectBuilder builder = getStateObjectBuilder();
IJPQLQueryBuilder oldBuilder = builder.jpqlQueryBuilder;
IManagedTypeProvider oldProvider = builder.managedTypeProvider;
JPQLQueryStateObject oldParent = builder.parent;
StateObject oldStateObject = builder.stateObject;
try {
builder.jpqlQueryBuilder = this;
builder.managedTypeProvider = parent.getManagedTypeProvider();
builder.parent = parent.getRoot();
builder.stateObject = parent;
// Visit the Expression for which a StateObject is needed
jpqlExpression.getQueryStatement().accept(wrap(builder));
return builder.stateObject;
}
finally {
builder.jpqlQueryBuilder = oldBuilder;
builder.managedTypeProvider = oldProvider;
builder.parent = oldParent;
builder.stateObject = oldStateObject;
}
}
/**
* Creates the builder that creates the {@link StateObject} for each {@link org.eclipse.persistence.jpa.jpql.parser.Expression Expression}.
*
* @return The builder that will be visiting the {@link org.eclipse.persistence.jpa.jpql.parser.Expression Expression}
*/
protected abstract BasicStateObjectBuilder buildStateObjectBuilder();
/**
* Returns the builder that creates the {@link StateObject} for each {@link org.eclipse.persistence.jpa.jpql.parser.Expression Expression}.
*
* @return The builder that will be visiting the {@link org.eclipse.persistence.jpa.jpql.parser.Expression Expression}
*/
protected final BasicStateObjectBuilder getStateObjectBuilder() {
if (builder == null) {
builder = buildStateObjectBuilder();
}
return builder;
}
/**
* Parses the given JPQL query with tolerant mode turned on.
*
* @param jpqlQuery The string representation of the JPQL query to parse
* @param jpqlGrammar The JPQL grammar that defines how to parse a JPQL query
* @param tolerant Determines if the parsing system should be tolerant, meaning if it should try
* to parse invalid or incomplete queries
* @return The root of the parsed tree representation of the JPQL query
*/
protected JPQLExpression parse(CharSequence jpqlQuery, JPQLGrammar jpqlGrammar, boolean tolerant) {
return new JPQLExpression(jpqlQuery, jpqlGrammar, tolerant);
}
/**
* Parses the given JPQL fragment with tolerant mode turned on.
*
* @param jpqFragment The string representation of the portion of a JPQL query to parse
* @param jpqlGrammar The JPQL grammar that defines how to parse a JPQL query
* @param queryBNFId The unique identifier of the {@link JPQLQueryBNF}
* @return The root of the parsed tree representation of the JPQL fragment
*/
protected JPQLExpression parse(CharSequence jpqFragment,
JPQLGrammar jpqlGrammar,
String queryBNFId) {
return new JPQLExpression(jpqFragment, jpqlGrammar, queryBNFId, true);
}
/**
* If a subclass needs to wrap the given {@link BasicStateObjectBuilder} with another visitor can
* do so by simply overriding this method. {@link #buildStateObjectBuilder()} can't be easily
* overridden with an {@link ExpressionVisitor} due to the constraint on the return type.
*
* @param builder The builder to wrap
* @return By default the given builder is returned
*/
protected ExpressionVisitor wrap(BasicStateObjectBuilder builder) {
return builder;
}
}