| /* |
| * 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.parser; |
| |
| import java.util.List; |
| import org.eclipse.persistence.jpa.jpql.ExpressionTools; |
| import org.eclipse.persistence.jpa.jpql.WordParser; |
| |
| /** |
| * In the <b>SELECT</b> clause the result of a query may be the result of an aggregate function |
| * applied to a path expression. The following aggregate functions can be used in the <b>SELECT</b> |
| * clause of a query: <b>AVG</b>, <b>COUNT</b>, <b>MAX</b>, <b>MIN</b>, <b>SUM</b>. |
| * <p> |
| * A <code>single_valued_association_field</code> is designated by the name of an association-field |
| * in a one-to-one or many-to-one relationship. The type of a <code>single_valued_association_field</code> |
| * and thus a <code>single_valued_association_path_expression</code> is the abstract schema type of |
| * the related entity. |
| * <p> |
| * The argument to an aggregate function may be preceded by the keyword <b>DISTINCT</b> to specify |
| * that duplicate values are to be eliminated before the aggregate function is applied. Null values |
| * are eliminated before the aggregate function is applied, regardless of whether the keyword |
| * <b>DISTINCT</b> |
| * is specified. |
| * |
| * <div><b>BNF:</b> <code>aggregate_expression ::= { AVG | MAX | MIN | SUM } ([DISTINCT] state_field_path_expression) | |
| * COUNT ([DISTINCT] identification_variable | |
| * state_field_path_expression | |
| * single_valued_object_path_expression)</code></div> |
| * |
| * @see AvgFunction |
| * @see CountFunction |
| * @see MaxFunction |
| * @see MinFunction |
| * @see SumFunction |
| * |
| * @version 2.5 |
| * @since 2.3 |
| * @author Pascal Filion |
| */ |
| public abstract class AggregateFunction extends AbstractSingleEncapsulatedExpression { |
| |
| /** |
| * The actual <b>DISTINCT</b> identifier found in the string representation of the JPQL query. |
| */ |
| private String distinctIdentifier; |
| |
| /** |
| * Determines whether whitespace was found after the identifier <b>DISTINCT</b>. |
| */ |
| private boolean hasSpaceAfterDistinct; |
| |
| /** |
| * Creates a new <code>AggregateFunction</code>. |
| * |
| * @param parent The parent of this expression |
| * @param identifier The JPQL identifier that starts this expression |
| */ |
| protected AggregateFunction(AbstractExpression parent, String identifier) { |
| super(parent, identifier); |
| } |
| |
| @Override |
| protected void addOrderedEncapsulatedExpressionTo(List<Expression> children) { |
| |
| if (distinctIdentifier != null) { |
| children.add(buildStringExpression(DISTINCT)); |
| } |
| |
| if (hasSpaceAfterDistinct) { |
| children.add(buildStringExpression(SPACE)); |
| } |
| |
| super.addOrderedEncapsulatedExpressionTo(children); |
| } |
| |
| /** |
| * Creates the {@link AbstractExpression} to represent the given word. |
| * |
| * @param word The word that was parsed |
| * @return The encapsulated {@link AbstractExpression} |
| */ |
| protected AbstractExpression buildEncapsulatedExpression(WordParser wordParser, String word) { |
| return new StateFieldPathExpression(this, word); |
| } |
| |
| @Override |
| public String getEncapsulatedExpressionQueryBNFId() { |
| return InternalAggregateFunctionBNF.ID; |
| } |
| |
| /** |
| * Returns the actual <b>DISTINCT</b> identifier found in the string representation of the JPQL |
| * query, which has the actual case that was used. |
| * |
| * @return The <b>DISTINCT</b> identifier that was actually parsed, or an empty string if it was |
| * not parsed |
| */ |
| public String getActualDistinctIdentifier() { |
| return (distinctIdentifier != null) ?distinctIdentifier : ExpressionTools.EMPTY_STRING; |
| } |
| |
| @Override |
| public JPQLQueryBNF getQueryBNF() { |
| return getQueryBNF(AggregateExpressionBNF.ID); |
| } |
| |
| /** |
| * Determines whether the <b>DISTINCT</b> identifier was specified in the query. |
| * |
| * @return <code>true</code> if the query has <b>DISTINCT</b>; <code>false</code> otherwise |
| */ |
| public final boolean hasDistinct() { |
| return distinctIdentifier != null; |
| } |
| |
| /** |
| * Determines whether a whitespace was parsed after <b>DISTINCT</b>. |
| * |
| * @return <code>true</code> if there was a whitespace after <b>DISTINCT</b>; <code>false</code> |
| * otherwise |
| */ |
| public final boolean hasSpaceAfterDistinct() { |
| return hasSpaceAfterDistinct; |
| } |
| |
| @Override |
| protected final void parseEncapsulatedExpression(WordParser wordParser, |
| int whitespaceCount, |
| boolean tolerant) { |
| |
| // Parse 'DISTINCT' |
| if (wordParser.startsWithIdentifier(DISTINCT)) { |
| distinctIdentifier = wordParser.moveForward(DISTINCT); |
| hasSpaceAfterDistinct = wordParser.skipLeadingWhitespace() > 0; |
| } |
| |
| // Parse the rest |
| super.parseEncapsulatedExpression(wordParser, whitespaceCount, tolerant); |
| } |
| |
| @Override |
| protected void toParsedTextEncapsulatedExpression(StringBuilder writer, boolean actual) { |
| |
| if (distinctIdentifier != null) { |
| writer.append(actual ? distinctIdentifier : DISTINCT); |
| } |
| |
| if (hasSpaceAfterDistinct) { |
| writer.append(SPACE); |
| } |
| |
| super.toParsedTextEncapsulatedExpression(writer, actual); |
| } |
| } |