| /* |
| * 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.OrderByItem; |
| import org.eclipse.persistence.jpa.jpql.parser.OrderByItem.Ordering; |
| import org.eclipse.persistence.jpa.jpql.parser.OrderByItemBNF; |
| import static org.eclipse.persistence.jpa.jpql.parser.AbstractExpression.*; |
| |
| /** |
| * An <code><b>ORDER BY</b></code> item must be one of the following: |
| * <ol> |
| * <li> A {@link StateFieldPathExpressionStateObject state_field_path_expression} that evaluates to |
| * an orderable state field of an entity or embeddable class abstract schema type designated in the |
| * <code><b>SELECT</b></code> clause by one of the following: |
| * <ul> |
| * <li>A general_identification_variable |
| * <li>A single_valued_object_path_expression |
| * </ul> |
| * <li>A {@link StateFieldPathExpressionStateObject state_field_path_expression} that evaluates to |
| * the same state field of the same entity or embeddable abstract schema type as a {@link |
| * StateFieldPathExpressionStateObject state_field_path_expression} in the <b>SELECT</b> clause |
| * <li>A {@link ResultVariableStateObject result_variable} that refers to an orderable item in the |
| * <code><b>SELECT</b></code> clause for which the same {@link ResultVariableStateObject result_variable} |
| * has been specified. This may be the result of an aggregate_expression, a <code>scalar_expression</code>, |
| * or a {@link StateFieldPathExpressionStateObject state_field_path_expression} in the |
| * <code><b>SELECT</b></code> clause. |
| * </ol> |
| * <p> |
| * The keyword <code><b>ASC</b></code> specifies that ascending ordering be used for the associated |
| * <code><b>ORDER BY</b></code> item; the keyword <code><b>DESC</b></code> specifies that descending |
| * ordering be used. Ascending ordering is the default. |
| * <p> |
| * JPA 1.0: <div><b>BNF:</b> <code>orderby_item ::= state_field_path_expression [ ASC | DESC ]</code></div> |
| * <p> |
| * JPA 2.0 <div><p><b>BNF:</b> <code>orderby_item ::= state_field_path_expression | result_variable [ ASC | DESC ]</code></p></div> |
| * |
| * @see OrderByItem |
| * |
| * @version 2.4 |
| * @since 2.4 |
| * @author Pascal Filion |
| */ |
| @SuppressWarnings("nls") |
| public class OrderByItemStateObject extends AbstractStateObject { |
| |
| /** |
| * The ascending ordering. |
| */ |
| private Ordering ordering; |
| |
| /** |
| * The {@link StateObject} representing the order by item. |
| */ |
| private StateObject stateObject; |
| |
| /** |
| * Notifies the ordering property has changed. |
| */ |
| public static final String ORDERING_PROPERTY = "ordering"; |
| |
| /** |
| * Notifies the ordering property has changed. |
| */ |
| public static final String STATE_OBJECT_PROPERTY = "stateObject"; |
| |
| /** |
| * Creates a new <code>OrderByItemStateObject</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 OrderByItemStateObject(OrderByClauseStateObject parent) { |
| super(parent); |
| } |
| |
| /** |
| * Creates a new <code>OrderByItemStateObject</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 OrderByItemStateObject(OrderByClauseStateObject parent, Ordering ordering) { |
| super(parent); |
| validateOrdering(ordering); |
| this.ordering = ordering; |
| } |
| |
| /** |
| * Creates a new <code>OrderByItemStateObject</code>. |
| * |
| * @param parent The parent of this state object, which cannot be <code>null</code> |
| * @param stateObject The {@link StateObject} of the item |
| * @param ordering One of the possible {@link Ordering} choice |
| * @exception NullPointerException The given parent cannot be <code>null</code> |
| */ |
| public OrderByItemStateObject(OrderByClauseStateObject parent, |
| StateObject stateObject, |
| Ordering ordering) { |
| |
| this(parent, ordering); |
| this.stateObject = parent(stateObject); |
| } |
| |
| /** |
| * Creates a new <code>OrderByItemStateObject</code>. |
| * |
| * @param parent The parent of this state object, which cannot be <code>null</code> |
| * @param jpqlFragment The JPQL fragment representing the ordering item |
| * @param ordering One of the possible {@link Ordering} choice |
| * @exception NullPointerException The given parent cannot be <code>null</code> |
| */ |
| public OrderByItemStateObject(OrderByClauseStateObject parent, |
| String jpqlFragment, |
| Ordering ordering) { |
| |
| this(parent, ordering); |
| parse(jpqlFragment); |
| } |
| |
| @Override |
| public void accept(StateObjectVisitor visitor) { |
| visitor.visit(this); |
| } |
| |
| @Override |
| protected void addChildren(List<StateObject> children) { |
| super.addChildren(children); |
| if (stateObject != null) { |
| children.add(stateObject); |
| } |
| } |
| |
| @Override |
| public OrderByItem getExpression() { |
| return (OrderByItem) super.getExpression(); |
| } |
| |
| /** |
| * Returns the enum constant representing the ordering type. |
| * |
| * @return The constant representing the ordering, in the case the ordering was not parsed, then |
| * {@link Ordering#DEFAULT} is returned |
| */ |
| public Ordering getOrdering() { |
| return ordering; |
| } |
| |
| @Override |
| public OrderByClauseStateObject getParent() { |
| return (OrderByClauseStateObject) super.getParent(); |
| } |
| |
| /** |
| * Returns the {@link StateObject} representing the value used for ordering. |
| * |
| * @return The {@link StateObject} representing the value used for ordering |
| */ |
| public StateObject getStateObject() { |
| return stateObject; |
| } |
| |
| /** |
| * Determines whether the {@link StateObject} representing the value used for ordering has been |
| * defined. |
| * |
| * @return <code>true</code> if the ordering value is defined; <code>false</code> otherwise |
| */ |
| public boolean hasStateObject() { |
| return stateObject != null; |
| } |
| |
| /** |
| * Determines whether the ordering was specified as being ascendant. |
| * |
| * @return <code>true</code> if <b>ASC</b> was parsed; <code>false</code> otherwise |
| */ |
| public boolean isAscending() { |
| return ordering == Ordering.ASC; |
| } |
| |
| /** |
| * Determines whether the ordering was not specified. |
| * |
| * @return <code>true</code> if no ordering was parsed; <code>false</code> otherwise |
| */ |
| public boolean isDefault() { |
| return ordering == Ordering.DEFAULT; |
| } |
| |
| /** |
| * Determines whether the ordering was specified as being descendant. |
| * |
| * @return <code>true</code> if <b>DESC</b> was parsed; <code>false</code> otherwise |
| */ |
| public boolean isDescending() { |
| return ordering == Ordering.DESC; |
| } |
| |
| @Override |
| public boolean isEquivalent(StateObject stateObject) { |
| |
| if (super.isEquivalent(stateObject)) { |
| OrderByItemStateObject orderByItem = (OrderByItemStateObject) stateObject; |
| return ordering == orderByItem.ordering && |
| areEquivalent(stateObject, orderByItem.stateObject); |
| } |
| |
| return false; |
| } |
| |
| /** |
| * Parses the given JPQL fragment, which represents either a state-field pathe expression or a |
| * result variable, and creates the {@link StateObject}. |
| * |
| * @param jpqlFragment The portion of the query representing a state-field path expression or |
| * result variable |
| */ |
| public void parse(String jpqlFragment) { |
| StateObject stateObject = buildStateObject(jpqlFragment, OrderByItemBNF.ID); |
| setStateObject(stateObject); |
| } |
| |
| /** |
| * Removes the ordering if it's specified, otherwise do nothing. |
| */ |
| public void removeOrdering() { |
| setOrdering(Ordering.DEFAULT); |
| } |
| |
| /** |
| * Keeps a reference of the {@link OrderByItem 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 OrderByItem parsed object} representing an order by item |
| */ |
| public void setExpression(OrderByItem expression) { |
| super.setExpression(expression); |
| } |
| |
| /** |
| * Sets the enum constant representing the ordering type. |
| * |
| * @param ordering The constant representing the ordering, in the case the ordering was not |
| * parsed, then {@link Ordering#DEFAULT} should be used |
| */ |
| public void setOrdering(Ordering ordering) { |
| |
| validateOrdering(ordering); |
| |
| Ordering oldOrdering = this.ordering; |
| this.ordering = ordering; |
| firePropertyChanged(ORDERING_PROPERTY, oldOrdering, ordering); |
| } |
| |
| /** |
| * Sets the {@link StateObject} representing the value used for ordering. |
| * |
| * @param stateObject The {@link StateObject} representing the value used for ordering |
| */ |
| public void setStateObject(StateObject stateObject) { |
| StateObject oldStateObject = this.stateObject; |
| this.stateObject = parent(stateObject); |
| firePropertyChanged(STATE_OBJECT_PROPERTY, oldStateObject, stateObject); |
| } |
| |
| @Override |
| protected void toTextInternal(Appendable writer) throws IOException { |
| |
| if (stateObject != null) { |
| stateObject.toString(writer); |
| } |
| |
| if (!isDefault()) { |
| writer.append(SPACE); |
| writer.append(ordering.name()); |
| } |
| } |
| |
| private void validateOrdering(Ordering ordering) { |
| Assert.isNotNull(ordering, "The Ordering cannot be null"); |
| } |
| } |