/******************************************************************************* * Copyright (c) 2006, 2015 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 v1.0 and Eclipse Distribution License v. 1.0 * which accompanies this distribution. * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * 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; /** * <div><b>BNF:</b> <code>general_case_expression ::= CASE when_clause {when_clause}* ELSE scalar_expression END</code></div> * or * <div><b>BNF:</b> <code>simple_case_expression ::= CASE case_operand simple_when_clause {simple_when_clause}* ELSE scalar_expression END</code><p></div> * * @version 2.5 * @since 2.3 * @author Pascal Filion */ public final class CaseExpression extends AbstractExpression { /** * The actual <b>CASE</b> identifier found in the string representation of the JPQL query. */ private String caseIdentifier; /** * The {@link Expression} representing the case operand. */ private AbstractExpression caseOperand; /** * The {@link Expression} representing the <b>ELSE</b> expression. */ private AbstractExpression elseExpression; /** * The actual <b>ELSE</b> identifier found in the string representation of the JPQL query. */ private String elseIdentifier; /** * The actual <b>END</b> identifier found in the string representation of the JPQL query. */ private String endIdentifier; /** * Determines whether a whitespace was parsed after <b>CASE</b>. */ private boolean hasSpaceAfterCase; /** * Determines whether a whitespace was parsed after the case operand. */ private boolean hasSpaceAfterCaseOperand; /** * Determines whether a whitespace was parsed after <b>ELSE</b>. */ private boolean hasSpaceAfterElse; /** * Determines whether a whitespace was parsed after the <b>BETWEEN</b> expression. */ private boolean hasSpaceAfterElseExpression; /** * Determines whether a whitespace was parsed after <b>WHEN</b> clause. */ private boolean hasSpaceAfterWhenClauses; /** * Used to determine how to check if the parsing is complete. */ private ParsingType parsingType; /** * The {@link Expression} representing the <b>WHEN</b> clauses. */ private AbstractExpression whenClauses; /** * Creates a new <code>CaseExpression</code>. * * @param parent The parent of this expression */ public CaseExpression(AbstractExpression parent) { super(parent, CASE); } /** * {@inheritDoc} */ public void accept(ExpressionVisitor visitor) { visitor.visit(this); } /** * {@inheritDoc} */ public void acceptChildren(ExpressionVisitor visitor) { getCaseOperand().accept(visitor); getWhenClauses().accept(visitor); getElseExpression().accept(visitor); } /** * {@inheritDoc} */ @Override protected void addChildrenTo(Collection<Expression> children) { children.add(getCaseOperand()); children.add(getWhenClauses()); children.add(getElseExpression()); } /** * {@inheritDoc} */ @Override protected void addOrderedChildrenTo(List<Expression> children) { // 'CASE' children.add(buildStringExpression(CASE)); if (hasSpaceAfterCase) { children.add(buildStringExpression(SPACE)); } // Case operand if (caseOperand != null) { children.add(caseOperand); } if (hasSpaceAfterCaseOperand) { children.add(buildStringExpression(SPACE)); } // WHEN clauses if (whenClauses != null) { children.add(whenClauses); } if (hasSpaceAfterWhenClauses) { children.add(buildStringExpression(SPACE)); } // 'ELSE' if (elseIdentifier != null) { children.add(buildStringExpression(ELSE)); } if (hasSpaceAfterElse) { children.add(buildStringExpression(SPACE)); } // Else expression if (elseExpression != null) { children.add(elseExpression); } if (hasSpaceAfterElseExpression) { children.add(buildStringExpression(SPACE)); } // 'END' if (endIdentifier != null) { children.add(buildStringExpression(END)); } } /** * {@inheritDoc} */ @Override public JPQLQueryBNF findQueryBNF(Expression expression) { if ((caseOperand != null) && caseOperand.isAncestor(expression)) { return getQueryBNF(CaseOperandBNF.ID); } if ((whenClauses != null) && whenClauses.isAncestor(expression)) { return getQueryBNF(WhenClauseBNF.ID); } if ((elseExpression != null) && elseExpression.isAncestor(expression)) { return getQueryBNF(ElseExpressionBNF.ID); } return super.findQueryBNF(expression); } /** * Returns the actual <b>CASE</b> identifier found in the string representation of the JPQL * query, which has the actual case that was used. * * @return The <b>CASE</b> identifier that was actually parsed */ public String getActualCaseIdentifier() { return caseIdentifier; } /** * Returns the actual <b>ELSE</b> identifier found in the string representation of the JPQL * query, which has the actual case that was used. * * @return The <b>ELSE</b> identifier that was actually parsed, or an empty string if it was not * parsed */ public String getActualElseIdentifier() { return (elseIdentifier != null) ? elseIdentifier : ExpressionTools.EMPTY_STRING; } /** * Returns the actual <b>END</b> identifier found in the string representation of the JPQL query, * which has the actual case that was used. * * @return The <b>END</b> identifier that was actually parsed, or an empty string if it was not * parsed */ public String getActualEndIdentifier() { return (endIdentifier != null) ? endIdentifier : ExpressionTools.EMPTY_STRING; } /** * Returns the {@link Expression} that represents the <b>CASE</b> operand. * * @return The expression that was parsed representing the <b>CASE</b> operand */ public Expression getCaseOperand() { if (caseOperand == null) { caseOperand = buildNullExpression(); } return caseOperand; } /** * Returns the {@link Expression} that represents the <b>ELSE</b> operand. * * @return The expression that was parsed representing the <b>ELSE</b> operand */ public AbstractExpression getElseExpression() { if (elseExpression == null) { elseExpression = buildNullExpression(); } return elseExpression; } /** * {@inheritDoc} */ public JPQLQueryBNF getQueryBNF() { return getQueryBNF(GeneralCaseExpressionBNF.ID); } /** * Returns the {@link Expression} that represents the <b>WHEN</b> clauses. * * @return The expression that was parsed representing the <b>WHEN</b> clauses */ public AbstractExpression getWhenClauses() { if (whenClauses == null) { whenClauses = buildNullExpression(); } return whenClauses; } /** * Determines whether the <b>CASE</b> operand was parsed. * * @return <code>true</code> if the <b>CASE</b> operand was parsed; <code>false</code> otherwise */ public boolean hasCaseOperand() { return caseOperand != null && !caseOperand.isNull(); } /** * Determines whether the identifier <b>ELSE</b> was parsed. * * @return <code>true</code> if the identifier <b>ELSE</b> was parsed; <code>false</code> * otherwise */ public boolean hasElse() { return elseIdentifier != null; } /** * Determines whether the <b>ELSE</b> expression was parsed. * * @return <code>true</code> if the <b>ELSE</b> expression was parsed; <code>false</code> * otherwise */ public boolean hasElseExpression() { return elseExpression != null && !elseExpression.isNull(); } /** * Determines whether the identifier <b>END</b> was parsed. * * @return <code>true</code> if the identifier <b>END</b> was parsed; <code>false</code> otherwise */ public boolean hasEnd() { return endIdentifier != null; } /** * Determines whether a whitespace was parsed after <b>CASE</b>. * * @return <code>true</code> if there was a whitespace after <b>CASE</b>; <code>false</code> * otherwise */ public boolean hasSpaceAfterCase() { return hasSpaceAfterCase; } /** * Determines whether a whitespace was parsed after the case operand. * * @return <code>true</code> if there was a whitespace after the case operand; <code>false</code> * otherwise */ public boolean hasSpaceAfterCaseOperand() { return hasSpaceAfterCaseOperand; } /** * Determines whether a whitespace was parsed after <b>ELSE</b>. * * @return <code>true</code> if there was a whitespace after <b>ELSE</b>; <code>false</code> * otherwise */ public boolean hasSpaceAfterElse() { return hasSpaceAfterElse; } /** * Determines whether a whitespace was parsed after the else expression. * * @return <code>true</code> if there was a whitespace after the else expression; * <code>false</code> otherwise */ public boolean hasSpaceAfterElseExpression() { return hasSpaceAfterElseExpression; } /** * Determines whether a whitespace was parsed after the <b>WHEN</b> clause. * * @return <code>true</code> if there was a whitespace after the <b>WHEN</b> * clause; <code>false</code> otherwise */ public boolean hasSpaceAfterWhenClauses() { return hasSpaceAfterWhenClauses; } /** * Determines whether the <b>WHEN</b> clauses were parsed. * * @return <code>true</code> if the <b>WHEN</b> clauses were parsed; * <code>false</code> otherwise */ public boolean hasWhenClauses() { return whenClauses != null && !whenClauses.isNull(); } /** * {@inheritDoc} */ @Override protected boolean isParsingComplete(WordParser wordParser, String word, Expression expression) { // 'CASE' if (parsingType == ParsingType.CASE) { return word.equalsIgnoreCase(WHEN) || word.equalsIgnoreCase(ELSE) || word.equalsIgnoreCase(END) || super.isParsingComplete(wordParser, word, expression); } // 'WHEN' if (parsingType == ParsingType.WHEN) { return word.equalsIgnoreCase(ELSE) || word.equalsIgnoreCase(END) || super.isParsingComplete(wordParser, word, expression); } // 'ELSE' return word.equalsIgnoreCase(END) || super.isParsingComplete(wordParser, word, expression); } /** * {@inheritDoc} */ @Override protected void parse(WordParser wordParser, boolean tolerant) { // Parse 'CASE' caseIdentifier = wordParser.moveForward(CASE); hasSpaceAfterCase = wordParser.skipLeadingWhitespace() > 0; // Parse case operand parsingType = ParsingType.CASE; if (!wordParser.startsWithIdentifier(WHEN)) { caseOperand = parse(wordParser, CaseOperandBNF.ID, tolerant); hasSpaceAfterCaseOperand = wordParser.skipLeadingWhitespace() > 0; } // Parse the WHEN clauses parsingType = ParsingType.WHEN; whenClauses = parse(wordParser, WhenClauseBNF.ID, tolerant); hasSpaceAfterWhenClauses = wordParser.skipLeadingWhitespace() > 0; // Parse 'ELSE' if (!tolerant || wordParser.startsWithIdentifier(ELSE)) { elseIdentifier = wordParser.moveForward(ELSE); hasSpaceAfterElse = wordParser.skipLeadingWhitespace() > 0; } // Parse the ELSE expression parsingType = ParsingType.ELSE; elseExpression = parse(wordParser, ElseExpressionBNF.ID, tolerant); hasSpaceAfterElseExpression = wordParser.skipLeadingWhitespace() > 0; // Parse 'END' if (!tolerant || wordParser.startsWithIdentifier(END)) { endIdentifier = wordParser.moveForward(END); } } /** * {@inheritDoc} */ @Override protected void toParsedText(StringBuilder writer, boolean actual) { // 'CASE' writer.append(actual ? caseIdentifier : CASE); if (hasSpaceAfterCase) { writer.append(SPACE); } // Case Operand if (caseOperand != null) { caseOperand.toParsedText(writer, actual); } if (hasSpaceAfterCaseOperand) { writer.append(SPACE); } // When clauses if (whenClauses != null) { whenClauses.toParsedText(writer, actual); } if (hasSpaceAfterWhenClauses) { writer.append(SPACE); } // 'ELSE' if (elseIdentifier != null) { writer.append(actual ? elseIdentifier : ELSE); } if (hasSpaceAfterElse) { writer.append(SPACE); } // Else expression if (elseExpression != null) { elseExpression.toParsedText(writer, actual); } if (hasSpaceAfterElseExpression) { writer.append(SPACE); } // 'END' if (endIdentifier != null) { writer.append(actual ? endIdentifier : END); } } /** * A enumeration used to determine how {@link CaseExpression#isParsingComplete(WordParser, String)} * should behaves. */ private enum ParsingType { CASE, ELSE, WHEN } }