| /* |
| * 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.parser.RangeVariableDeclaration; |
| import org.eclipse.persistence.jpa.jpql.tools.model.Problem; |
| import org.eclipse.persistence.jpa.jpql.tools.utility.iterable.SingleElementListIterable; |
| import org.eclipse.persistence.jpa.jpql.utility.iterable.ListIterable; |
| 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 AbstractRangeVariableDeclarationStateObject extends AbstractStateObject |
| implements VariableDeclarationStateObject { |
| |
| /** |
| * Flag used to determine if the <code><b>AS</b></code> identifier is used or not. |
| */ |
| private boolean as; |
| |
| /** |
| * The state object representing the identification variable used to reach the "root" object. |
| */ |
| private IdentificationVariableStateObject identificationVariable; |
| |
| /** |
| * Flag used to determine if the identification variable is optional or not, which is <code>true</code> |
| * only when this is used by {@link UpdateClauseStateObject} or by {@link DeleteClauseStateObject}. |
| */ |
| private boolean identificationVariableOptional; |
| |
| /** |
| * The state object holding the "root" for objects which may not be reachable by navigation. |
| */ |
| private StateObject rootStateObject; |
| |
| /** |
| * Notifies the visibility of the <code><b>AS</b></code> identifier has changed. |
| */ |
| public static final String AS_PROPERTY = "as"; |
| |
| /** |
| * Notifies the identification variable property has changed. |
| */ |
| public static final String IDENTIFICATION_VARIABLE_PROPERTY = "identificationVariable"; |
| |
| /** |
| * Creates a new <code>RangeVariableDeclarationStateObject</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> |
| */ |
| public AbstractRangeVariableDeclarationStateObject(AbstractIdentificationVariableDeclarationStateObject parent) { |
| super(parent); |
| } |
| |
| /** |
| * Creates a new <code>RangeVariableDeclarationStateObject</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> |
| */ |
| public AbstractRangeVariableDeclarationStateObject(AbstractIdentificationVariableDeclarationStateObject parent, |
| String root) { |
| super(parent); |
| setRootPath(root); |
| } |
| |
| /** |
| * Creates a new <code>AbstractRangeVariableDeclarationStateObject</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> |
| */ |
| public AbstractRangeVariableDeclarationStateObject(AbstractModifyClauseStateObject parent) { |
| super(parent); |
| setIdentificationVariableOptional(true); |
| } |
| |
| /** |
| * Makes sure the <code><b>AS</b></code> identifier is specified. |
| * |
| * @return This object |
| */ |
| public AbstractRangeVariableDeclarationStateObject addAs() { |
| if (!as) { |
| setAs(true); |
| } |
| return this; |
| } |
| |
| @Override |
| protected void addChildren(List<StateObject> children) { |
| super.addChildren(children); |
| children.add(rootStateObject); |
| children.add(identificationVariable); |
| } |
| |
| @Override |
| protected void addProblems(List<Problem> problems) { |
| super.addProblems(problems); |
| // TODO |
| |
| if (!identificationVariableOptional) { |
| // validate |
| } |
| } |
| |
| protected abstract StateObject buildRootStateObject(); |
| |
| @Override |
| public RangeVariableDeclaration getExpression() { |
| return (RangeVariableDeclaration) super.getExpression(); |
| } |
| |
| /** |
| * Returns the identification variable name that is ranging over the abstract schema type. |
| * |
| * @return The identification variable name |
| */ |
| public String getIdentificationVariable() { |
| return identificationVariable.getText(); |
| } |
| |
| /** |
| * Returns the {@link IdentificationVariableStateObject} holding onto the identification variable. |
| * |
| * @return The {@link IdentificationVariableStateObject}, which is never <code>null</code> |
| */ |
| public IdentificationVariableStateObject getIdentificationVariableStateObject() { |
| return identificationVariable; |
| } |
| |
| /** |
| * Returns the "root" object for objects which may not be reachable by navigation. |
| * |
| * @return The "root" object |
| */ |
| public abstract String getRootPath(); |
| |
| /** |
| * Returns the {@link StateObject} holding onto the abstract schema name. |
| * |
| * @return The {@link StateObject}, which is never <code>null</code> |
| */ |
| public StateObject getRootStateObject() { |
| return rootStateObject; |
| } |
| |
| /** |
| * Determines whether the <code><b>AS</b></code> identifier is used or not. |
| * |
| * @return <code>true</code> if the <code><b>AS</b></code> identifier is part of the expression; |
| * <code>false</code> otherwise |
| */ |
| public boolean hasAs() { |
| return as; |
| } |
| |
| /** |
| * Determines whether an identification variable was defined. |
| * |
| * @return <code>true</code> if an identification variable is defined; <code>false</code> otherwise |
| */ |
| public boolean hasIdentificationVariable() { |
| return identificationVariable.hasText(); |
| } |
| |
| @Override |
| public ListIterable<IdentificationVariableStateObject> identificationVariables() { |
| return new SingleElementListIterable<IdentificationVariableStateObject>(identificationVariable); |
| } |
| |
| @Override |
| protected void initialize() { |
| super.initialize(); |
| rootStateObject = buildRootStateObject(); |
| identificationVariable = new IdentificationVariableStateObject(this); |
| } |
| |
| @Override |
| public boolean isEquivalent(StateObject stateObject) { |
| |
| if (super.isEquivalent(stateObject)) { |
| AbstractRangeVariableDeclarationStateObject declaration = (AbstractRangeVariableDeclarationStateObject) stateObject; |
| |
| return as == declaration.as && |
| identificationVariableOptional == declaration.identificationVariableOptional && |
| identificationVariable.isEquivalent(declaration.identificationVariable) && |
| areEquivalent(rootStateObject, declaration.rootStateObject); |
| } |
| |
| return false; |
| } |
| |
| /** |
| * Determines whether the identification variable is optional or not. The only time it is |
| * optional is when this model is used in a modify clause (<code><b>DELETE</b></code> or |
| * <code><b>UPDATE</b></code>). |
| * |
| * @return <code>true</code> if an identification variable is not required; <code>false</code> if |
| * it is required |
| */ |
| public boolean isIdentificationVariableOptional() { |
| return identificationVariableOptional; |
| } |
| |
| /** |
| * Determines whether this identification variable is virtual, meaning it's not part of the query |
| * but is required for proper navigability. |
| * |
| * @return <code>true</code> if this identification variable was virtually created to fully |
| * qualify path expression; <code>false</code> if it was parsed |
| */ |
| public boolean isIdentificationVariableVirtual() { |
| return identificationVariable.isVirtual(); |
| } |
| |
| /** |
| * Sets whether the <code><b>AS</b></code> identifier is used or not. |
| * |
| * @param as <code>true</code> if the <code><b>AS</b></code> identifier is part of the |
| * expression; <code>false</code> otherwise |
| */ |
| public void setAs(boolean as) { |
| boolean oldAs = this.as; |
| this.as = as; |
| firePropertyChanged(AS_PROPERTY, oldAs, as); |
| } |
| |
| /** |
| * Keeps a reference of the {@link RangeVariableDeclaration 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 RangeVariableDeclaration parsed object} representing a range |
| * variable declaration |
| */ |
| public void setExpression(RangeVariableDeclaration expression) { |
| super.setExpression(expression); |
| } |
| |
| /** |
| * Sets the new identification variable that will range over the "root" object. |
| * |
| * @param identificationVariable The new identification variable |
| */ |
| public void setIdentificationVariable(String identificationVariable) { |
| String oldIdentificationVariable = getIdentificationVariable(); |
| this.identificationVariable.setText(identificationVariable); |
| firePropertyChanged(IDENTIFICATION_VARIABLE_PROPERTY, oldIdentificationVariable, identificationVariable); |
| } |
| |
| /** |
| * Sets whether the identification variable is optional or not. The only time it is optional is |
| * when this model is used in a modify clause (<code><b>DELETE</b></code> or |
| * <code><b>UPDATE</b></code>). |
| * |
| * @param identificationVariableOptional <code>true</code> if an identification variable is not |
| * required; <code>false</code> if it is required |
| */ |
| protected void setIdentificationVariableOptional(boolean identificationVariableOptional) { |
| this.identificationVariableOptional = identificationVariableOptional; |
| } |
| |
| /** |
| * Sets the "root" object for objects which may not be reachable by navigation. |
| * |
| * @param root The "root" object |
| */ |
| public abstract void setRootPath(String root); |
| |
| /** |
| * Toggles the usage of the <code><b>AS</b></code> identifier. |
| */ |
| public void toggleAs() { |
| setAs(!hasAs()); |
| } |
| |
| @Override |
| protected void toTextInternal(Appendable writer) throws IOException { |
| |
| rootStateObject.toString(writer); |
| |
| if (as) { |
| writer.append(SPACE); |
| writer.append(AS); |
| |
| if (!identificationVariable.isVirtual()) { |
| writer.append(SPACE); |
| } |
| } |
| |
| if (!identificationVariable.isVirtual()) { |
| |
| if (rootStateObject != null && !as) { |
| writer.append(SPACE); |
| } |
| |
| identificationVariable.toString(writer); |
| } |
| } |
| } |