Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[master] Issue 1755: Add support for non-breaking spaces #1759

Merged
merged 2 commits into from
Dec 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2022 IBM Corporation. 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
*/
package org.eclipse.persistence.jpa.test.jpql;

import java.util.List;

import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.TypedQuery;

import org.eclipse.persistence.jpa.test.framework.DDLGen;
import org.eclipse.persistence.jpa.test.framework.Emf;
import org.eclipse.persistence.jpa.test.framework.EmfRunner;
import org.eclipse.persistence.jpa.test.jpql.model.NoResultEntity;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;

@RunWith(EmfRunner.class)
public class TestWhiteSpace {

@Emf(name = "noResultEMF", createTables = DDLGen.DROP_CREATE, classes = { NoResultEntity.class })
private EntityManagerFactory noResultEmf;

/**
* Complex test of the aggregate functions in JPQL.
* For this test, there must be zero results in the entity table and the Entity state field
* must be a primitive type.
* JPA 2.1 specification; Section 4.8.5 states aggregate functions (MIN, MAX, AVG, & SUM)
* must return a result of NULL if there are no values to apply the aggregate function to
*/
@Test
public void testNonBreakingSpace() {
EntityManager em = noResultEmf.createEntityManager();
try {
String queryString = "SELECT n FROM\u00A0NoResultEntity n";
TypedQuery<NoResultEntity> checkQuery = em.createQuery(queryString, NoResultEntity.class);
List<NoResultEntity> checkResult = checkQuery.getResultList();
Assert.assertEquals("Entity table NoResultEntity must be empty for this test", 0, checkResult.size());

queryString = "SELECT n FROM NoResultEntity n WHERE n.id =\u00A0:idparam";
checkQuery = em.createQuery(queryString, NoResultEntity.class);
checkQuery.setParameter("idparam", 22);
checkResult = checkQuery.getResultList();
Assert.assertEquals("Entity table NoResultEntity must be empty for this test", 0, checkResult.size());
} finally {
if (em.getTransaction().isActive()) {
em.getTransaction().rollback();
}
if (em.isOpen()) {
em.close();
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2006, 2022 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2022 IBM Corporation. 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
Expand Down Expand Up @@ -1168,6 +1169,7 @@ protected JPQLGrammar getGrammar() {
/**
* Returns the registered helper that was cached with the given id.
*
* @param <T> This is the type parameter
* @param id The key used to retrieve the cached helper, if one was cached
* @return Either the cached helper or <code>null</code> if no helper was previously cached for
* the given id
Expand Down Expand Up @@ -1682,6 +1684,7 @@ protected void validateAbstractConditionalClause(AbstractConditionalClause expre
* Validates the content of an {@link AbstractDoubleEncapsulatedExpression}, which encapsulates
* two expressions separated by a comma.
*
* @param <T> This is the type parameter
* @param expression The {@link AbstractDoubleEncapsulatedExpression} to validate
* @param helper This helper is used to retrieve specific information related to the {@link
* AbstractDoubleEncapsulatedExpression expression} being validated
Expand Down Expand Up @@ -3033,7 +3036,7 @@ public void visit(DateTime expression) {
int endPosition = startPosition;

for (int index = 1; index < length; index++) {
if (Character.isWhitespace(dateTime.charAt(index))) {
if (ExpressionTools.isWhiteSpace(dateTime.charAt(index))) {
break;
}
endPosition++;
Expand All @@ -3051,7 +3054,7 @@ else if (!dateTime.startsWith("{d '") &&
for (int index = 1; index < length; index++) {
startPosition++;

if (Character.isWhitespace(dateTime.charAt(index))) {
if (ExpressionTools.isWhiteSpace(dateTime.charAt(index))) {
break;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2006, 2021 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021 IBM Corporation. All rights reserved.
* Copyright (c) 2006, 2022 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, 2022 IBM Corporation. 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
Expand Down Expand Up @@ -518,6 +518,7 @@ protected boolean validateAbsExpression(AbsExpression expression) {
* identification variable declarations.
*
* @param expression The {@link AbstractFromClause} to validate
* @param visitor The {@link FirstDeclarationVisitor} to validate
*/
protected void validateAbstractFromClause(AbstractFromClause expression,
FirstDeclarationVisitor visitor) {
Expand Down Expand Up @@ -765,6 +766,8 @@ protected boolean validateAvgFunction(AvgFunction expression) {
* </ul>
*
* @param expression The {@link BetweenExpression} to validate
* @return A number indicating the validation result. {@link #isValid(int, int)} can be used to
* determine the validation status of an expression based on its position
*/
protected int validateBetweenExpression(BetweenExpression expression) {

Expand Down Expand Up @@ -874,6 +877,8 @@ protected int validateCollectionMemberExpression(CollectionMemberExpression expr
* @param expression The {@link Expression} to validate
* @param collectionTypeOnly <code>true</code> to make sure the path expression resolves to a
* collection mapping only; <code>false</code> if it can simply resolves to a relationship mapping
* @return <code>false</code> if the encapsulated expression was validated and is invalid;
* <code>true</code> otherwise
*/
protected boolean validateCollectionValuedPathExpression(Expression expression,
boolean collectionTypeOnly) {
Expand Down Expand Up @@ -940,6 +945,8 @@ else if (collectionTypeOnly && !helper.isCollectionMapping(mapping) ||
* @param expression The {@link Expression} to validate
* @param collectionTypeOnly <code>true</code> to make sure the path expression resolves to a
* collection mapping only; <code>false</code> if it can simply resolves to a relationship mapping
* @return <code>false</code> if the encapsulated expression was validated and is invalid;
* <code>true</code> otherwise
*/
protected boolean validateJoinCollectionValuedPathExpression(Expression expression,
boolean collectionTypeOnly) {
Expand Down Expand Up @@ -1604,6 +1611,8 @@ protected void validateIdentificationVariables() {
* makes sure is it defined in <code><b>IN</b></code> or <code><b>IN</b></code> expression.
*
* @param expression The {@link IndexExpression} to validate
* @return <code>false</code> if the encapsulated expression was validated and is invalid;
* <code>true</code> otherwise
*/
protected boolean validateIndexExpression(IndexExpression expression) {

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2006, 2021 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2006, 2022 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
Expand All @@ -23,7 +23,6 @@
import java.util.Map;
import java.util.Set;

import org.eclipse.persistence.jpa.jpql.parser.AbstractExpression;
import org.eclipse.persistence.jpa.jpql.parser.AbstractExpressionVisitor;
import org.eclipse.persistence.jpa.jpql.parser.AbstractTraverseParentVisitor;
import org.eclipse.persistence.jpa.jpql.parser.AnonymousExpressionVisitor;
Expand Down Expand Up @@ -508,6 +507,7 @@ protected void initialize() {
* nested array, the given {@link Expression} is a {@link SubExpression} and its child is a
* {@link CollectionExpression}.
*
* @param expression The {@link Expression} to check its size
* @return <code>true</code> if the given {@link Expression} is a nested array; <code>false</code> otherwise
* @since 2.5
*/
Expand Down Expand Up @@ -594,6 +594,8 @@ protected boolean isValid(Expression expression, String... queryBNFIds) {
* will be bypassed.
*
* @param expression The {@link Expression} to validate based on the query BNF
* @param queryBNF The unique identifier of the {@link JPQLQueryBNF} that looks up
* the {@link JPQLQueryBNFValidator}
* @return <code>true</code> if the {@link Expression} part is a child of the given query BNF;
* <code>false</code> otherwise
*/
Expand Down Expand Up @@ -690,6 +692,7 @@ protected String literal(Expression expression, LiteralType type) {
* To be a nested array, the given {@link Expression} is a {@link SubExpression} and its child is
* a {@link CollectionExpression}.
*
* @param expression The {@link Expression} to visit
* @return The number of items in the array or -1 if the {@link Expression} is not a nested array
* @since 2.5
*/
Expand Down Expand Up @@ -903,8 +906,10 @@ public boolean isValid() {
}

/**
* Sets
*
* Sets bypassCompound
*
* @param bypassCompound Indicates whether a {@link JPQLQueryBNF} representing a compound
* expression should be considered when doing the validation
*/
public void setBypassCompound(boolean bypassCompound) {
this.bypassCompound = bypassCompound;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2006, 2021 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2006, 2022 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2022 IBM Corporation. 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
Expand Down Expand Up @@ -239,6 +240,17 @@ public static boolean isQuote(char character) {
character == '\"';
}

/**
* Determines whether the given character is whitespace. Supports non-breaking space characters
*
* @param character The character to check if it's whitespace
* @return <code>true</code> if the given character is either whitespace or non-breaking space; <code>false</code> otherwise
*/
public static boolean isWhiteSpace(char character) {
return Character.isWhitespace(character) ||
Character.isSpaceChar(character);
}

/**
* Returns the given string literal by wrapping it with single quotes. Any single quote within
* the string will be escaped with another single quote.
Expand Down Expand Up @@ -292,8 +304,8 @@ public static void reposition(CharSequence query1, int[] positions, CharSequence
char character1 = Character.toUpperCase(query1.charAt(index1));
char character2 = Character.toUpperCase(query2.charAt(index2));

boolean whitespace1 = Character.isWhitespace(character1);
boolean whitespace2 = Character.isWhitespace(character2);
boolean whitespace1 = ExpressionTools.isWhiteSpace(character1);
boolean whitespace2 = ExpressionTools.isWhiteSpace(character2);

if (character1 != character2) {

Expand Down Expand Up @@ -459,7 +471,7 @@ public static boolean stringIsEmpty(CharSequence text) {
}

for (int i = text.length(); i-- > 0;) {
if (!Character.isWhitespace(text.charAt(i))) {
if (!ExpressionTools.isWhiteSpace(text.charAt(i))) {
return false;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2006, 2021 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2006, 2022 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2022 IBM Corporation. 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
Expand Down Expand Up @@ -229,12 +230,12 @@ public boolean isTail() {
* @return <code>true</code> if the character can be part of a word; <code>false</code> if it is
* not an alphanumeric character, which usually means is a whitespace, a delimiter or an
* arithmetic symbol
* @see Character#isWhitespace(char)
* @see ExpressionTools#isWhiteSpace(char)
* @see #isArithmeticSymbol(char)
* @see #isDelimiter(char)
*/
public boolean isWordSeparator(char character) {
return Character.isWhitespace(character) ||
return ExpressionTools.isWhiteSpace(character) ||
isDelimiter(character) ||
isArithmeticSymbol(character);
}
Expand Down Expand Up @@ -311,9 +312,9 @@ public String moveForwardIgnoreWhitespace(CharSequence word) {
// Handle testing
// 1. "... GROUP BY" and "GROUP BY"
// 2. "... GROUP\nBY" and "GROUP BY"
if (Character.isWhitespace(c1)) {
if (ExpressionTools.isWhiteSpace(c1)) {

if (Character.isWhitespace(c2)) {
if (ExpressionTools.isWhiteSpace(c2)) {
sb.append(' ');
continue;
}
Expand Down Expand Up @@ -397,10 +398,7 @@ public int partialWordStartPosition(int position) {
for (int index = position; --index >= 0; ) {
char character = text.charAt(index);

if (Character.isWhitespace(character) ||
isDelimiter(character) ||
isArithmeticSymbol(character)) {

if (isWordSeparator(character)) {
break;
}

Expand Down Expand Up @@ -613,7 +611,7 @@ public int skipLeadingWhitespace() {
while (cursor < length) {

char character = text.charAt(cursor);
if (!Character.isWhitespace(character)) {
if (!ExpressionTools.isWhiteSpace(character)) {
break;
}

Expand Down Expand Up @@ -788,9 +786,9 @@ public boolean startsWithIdentifier(CharSequence identifier, int position) {
// Handle testing
// 1. "... GROUP BY" and "GROUP BY"
// 2. "... GROUP\nBY" and "GROUP BY"
if (Character.isWhitespace(c1)) {
if (ExpressionTools.isWhiteSpace(c1)) {

if (Character.isWhitespace(c2)) {
if (ExpressionTools.isWhiteSpace(c2)) {
continue;
}

Expand Down Expand Up @@ -964,7 +962,7 @@ public int whitespaceCount(int position) {
for (int index = position; index < length; index++) {
char character = text.charAt(index);

if (!Character.isWhitespace(character)) {
if (!ExpressionTools.isWhiteSpace(character)) {
return index - position;
}
}
Expand Down Expand Up @@ -1147,7 +1145,7 @@ else if (isWordSeparator(character)) {
continue;
}

if (Character.isWhitespace(character) ||
if (ExpressionTools.isWhiteSpace(character) ||
isDelimiter(character) ||
character == '>' ||
character == '<' ||
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2006, 2022 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2022 IBM Corporation. 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
Expand Down Expand Up @@ -439,7 +440,7 @@ else if (isValidVersion(identifier)) {

char character = wordParser.character(position - fragment.length() - 1);

if (Character.isWhitespace(character)) {
if (ExpressionTools.isWhiteSpace(character)) {
proposals.addIdentifier(identifier);
break;
}
Expand Down