blob: d95811595848d3f001780f080b4a115b65560e0c [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.query;
import java.io.IOException;
import java.util.List;
import org.eclipse.persistence.jpa.jpql.Assert;
import org.eclipse.persistence.jpa.jpql.parser.JPQLExpression;
import org.eclipse.persistence.jpa.jpql.parser.JPQLGrammar;
import org.eclipse.persistence.jpa.jpql.tools.model.IJPQLQueryBuilder;
import org.eclipse.persistence.jpa.jpql.tools.spi.IManagedTypeProvider;
/**
* This is the root of the {@link StateObject} hierarchy that represents a JPQL query. The only
* child of this state object is one of the three possible query statements.
*
* @see DeleteStatementStateObject
* @see SelectStatementStateObject
* @see UpdateStatementStateObject
* @see org.eclipse.persistence.jpa.jpql.parser.JPQLExpression JPQLExpression
*
* @version 2.4
* @since 2.4
* @author Pascal Filion
*/
@SuppressWarnings("nls")
public class JPQLQueryStateObject extends AbstractStateObject {
/**
* The external form of a provider that gives access to the JPA metadata.
*/
private IManagedTypeProvider provider;
/**
* The builder that can create any {@link StateObject} for any given JPQL fragment.
*/
private IJPQLQueryBuilder queryBuilder;
/**
* One of the possible three statements.
*/
private StateObject queryStatement;
/**
* Notifies the query statement has changed.
*/
public static final String QUERY_STATEMENT_PROPERTY = "statement";
/**
* Creates a new <code>JPQLQueryStateObject</code>.
*
* @param queryBuilder The builder that can create any {@link StateObject} for any given JPQL
* fragment when using a parse method
* @param provider The external form of a provider that gives access to the JPA metadata
* @exception NullPointerException If one of the given arguments is <code>null</code>
*/
public JPQLQueryStateObject(IJPQLQueryBuilder queryBuilder, IManagedTypeProvider provider) {
super(null);
initialize(queryBuilder, provider);
}
@Override
public void accept(StateObjectVisitor visitor) {
visitor.visit(this);
}
@Override
protected void addChildren(List<StateObject> children) {
super.addChildren(children);
if (queryStatement != null) {
children.add(queryStatement);
}
}
/**
* Changes the query statement to be a <code><b>DELETE</b></code> statement.
*
* @return The new root object of the <code><b>DELETE</b></code> statement
*/
public DeleteStatementStateObject addDeleteStatement() {
DeleteStatementStateObject stateObject = new DeleteStatementStateObject(this);
setQueryStatement(stateObject);
return stateObject;
}
/**
* Changes the query statement to be a <code><b>SELECT</b></code> statement. The <code><b>SELECT</b></code>
* clause will have the <code><b>DISTINCT</b></code> identifier set.
*
* @return The new root object of the <code><b>SELECT</b></code> statement
*/
public SelectStatementStateObject addDistinctSelectStatement() {
SelectStatementStateObject stateObject = new SelectStatementStateObject(this);
stateObject.getSelectClause().toggleDistinct();
setQueryStatement(stateObject);
return stateObject;
}
/**
* Changes the query statement to be a <code><b>SELECT</b></code> statement.
*
* @return The new root object of the <code><b>SELECT</b></code> statement
*/
public SelectStatementStateObject addSelectStatement() {
SelectStatementStateObject stateObject = new SelectStatementStateObject(this);
setQueryStatement(stateObject);
return stateObject;
}
/**
* Changes the query statement to be a <code><b>SELECT</b></code> statement.
*
* @param jpqlFragment The portion of the query representing the select items, excluding the
* <code><b>SELECT</b></code> identifier
* @return The new root object of the <code><b>SELECT</b></code> statement
*/
public SelectStatementStateObject addSelectStatement(String jpqlFragment) {
SelectStatementStateObject stateObject = new SelectStatementStateObject(this);
stateObject.getSelectClause().parse(jpqlFragment);
setQueryStatement(stateObject);
return stateObject;
}
/**
* Changes the query statement to be a <code><b>UPDATE</b></code> statement.
*
* @return The new root object of the <code><b>UPDTE</b></code> statement
*/
public UpdateStatementStateObject addUpdateStatement() {
UpdateStatementStateObject stateObject = new UpdateStatementStateObject(this);
setQueryStatement(stateObject);
return stateObject;
}
/**
* Changes the query statement to be a <code><b>UPDATE</b></code> statement.
*
* @param jpqlFragment The portion of the query representing the select items, excluding the
* <code><b>UPDATE</b></code> identifier
* @return The new root object of the <code><b>UPDATE</b></code> statement
*/
public UpdateStatementStateObject addUpdateStatement(String jpqlFragment) {
UpdateStatementStateObject stateObject = new UpdateStatementStateObject(this);
stateObject.getModifyClause().parse(jpqlFragment);
setQueryStatement(stateObject);
return stateObject;
}
@Override
protected StateObject checkParent(StateObject parent) {
return parent;
}
@Override
public DeclarationStateObject getDeclaration() {
return (queryStatement == null) ? null : queryStatement.getDeclaration();
}
@Override
public JPQLExpression getExpression() {
return (JPQLExpression) super.getExpression();
}
@Override
public JPQLGrammar getGrammar() {
return queryBuilder.getGrammar();
}
@Override
public IManagedTypeProvider getManagedTypeProvider() {
return provider;
}
@Override
public StateObject getParent() {
return null;
}
@Override
public IJPQLQueryBuilder getQueryBuilder() {
return queryBuilder;
}
/**
* Returns the only child of this state object, which represents one of the three query statement.
*
* @return The state object representing the query statement, which was created based on the
* query type
*/
public StateObject getQueryStatement() {
return queryStatement;
}
@Override
public JPQLQueryStateObject getRoot() {
return this;
}
/**
* Determines whether a query statement has been defined.
*
* @return <code>true</code> if there is a query statement defined; <code>false</code> otherwise
*/
public boolean hasQueryStatement() {
return queryStatement != null;
}
/**
* Initializes this <code>JPQLQueryStateObject</code>.
*
* @param queryBuilder The builder that can create any {@link StateObject} for any given JPQL fragment
* @param provider The external form that gives access to the JPA metadata
* @exception NullPointerException If one of the given arguments is <code>null</code>
*/
protected void initialize(IJPQLQueryBuilder queryBuilder, IManagedTypeProvider provider) {
Assert.isNotNull(queryBuilder, "IJPQLQueryBuilder cannot be null");
Assert.isNotNull(provider, "IManagedTypeProvider cannot be null");
this.provider = provider;
this.queryBuilder = queryBuilder;
}
@Override
public boolean isEquivalent(StateObject stateObject) {
if (super.isEquivalent(stateObject)) {
JPQLQueryStateObject jpqlStateObject = (JPQLQueryStateObject) stateObject;
return areEquivalent(queryStatement, jpqlStateObject.queryStatement);
}
return false;
}
/**
* Parses the given JPQL fragment as this state object's query statement.
*
* @param jpqlFragment The portion of the query to parse
* @param queryBNFId The unique identifier of the BNF that determines how to parse the fragment
*/
public void parse(CharSequence jpqlFragment, String queryBNFId) {
StateObject stateObject = buildStateObject(jpqlFragment, queryBNFId);
setQueryStatement(stateObject);
}
/**
* Keeps a reference of the {@link JPQLExpression parsed object} object, which should only be
* done when this object is instantiated during the conversion of a parsed JPQL query into
* {@link StateObject StateObjects}.
*
* @param expression The {@link JPQLExpression parsed object} representing the JPQL query
*/
public void setExpression(JPQLExpression expression) {
super.setExpression(expression);
}
/**
* Sets the only child of this state object, which represents one of the three query statement.
*
* @param queryStatement The state object representing the query statement, which was created
* based on the query type
*/
public void setQueryStatement(StateObject queryStatement) {
StateObject oldStatement = this.queryStatement;
this.queryStatement = parent(queryStatement);
firePropertyChanged(QUERY_STATEMENT_PROPERTY, oldStatement, queryStatement);
}
@Override
protected void toTextInternal(Appendable writer) throws IOException {
if (queryStatement != null) {
queryStatement.toString(writer);
}
}
}