| /* |
| * 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.InExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.InExpressionItemBNF; |
| import static org.eclipse.persistence.jpa.jpql.parser.AbstractExpression.*; |
| |
| /** |
| * The state field path expression must have a string, numeric, or enum value. The literal and/or |
| * input parameter values must be like the same abstract schema type of the state field path |
| * expression in type. |
| * <p> |
| * The results of the subquery must be like the same abstract schema type of the state field path |
| * expression in type. |
| * <p> |
| * There must be at least one element in the comma separated list that defines the set of values for |
| * the <code><b>IN</b></code> expression. If the value of a state field path expression in an |
| * <code><b>IN</b></code> or <code><b>NOT IN</b></code> expression is <code><b>NULL</b></code> or |
| * unknown, the value of the expression is unknown. |
| * <p> |
| * JPA 1.0: |
| * <div><p><b>BNF:</b> <code>in_expression ::= state_field_path_expression [NOT] IN(in_item {, in_item}* | subquery)</code></p></div> |
| * JPA 2.0 |
| * <div><p><b>BNF:</b> <code>in_expression ::= {state_field_path_expression | type_discriminator} [NOT] IN { ( in_item {, in_item}* ) | (subquery) | collection_valued_input_parameter }</code></p></div> |
| * |
| * @see InExpression |
| * |
| * @version 2.4 |
| * @since 2.4 |
| * @author Pascal Filion |
| */ |
| @SuppressWarnings({"nls", "unused"}) // unused used for the import statement: see bug 330740 |
| public class InExpressionStateObject extends AbstractListHolderStateObject<StateObject> { |
| |
| /** |
| * Determines whether the <code><b>NOT</b></code> identifier is part of the expression or not. |
| */ |
| private boolean not; |
| |
| /** |
| * Determines whether what was parsed after the <code>IN</code> identifier is a single input |
| * parameter. |
| */ |
| private boolean singleInputParameter; |
| |
| /** |
| * The {@link StateObject} representing either the state field path expression or the type |
| * discriminator. |
| */ |
| private StateObject stateObject; |
| |
| /** |
| * Notifies the list of items has changed. |
| */ |
| public static final String ITEMS_LIST = "items"; |
| |
| /** |
| * Notifies the visibility of the <code><b>NOT</b></code> identifier has changed. |
| */ |
| public static final String NOT_PROPERTY = "not"; |
| |
| /** |
| * Notifies the {@link StateObject} representing the state field path expression or the input |
| * parameter has changed. |
| */ |
| public static final String STATE_OBJECT_PROPERTY = "stateObject"; |
| |
| /** |
| * Creates a new <code>InExpressionStateObject</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 InExpressionStateObject(StateObject parent) { |
| super(parent); |
| } |
| |
| /** |
| * Creates a new <code>InExpressionStateObject</code>. |
| * |
| * @param parent The parent of this state object, which cannot be <code>null</code> |
| * @param not Determines whether the <code><b>NOT</b></code> identifier is part of the expression |
| * or not |
| * @param items The list of JPQL fragments that will be parsed and converted into {@link |
| * StateObject} |
| * @exception NullPointerException The given parent cannot be <code>null</code> |
| */ |
| public InExpressionStateObject(StateObject parent, |
| boolean not, |
| String path, |
| List<String> items) { |
| |
| super(parent); |
| // TODO |
| } |
| |
| /** |
| * Creates a new <code>InExpressionStateObject</code>. |
| * |
| * @param parent The parent of this state object, which cannot be <code>null</code> |
| * @param not Determines whether the <code><b>NOT</b></code> identifier is part of the expression |
| * or not |
| * @param parameter The |
| * @exception NullPointerException The given parent cannot be <code>null</code> |
| */ |
| public InExpressionStateObject(StateObject parent, |
| boolean not, |
| String path, |
| String parameter) { |
| |
| super(parent); |
| this.not = not; |
| parse(path); |
| addItem(new InputParameterStateObject(this, parameter)); |
| } |
| |
| /** |
| * Creates a new <code>InExpressionStateObject</code>. |
| * |
| * @param parent The parent of this state object, which cannot be <code>null</code> |
| * @param not Determines whether the <code><b>NOT</b></code> identifier is part of the expression |
| * or not |
| * @exception NullPointerException The given parent cannot be <code>null</code> |
| */ |
| public InExpressionStateObject(StateObject parent, |
| StateObject stateObject, |
| boolean not, |
| List<? extends StateObject> items) { |
| |
| super(parent, items); |
| this.not = not; |
| this.stateObject = parent(stateObject); |
| } |
| |
| /** |
| * Creates a new <code>InExpressionStateObject</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 InExpressionStateObject(StateObject parent, |
| StateObject stateFieldPath, |
| List<? extends StateObject> items) { |
| |
| this(parent, stateFieldPath, false, items); |
| } |
| |
| /** |
| * Creates a new <code>InExpressionStateObject</code>. |
| * |
| * @param parent The parent of this state object, which cannot be <code>null</code> |
| * @param items The list of JPQL fragments that will be parsed and converted into {@link |
| * StateObject} |
| * @exception NullPointerException The given parent cannot be <code>null</code> |
| */ |
| public InExpressionStateObject(StateObject parent, |
| String path, |
| List<String> items) { |
| |
| this(parent, false, path, items); |
| } |
| |
| @Override |
| public void accept(StateObjectVisitor visitor) { |
| visitor.visit(this); |
| } |
| |
| @Override |
| protected void addChildren(List<StateObject> children) { |
| if (stateObject != null) { |
| children.add(stateObject); |
| } |
| super.addChildren(children); |
| } |
| |
| /** |
| * Makes sure the <code><b>NOT</b></code> identifier is specified. |
| * |
| * @return This object |
| */ |
| public InExpressionStateObject addNot() { |
| if (!not) { |
| setNot(true); |
| } |
| return this; |
| } |
| |
| @Override |
| public InExpression getExpression() { |
| return (InExpression) super.getExpression(); |
| } |
| |
| /** |
| * Returns |
| * |
| */ |
| public StateObject getStateObject() { |
| return stateObject; |
| } |
| |
| /** |
| * Determines whether the <code><b>NOT</b></code> identifier is used or not. |
| * |
| * @return <code>true</code> if the <code><b>NOT</b></code> identifier is part of the expression; |
| * <code>false</code> otherwise |
| */ |
| public boolean hasNot() { |
| return not; |
| } |
| |
| /** |
| * Determines whether |
| * |
| */ |
| public boolean hasStateObject() { |
| return stateObject != null; |
| } |
| |
| @Override |
| public boolean isEquivalent(StateObject stateObject) { |
| |
| if (super.isEquivalent(stateObject)) { |
| InExpressionStateObject inStateObject = (InExpressionStateObject) stateObject; |
| return not == inStateObject.not && |
| singleInputParameter == inStateObject.singleInputParameter && |
| areEquivalent(stateObject, stateObject) && |
| areChildrenEquivalent(inStateObject); |
| } |
| |
| return false; |
| } |
| |
| /** |
| * Determines whether what was parsed after the <code>IN</code> identifier is a single input |
| * parameter. |
| * |
| * @return <code>true</code> if the only item is an input parameter; <code>false</code> otherwise |
| */ |
| public boolean isSingleInputParameter() { |
| return singleInputParameter; |
| } |
| |
| @Override |
| protected String listName() { |
| return ITEMS_LIST; |
| } |
| |
| /** |
| * Parses the given JPQL fragment, which represents either a single or many items, the fragment |
| * will be parsed and converted into {@link StateObject}. |
| * |
| * @param jpqlFragment The portion of the query to parse |
| */ |
| public void parse(String jpqlFragment) { |
| StateObject stateObject = buildStateObject(jpqlFragment, InExpressionItemBNF.ID); |
| setStateObject(stateObject); |
| } |
| |
| /** |
| * Makes sure the <code><b>NOT</b></code> identifier is not specified. |
| */ |
| public void removeNot() { |
| if (not) { |
| setNot(false); |
| } |
| } |
| |
| /** |
| * Keeps a reference of the {@link InExpression 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 InExpression parsed object} representing an <code><b>IN</b></code> |
| * expression |
| */ |
| public void setExpression(InExpression expression) { |
| super.setExpression(expression); |
| } |
| |
| /** |
| * Sets whether the <code><b>NOT</b></code> identifier should be part of the expression or not. |
| * |
| * @param not <code>true</code> if the <code><b>NOT</b></code> identifier should be part of the |
| * expression; <code>false</code> otherwise |
| */ |
| public void setNot(boolean not) { |
| boolean oldNot = this.not; |
| this.not = not; |
| firePropertyChanged(NOT_PROPERTY, oldNot, not); |
| } |
| |
| /** |
| * Sets whether what was parsed after the <code>IN</code> identifier is a single input parameter. |
| * |
| * @param singleInputParameter <code>true</code> if the only item is an input parameter; |
| * <code>false</code> otherwise |
| */ |
| public void setSingleInputParameter(boolean singleInputParameter) { |
| this.singleInputParameter = singleInputParameter; |
| } |
| |
| /** |
| * Sets |
| * |
| */ |
| public void setStateObject(StateObject stateObject) { |
| StateObject oldStateObject = this.stateObject; |
| this.stateObject = parent(stateObject); |
| firePropertyChanged(STATE_OBJECT_PROPERTY, oldStateObject, stateObject); |
| } |
| |
| /** |
| * Changes the visibility state of the <code><b>NOT</b></code> identifier. |
| */ |
| public void toggleNot() { |
| setNot(!not); |
| } |
| |
| @Override |
| protected void toTextInternal(Appendable writer) throws IOException { |
| |
| if (stateObject != null) { |
| stateObject.toString(writer); |
| writer.append(SPACE); |
| } |
| |
| writer.append(not ? NOT_IN : IN); |
| writer.append(SPACE); |
| |
| if (singleInputParameter) { |
| writer.append(LEFT_PARENTHESIS); |
| } |
| |
| toStringItems(writer, true); |
| |
| if (singleInputParameter) { |
| writer.append(RIGHT_PARENTHESIS); |
| } |
| } |
| } |