org.eclipse.persistence.jpa.jpql.parser.ExtractExpression Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of eclipselink Show documentation
Show all versions of eclipselink Show documentation
EclipseLink build based upon Git transaction f2b9fc5
/*
* Copyright (c) 2012, 2018 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;
/**
* The EXTRACT function extracts a date part from a date/time value. The date part can be
* YEAR
, MONTH
, DAY
, HOUR
, MINUTE
,
* SECOND
. Some databases may support other parts.
*
* BNF: extract_expression ::= EXTRACT(date_part_literal [FROM] scalar_expression)
*
* date_part_literal ::= { MICROSECOND | SECOND | MINUTE | HOUR | DAY | WEEK | MONTH | QUARTER |
* YEAR | SECOND_MICROSECOND | MINUTE_MICROSECOND | MINUTE_SECOND |
* HOUR_MICROSECOND | HOUR_SECOND | HOUR_MINUTE | DAY_MICROSECOND |
* DAY_SECOND | DAY_MINUTE | DAY_HOUR | YEAR_MONTH, etc }
*
*
* @version 2.5
* @since 2.4
* @author James Sutherland
*/
public final class ExtractExpression extends AbstractSingleEncapsulatedExpression {
/**
* The date part to extract from the date/time.
*/
private String datePart;
/**
* The actual FROM identifier found in the string representation of the JPQL query.
*/
private String fromIdentifier;
/**
* Determines whether a space was parsed after the date part.
*/
private boolean hasSpaceAfterDatePart;
/**
* Determines whether a space was parsed after the identifier FROM.
*/
private boolean hasSpaceAfterFrom;
/**
* Creates a new ExtractExpression
.
*
* @param parent The parent of this expression
*/
public ExtractExpression(AbstractExpression parent) {
super(parent, EXTRACT);
}
/**
* {@inheritDoc}
*/
public void accept(ExpressionVisitor visitor) {
acceptUnknownVisitor(visitor);
}
/**
* {@inheritDoc}
*/
@Override
protected void addOrderedEncapsulatedExpressionTo(List children) {
// Date part
if (hasDatePart()) {
children.add(buildStringExpression(datePart));
}
if (hasSpaceAfterDatePart) {
children.add(buildStringExpression(SPACE));
}
// 'FROM'
if (fromIdentifier != null) {
children.add(buildStringExpression(FROM));
}
if (hasSpaceAfterFrom) {
children.add(buildStringExpression(SPACE));
}
// Value
super.addOrderedEncapsulatedExpressionTo(children);
}
/**
* {@inheritDoc}
*/
@Override
public String getEncapsulatedExpressionQueryBNFId() {
return ScalarExpressionBNF.ID;
}
/**
* Returns the actual FROM identifier found in the string representation of the JPQL
* query, which has the actual case that was used.
*
* @return The FROM identifier that was actually parsed, or an empty string if it was not
* parsed
*/
public String getActualFromIdentifier() {
return (fromIdentifier != null) ? fromIdentifier : ExpressionTools.EMPTY_STRING;
}
/**
* Returns the date part that was parsed, it is used to extract a single part of a date/time,
* such as year, month, day, your, etc.
*
* @return The part of the date/time to retrieve
*/
public String getDatePart() {
return datePart;
}
/**
* {@inheritDoc}
*/
public JPQLQueryBNF getQueryBNF() {
return getQueryBNF(ExtractExpressionBNF.ID);
}
/**
* Determines whether the date part literal was parsed or not.
*
* @return true
if the date part literal was parsed; false
otherwise
*/
public boolean hasDatePart() {
return ExpressionTools.stringIsNotEmpty(datePart);
}
/**
* {@inheritDoc}
*/
@Override
public boolean hasEncapsulatedExpression() {
return hasDatePart() || (fromIdentifier != null) || hasExpression();
}
/**
* Determines whether the identifier FROM was part of the query.
*
* @return true
if the identifier FROM was parsed; false
otherwise
*/
public boolean hasFrom() {
return fromIdentifier != null;
}
/**
* Determines whether a whitespace was found after the date part literal.
*
* @return true
if there was a whitespace after the date part literal;
* false
otherwise
*/
public boolean hasSpaceAfterDatePart() {
return hasSpaceAfterDatePart;
}
/**
* Determines whether a whitespace was found after FROM.
*
* @return true
if there was a whitespace after FROM; false
otherwise
*/
public boolean hasSpaceAfterFrom() {
return hasSpaceAfterFrom;
}
/**
* {@inheritDoc}
*/
@Override
protected boolean isParsingComplete(WordParser wordParser, String word, Expression expression) {
return word.equalsIgnoreCase(FROM) ||
super.isParsingComplete(wordParser, word, expression);
}
/**
* {@inheritDoc}
*/
@Override
protected void parseEncapsulatedExpression(WordParser wordParser,
int whitespaceCount,
boolean tolerant) {
if (wordParser.isTail()) {
datePart = ExpressionTools.EMPTY_STRING;
return;
}
// Parse the date part
datePart = wordParser.word();
// Invalid date part, parse it as the scalar expression
if (tolerant &&
(isParsingComplete(wordParser, datePart, null) ||
datePart.indexOf(DOT) > -1 ||
datePart.charAt(0) == SINGLE_QUOTE ||
datePart.charAt(0) == DOUBLE_QUOTE ||
Character.isDigit(datePart.charAt(0)))) {
datePart = ExpressionTools.EMPTY_STRING;
hasSpaceAfterDatePart = whitespaceCount > 0;
}
else {
wordParser.moveForward(datePart);
hasSpaceAfterDatePart = wordParser.skipLeadingWhitespace() > 0;
}
// Parse 'FROM'
if (wordParser.startsWithIdentifier(FROM)) {
fromIdentifier = wordParser.moveForward(FROM);
hasSpaceAfterFrom = wordParser.skipLeadingWhitespace() > 0;
}
// Parse the value
super.parseEncapsulatedExpression(wordParser, whitespaceCount, tolerant);
}
/**
* {@inheritDoc}
*/
@Override
protected void removeEncapsulatedExpression() {
super.removeEncapsulatedExpression();
datePart = ExpressionTools.EMPTY_STRING;
fromIdentifier = null;
hasSpaceAfterFrom = false;
hasSpaceAfterDatePart = false;
}
/**
* {@inheritDoc}
*/
@Override
protected void toParsedTextEncapsulatedExpression(StringBuilder writer, boolean actual) {
// Date part
writer.append(datePart);
if (hasSpaceAfterDatePart) {
writer.append(SPACE);
}
// FROM
if (fromIdentifier != null) {
writer.append(actual ? fromIdentifier : FROM);
}
if (hasSpaceAfterFrom) {
writer.append(SPACE);
}
// Value
super.toParsedTextEncapsulatedExpression(writer, actual);
}
}