blob: 493016dd611078beb75d8e34ab58377b769b020e [file] [log] [blame]
/*
* Copyright (c) 2006, 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.ArrayList;
import java.util.List;
import java.util.ListIterator;
import org.eclipse.persistence.jpa.jpql.parser.IdentificationVariableDeclaration;
import org.eclipse.persistence.jpa.jpql.parser.JoinBNF;
import org.eclipse.persistence.jpa.jpql.utility.iterable.ListIterable;
import org.eclipse.persistence.jpa.jpql.utility.iterable.SnapshotCloneListIterable;
import static org.eclipse.persistence.jpa.jpql.parser.AbstractExpression.*;
/**
*
* @version 2.5
* @since 2.4
* @author Pascal Filion
*/
@SuppressWarnings({"nls", "unused"}) // unused used for the import statement: see bug 330740
public abstract class AbstractIdentificationVariableDeclarationStateObject extends AbstractListHolderStateObject<JoinStateObject>
implements VariableDeclarationStateObject {
/**
* The state object of the range variable declaration.
*/
private AbstractRangeVariableDeclarationStateObject rangeVariableDeclaration;
/**
* Notifies the content of the list of {@link JoinStateObject} has changed.
*/
public static final String JOINS_LIST = "joins";
/**
* Creates a new <code>AbstractIdentificationVariableDeclarationStateObject</code>.
*
* @param parent The parent of this state object, which cannot be <code>null</code>
* @exception NullPointerException The given parent cannot be <code>null</code>
*/
protected AbstractIdentificationVariableDeclarationStateObject(AbstractFromClauseStateObject parent) {
super(parent);
}
/**
* Creates a new <code>AbstractIdentificationVariableDeclarationStateObject</code>.
*
* @param parent The parent of this state object, which cannot be <code>null</code>
* @param root The "root" object
* @param identificationVariable The identification variable defining the given path
* @exception NullPointerException The given parent cannot be <code>null</code>
*/
protected AbstractIdentificationVariableDeclarationStateObject(AbstractFromClauseStateObject parent,
String root,
String identificationVariable) {
super(parent);
setRootPath(root);
setIdentificationVariable(identificationVariable);
}
@Override
protected void addChildren(List<StateObject> children) {
children.add(rangeVariableDeclaration);
super.addChildren(children);
}
/**
* Adds a new <code><b>INNER JOIN</b></code> expression to this declaration.
*
* @param path The join association path expression
* @param identificationVariable The new variable defining the join association path
* @return A new {@link JoinStateObject}
*/
public JoinStateObject addInnerJoin(String path, String identificationVariable) {
return addJoin(INNER_JOIN, path, identificationVariable);
}
/**
* Adds a new <code><b>JOIN</b></code> expression to this declaration.
*
* @param joinType One of the joining types: <code><b>LEFT JOIN</b></code>, <code><b>LEFT OUTER
* JOIN</b></code>, <code><b>INNER JOIN</b></code> or <code><b>JOIN</b></code>
* @return A new {@link JoinStateObject}
*/
public JoinStateObject addJoin(String joinType) {
JoinStateObject stateObject = new JoinStateObject(this, joinType, false);
addItem(stateObject);
return stateObject;
}
/**
* Adds a new <code><b>JOIN</b></code> expression to this declaration.
*
* @param joinType One of the joining types: <code><b>LEFT JOIN</b></code>, <code><b>LEFT OUTER
* JOIN</b></code>, <code><b>INNER JOIN</b></code> or <code><b>JOIN</b></code>
* @param paths The join association path expression
* @param identificationVariable The new variable defining the join association path
* @return A new {@link JoinStateObject}
*/
public JoinStateObject addJoin(String joinType, ListIterator<String> paths, String identificationVariable) {
JoinStateObject stateObject = addJoin(joinType);
stateObject.setJoinAssociationPaths(paths);
stateObject.setIdentificationVariable(identificationVariable);
return stateObject;
}
/**
* Adds a new <code><b>JOIN</b></code> expression to this declaration.
*
* @param path The join association path expression
* @param identificationVariable The new variable defining the join association path
* @return A new {@link JoinStateObject}
*/
public JoinStateObject addJoin(String path, String identificationVariable) {
return addJoin(JOIN, path, identificationVariable);
}
/**
* Adds a new <code><b>JOIN</b></code> expression to this declaration.
*
* @param joinType One of the joining types: <code><b>LEFT JOIN</b></code>, <code><b>LEFT OUTER
* JOIN</b></code>, <code><b>INNER JOIN</b></code> or <code><b>JOIN</b></code>
* @param path The join association path expression
* @param identificationVariable The new variable defining the join association path
* @return A new {@link JoinStateObject}
*/
public JoinStateObject addJoin(String joinType, String path, String identificationVariable) {
JoinStateObject stateObject = addJoin(joinType);
stateObject.setJoinAssociationPath(path);
stateObject.setIdentificationVariable(identificationVariable);
return stateObject;
}
/**
* Adds a new <code><b>LEFT JOIN</b></code> expression to this declaration.
*
* @param path The join association path expression
* @param identificationVariable The new variable defining the join association path
* @return A new {@link JoinStateObject}
*/
public JoinStateObject addLeftJoin(String path, String identificationVariable) {
return addJoin(LEFT_JOIN, path, identificationVariable);
}
/**
* Adds a new <code><b>LEFT OUTER JOIN</b></code> expression to this declaration.
*
* @param path The join association path expression
* @param identificationVariable The new variable defining the join association path
* @return A new {@link JoinStateObject}
*/
public JoinStateObject addLeftOuterJoin(String path, String identificationVariable) {
return addJoin(LEFT_OUTER_JOIN, path, identificationVariable);
}
/**
* Creates
*
*/
protected abstract AbstractRangeVariableDeclarationStateObject buildRangeVariableDeclarationStateObject();
@Override
public IdentificationVariableDeclaration getExpression() {
return (IdentificationVariableDeclaration) super.getExpression();
}
/**
* Returns the identification variable identifying the "root".
*
* @return A case insensitive unique identifier declaring the "root" of the declaration
*/
public String getIdentificationVariable() {
return getRangeVariableDeclaration().getIdentificationVariable();
}
/**
* Returns the {@link IdentificationVariableStateObject} holding onto the identification variable.
*
* @return The {@link IdentificationVariableStateObject}, which is never <code>null</code>
*/
public IdentificationVariableStateObject getIdentificationVariableStateObject() {
return getRangeVariableDeclaration().getIdentificationVariableStateObject();
}
@Override
public AbstractFromClauseStateObject getParent() {
return (AbstractFromClauseStateObject) super.getParent();
}
/**
* Returns the {@link StateObject} representing the range variable declaration portion.
*
* @return The concrete instance
*/
public AbstractRangeVariableDeclarationStateObject getRangeVariableDeclaration() {
return rangeVariableDeclaration;
}
/**
* Returns the "root" object for objects which may not be reachable by navigation.
*
* @return The "root" object
*/
public String getRootPath() {
return getRangeVariableDeclaration().getRootPath();
}
/**
* Returns the {@link StateObject} representing the "root" for objects which may not be
* reachable by navigation.
*
* @return The {@link StateObject} representing one of the possible valid "root"
*/
public StateObject getRootStateObject() {
return getRangeVariableDeclaration().getRootStateObject();
}
@Override
public ListIterable<IdentificationVariableStateObject> identificationVariables() {
List<IdentificationVariableStateObject> stateObjects = new ArrayList<IdentificationVariableStateObject>();
stateObjects.add(rangeVariableDeclaration.getIdentificationVariableStateObject());
for (JoinStateObject join : items()) {
stateObjects.add(join.getIdentificationVariableStateObject());
}
return new SnapshotCloneListIterable<IdentificationVariableStateObject>(stateObjects);
}
@Override
protected void initialize() {
super.initialize();
rangeVariableDeclaration = buildRangeVariableDeclarationStateObject();
}
@Override
public boolean isEquivalent(StateObject stateObject) {
if (super.isEquivalent(stateObject)) {
AbstractIdentificationVariableDeclarationStateObject declaration = (AbstractIdentificationVariableDeclarationStateObject) stateObject;
return rangeVariableDeclaration.isEquivalent(declaration.rangeVariableDeclaration) &&
areChildrenEquivalent(declaration);
}
return false;
}
@Override
protected String listName() {
return JOINS_LIST;
}
/**
* Parses the given JPQL fragment that should contain a single <code><b>JOIN</b></code>
* expression.
*
* @param jpqlFragment The portion representing a <code><b>JOIN</b></code> expression
*/
public void parseJoin(String jpqlFragment) {
// The JoinStateObject is automatically added to this state object
buildStateObject(jpqlFragment, JoinBNF.ID);
}
/**
* Keeps a reference of the {@link IdentificationVariableDeclaration 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 IdentificationVariableDeclaration parsed object} representing an
* identification variable declaration
*/
public void setExpression(IdentificationVariableDeclaration expression) {
super.setExpression(expression);
}
/**
* Sets the new identification variable that will range over the "root".
*
* @param identificationVariable The new identification variable
*/
public void setIdentificationVariable(String identificationVariable) {
rangeVariableDeclaration.setIdentificationVariable(identificationVariable);
}
/**
* Sets the "root" object for objects which may not be reachable by navigation.
*
* @param root The "root" object
*/
public void setRootPath(String root) {
getRangeVariableDeclaration().setRootPath(root);
}
@Override
protected void toTextInternal(Appendable writer) throws IOException {
rangeVariableDeclaration.toString(writer);
if (itemsSize() > 0) {
writer.append(SPACE);
toStringItems(writer, false);
}
}
}