org.eclipse.persistence.jpa.jpql.parser.LikeExpression 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) 2006, 2020 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.Collection;
import java.util.List;
import org.eclipse.persistence.jpa.jpql.ExpressionTools;
import org.eclipse.persistence.jpa.jpql.WordParser;
/**
* The LIKE condition is used to specify a search for a pattern.
*
* The string_expression
must have a string value. The pattern_value
is a
* string literal or a string-valued input parameter in which an underscore (_) stands for any
* single character, a percent (%) character stands for any sequence of characters (including the
* empty sequence), and all other characters stand for themselves. The optional escape_character
* is a single-character string literal or a character-valued input parameter (i.e., char or
* Character) and is used to escape the special meaning of the underscore and percent characters in
* pattern_value
.
*
*
BNF: like_expression ::= string_expression [NOT] LIKE pattern_value [ESCAPE escape_character]
*
* @version 2.5
* @since 2.3
* @author Pascal Filion
*/
public final class LikeExpression extends AbstractExpression {
/**
* The {@link Expression} representing the escape character, which is either a single character
* or an input parameter.
*/
private AbstractExpression escapeCharacter;
/**
* The actual escape identifier found in the string representation of the JPQL query.
*/
private String escapeIdentifier;
/**
* Determines whether a whitespace was parsed after ESCAPE.
*/
private boolean hasSpaceAfterEscape;
/**
* Determines whether a whitespace was parsed after LIKE.
*/
private boolean hasSpaceAfterLike;
/**
* Determines whether a whitespace was parsed after the pattern value.
*/
private boolean hasSpaceAfterPatternValue;
/**
* The actual LIKE identifier found in the string representation of the JPQL query.
*/
private String likeIdentifier;
/**
* The actual NOT identifier found in the string representation of the JPQL query.
*/
private String notIdentifier;
/**
* The {@link Expression} representing the pattern value.
*/
private AbstractExpression patternValue;
/**
* The {@link Expression} representing the string expression.
*/
private AbstractExpression stringExpression;
/**
* Creates a new LikeExpression
.
*
* @param parent The parent of this expression
* @param stringExpression The first part of this expression, which is the string expression
*/
public LikeExpression(AbstractExpression parent, AbstractExpression stringExpression) {
super(parent, LIKE);
if (stringExpression != null) {
this.stringExpression = stringExpression;
this.stringExpression.setParent(this);
}
}
@Override
public void accept(ExpressionVisitor visitor) {
visitor.visit(this);
}
@Override
public void acceptChildren(ExpressionVisitor visitor) {
getStringExpression().accept(visitor);
getPatternValue().accept(visitor);
getEscapeCharacter().accept(visitor);
}
@Override
protected void addChildrenTo(Collection children) {
children.add(getStringExpression());
children.add(getPatternValue());
children.add(getEscapeCharacter());
}
@Override
protected void addOrderedChildrenTo(List children) {
// String expression
if (stringExpression != null) {
children.add(stringExpression);
}
// 'NOT'
if (notIdentifier != null) {
children.add(buildStringExpression(SPACE));
children.add(buildStringExpression(NOT));
}
children.add(buildStringExpression(SPACE));
// 'LIKE'
children.add(buildStringExpression(LIKE));
if (hasSpaceAfterLike) {
children.add(buildStringExpression(SPACE));
}
// Pattern value
if (patternValue != null) {
children.add(patternValue);
}
if (hasSpaceAfterPatternValue) {
children.add(buildStringExpression(SPACE));
}
// 'ESCAPE'
if (escapeIdentifier != null) {
children.add(buildStringExpression(ESCAPE));
}
if (hasSpaceAfterEscape) {
children.add(buildStringExpression(SPACE));
}
// Escape character
if (escapeCharacter != null) {
children.add(escapeCharacter);
}
}
@Override
public JPQLQueryBNF findQueryBNF(Expression expression) {
if ((patternValue != null) && patternValue.isAncestor(expression)) {
return getQueryBNF(PatternValueBNF.ID);
}
if ((escapeCharacter != null) && escapeCharacter.isAncestor(expression)) {
return getQueryBNF(LikeExpressionEscapeCharacterBNF.ID);
}
return super.findQueryBNF(expression);
}
/**
* Returns the actual ESCAPE found in the string representation of the JPQL query, which
* has the actual case that was used.
*
* @return The ESCAPE identifier that was actually parsed, or an empty string if it was
* not parsed
*/
public String getActualEscapeIdentifier() {
return (escapeIdentifier != null) ? escapeIdentifier : ExpressionTools.EMPTY_STRING;
}
/**
* Returns the actual LIKE found in the string representation of the JPQL query, which
* has the actual case that was used.
*
* @return The LIKE identifier that was actually parsed
*/
public String getActualLikeIdentifier() {
return likeIdentifier;
}
/**
* Returns the actual NOT found in the string representation of the JPQL query, which has
* the actual case that was used.
*
* @return The NOT identifier that was actually parsed, or an empty string if it was not
* parsed
*/
public String getActualNotIdentifier() {
return (notIdentifier != null) ? notIdentifier : ExpressionTools.EMPTY_STRING;
}
/**
* Returns the {@link Expression} that represents the escape character, which is either a single
* character or an input parameter.
*
* @return The expression that was parsed representing the escape character
*/
public Expression getEscapeCharacter() {
if (escapeCharacter == null) {
escapeCharacter = buildNullExpression();
}
return escapeCharacter;
}
/**
* Returns the enum constant that represents the identifier.
*
* @return Either LIKE or NOT LIKE
*/
public String getIdentifier() {
return (notIdentifier != null) ? NOT_LIKE : LIKE;
}
/**
* Returns the {@link Expression} that represents the pattern value.
*
* @return The expression that was parsed representing the pattern value
*/
public Expression getPatternValue() {
if (patternValue == null) {
patternValue = buildNullExpression();
}
return patternValue;
}
@Override
public JPQLQueryBNF getQueryBNF() {
return getQueryBNF(LikeExpressionBNF.ID);
}
/**
* Returns the {@link Expression} that represents the string expression.
*
* @return The expression that was parsed representing the string expression
*/
public Expression getStringExpression() {
if (stringExpression == null) {
stringExpression = buildNullExpression();
}
return stringExpression;
}
/**
* Determines whether the identifier ESCAPE was parsed.
*
* @return true
if the identifier ESCAPE was parsed; false
otherwise
*/
public boolean hasEscape() {
return escapeIdentifier != null;
}
/**
* Determines whether the escape character was parsed, which is either a single character or an
* input parameter.
*
* @return true
if the escape character was parsed; false
otherwise
*/
public boolean hasEscapeCharacter() {
return escapeCharacter != null &&
!escapeCharacter.isNull();
}
/**
* Determines whether the identifier NOT was parsed.
*
* @return true
if the identifier NOT was parsed; false
otherwise
*/
public boolean hasNot() {
return notIdentifier != null;
}
/**
* Determines whether the pattern value was parsed.
*
* @return true
if the pattern value was parsed; false
otherwise
*/
public boolean hasPatternValue() {
return patternValue != null &&
!patternValue.isNull();
}
/**
* Determines whether a whitespace was parsed after ESCAPE.
*
* @return true
if there was a whitespace after ESCAPE; false
otherwise
*/
public boolean hasSpaceAfterEscape() {
return hasSpaceAfterEscape;
}
/**
* Determines whether a whitespace was parsed after LIKE.
*
* @return true
if there was a whitespace after LIKE; false
otherwise
*/
public boolean hasSpaceAfterLike() {
return hasSpaceAfterLike;
}
/**
* Determines whether a whitespace was parsed after the pattern value.
*
* @return true
if there was a whitespace after the pattern value; false
otherwise
*/
public boolean hasSpaceAfterPatternValue() {
return hasSpaceAfterPatternValue;
}
/**
* Determines whether a whitespace was parsed after the string expression.
*
* @return true
if there was a whitespace after the string expression;
* false
otherwise
*/
public boolean hasSpaceAfterStringExpression() {
return hasStringExpression();
}
/**
* Determines whether the string expression was parsed.
*
* @return true
if the string expression was parsed; false
otherwise
*/
public boolean hasStringExpression() {
return stringExpression != null &&
!stringExpression.isNull();
}
@Override
protected boolean isParsingComplete(WordParser wordParser, String word, Expression expression) {
char character = word.charAt(0);
if (getQueryBNF(PatternValueBNF.ID).handleAggregate() &&
(character == '+' || character == '-' || character == '*' || character == '/')) {
return false;
}
return super.isParsingComplete(wordParser, word, expression) ||
word.equalsIgnoreCase(ESCAPE) ||
word.equalsIgnoreCase(AND) ||
word.equalsIgnoreCase(OR) ||
expression != null;
}
@Override
protected void parse(WordParser wordParser, boolean tolerant) {
// Parse 'NOT
if (wordParser.startsWithIgnoreCase('N')) {
notIdentifier = wordParser.moveForward(NOT);
wordParser.skipLeadingWhitespace();
}
// Parse 'LIKE'
likeIdentifier = wordParser.moveForward(LIKE);
hasSpaceAfterLike = wordParser.skipLeadingWhitespace() > 0;
// Parse the pattern value
patternValue = parse(wordParser, PatternValueBNF.ID, tolerant);
int count = wordParser.skipLeadingWhitespace();
// Parse 'ESCAPE'
if (wordParser.startsWithIdentifier(ESCAPE)) {
hasSpaceAfterPatternValue = (count > 0);
count = 0;
escapeIdentifier = wordParser.moveForward(ESCAPE);
hasSpaceAfterEscape = wordParser.skipLeadingWhitespace() > 0;
}
else if (tolerant) {
hasSpaceAfterPatternValue = (count > 0);
}
else {
wordParser.moveBackward(count);
return;
}
// Parse escape character
char character = wordParser.character();
// Single escape character
if (character == SINGLE_QUOTE) {
escapeCharacter = new StringLiteral(this, wordParser.word());
escapeCharacter.parse(wordParser, tolerant);
count = 0;
}
// Parse input parameter
else if (ExpressionTools.isParameter(character)) {
escapeCharacter = new InputParameter(this, wordParser.word());
escapeCharacter.parse(wordParser, tolerant);
count = 0;
}
// Parse with the BNF
else {
escapeCharacter = parse(wordParser, LikeExpressionEscapeCharacterBNF.ID, tolerant);
if ((escapeIdentifier == null) &&
(escapeCharacter == null) &&
!wordParser.isTail()) {
hasSpaceAfterPatternValue = false;
wordParser.moveBackward(count);
}
}
}
@Override
protected void toParsedText(StringBuilder writer, boolean actual) {
// String expression
if (stringExpression != null) {
stringExpression.toParsedText(writer, actual);
writer.append(SPACE);
}
// 'NOT LIKE' or 'LIKE'
if (actual) {
if (notIdentifier != null) {
writer.append(notIdentifier);
writer.append(SPACE);
}
writer.append(likeIdentifier);
}
else {
writer.append((notIdentifier != null) ? NOT_LIKE : LIKE);
}
if (hasSpaceAfterLike) {
writer.append(SPACE);
}
// Pattern value
if (patternValue != null) {
patternValue.toParsedText(writer, actual);
}
if (hasSpaceAfterPatternValue) {
writer.append(SPACE);
}
// 'ESCAPE'
if (escapeIdentifier != null) {
writer.append(actual ? escapeIdentifier : ESCAPE);
}
if (hasSpaceAfterEscape) {
writer.append(SPACE);
}
// Escape character
if (escapeCharacter != null) {
escapeCharacter.toParsedText(writer, actual);
}
}
}