Repository: cayenne Updated Branches: refs/heads/master 89439015f -> eb0373e81
http://git-wip-us.apache.org/repos/asf/cayenne/blob/c1272b17/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ExpressionUtils.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ExpressionUtils.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ExpressionUtils.java new file mode 100644 index 0000000..7325b15 --- /dev/null +++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ExpressionUtils.java @@ -0,0 +1,52 @@ +/***************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + ****************************************************************/ + +package org.apache.cayenne.exp.parser; + +import java.util.HashMap; +import java.util.Map; + +/** + * @since 4.2 + */ +class ExpressionUtils { + + static void parsePath(ASTPath pathExp, Object path) throws ParseException { + if (path != null && path.toString().contains("#")) { + String[] pathSegments = path.toString().split("\\."); + Map<String, String> aliasMap = new HashMap<>(); + for (int i = 0; i < pathSegments.length; i++) { + if (pathSegments[i].contains("#")) { + String[] splitedSegment = pathSegments[i].split("#"); + splitedSegment[0] += splitedSegment[1].endsWith("+") ? "+" : ""; + splitedSegment[1] = splitedSegment[1].endsWith("+") ? splitedSegment[1].substring(0, splitedSegment[1].length() - 1) : splitedSegment[1]; + if (aliasMap.putIfAbsent(splitedSegment[1], splitedSegment[0]) != null && !aliasMap.get(splitedSegment[1]).equals(splitedSegment[0])) { + throw new ParseException("Can't add the same alias to different path segments."); + } + pathSegments[i] = splitedSegment[1]; + } + } + pathExp.setPath(String.join(".", pathSegments)); + pathExp.setPathAliases(aliasMap); + } else { + pathExp.setPath(path); + } + } + +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/c1272b17/cayenne-server/src/main/jjtree/org/apache/cayenne/exp/parser/ExpressionParser.jjt ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/jjtree/org/apache/cayenne/exp/parser/ExpressionParser.jjt b/cayenne-server/src/main/jjtree/org/apache/cayenne/exp/parser/ExpressionParser.jjt index 39dddea..d77c01e 100644 --- a/cayenne-server/src/main/jjtree/org/apache/cayenne/exp/parser/ExpressionParser.jjt +++ b/cayenne-server/src/main/jjtree/org/apache/cayenne/exp/parser/ExpressionParser.jjt @@ -37,6 +37,7 @@ package org.apache.cayenne.exp.parser; import java.math.BigDecimal; import java.math.BigInteger; import org.apache.cayenne.exp.Expression; +import org.apache.cayenne.exp.parser.ExpressionUtils; /** * Parser of Cayenne Expressions. @@ -579,11 +580,11 @@ void pathExpression() : { } { ( - t = <PROPERTY_PATH> { jjtThis.setPath(t.image); } #ObjPath(0) + t = <PROPERTY_PATH> { ExpressionUtils.parsePath(jjtThis, t.image); } #ObjPath(0) | - "obj:" t = <PROPERTY_PATH> { jjtThis.setPath(t.image); } #ObjPath(0) + "obj:" t = <PROPERTY_PATH> { ExpressionUtils.parsePath(jjtThis, t.image); } #ObjPath(0) | - "db:" t = <PROPERTY_PATH> { jjtThis.setPath(t.image); } #DbPath(0) + "db:" t = <PROPERTY_PATH> { ExpressionUtils.parsePath(jjtThis, t.image); } #DbPath(0) | "enum:" t = <PROPERTY_PATH> { jjtThis.setEnumValue(t.image); } #Enum(0) ) @@ -599,7 +600,7 @@ TOKEN : { TOKEN : { - < IDENTIFIER: <LETTER> (<LETTER>|<DIGIT>)* (["+"])? > + < IDENTIFIER: <LETTER> (<LETTER>|<DIGIT>)* (["#"]<LETTER> (<LETTER>|<DIGIT>)*)? (["+"])? > | < #LETTER: ["_","a"-"z","A"-"Z"] > | http://git-wip-us.apache.org/repos/asf/cayenne/blob/c1272b17/cayenne-server/src/test/java/org/apache/cayenne/exp/ExpressionFactoryTest.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/test/java/org/apache/cayenne/exp/ExpressionFactoryTest.java b/cayenne-server/src/test/java/org/apache/cayenne/exp/ExpressionFactoryTest.java index 60a44d9..fad6864 100644 --- a/cayenne-server/src/test/java/org/apache/cayenne/exp/ExpressionFactoryTest.java +++ b/cayenne-server/src/test/java/org/apache/cayenne/exp/ExpressionFactoryTest.java @@ -31,8 +31,10 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import org.apache.cayenne.CayenneRuntimeException; import org.apache.cayenne.exp.parser.ASTLike; import org.apache.cayenne.exp.parser.ASTLikeIgnoreCase; +import org.apache.cayenne.exp.parser.ASTObjPath; import org.apache.cayenne.exp.parser.ASTTrim; import org.junit.Before; import org.junit.Test; @@ -513,6 +515,26 @@ public class ExpressionFactoryTest { assertEquals(ASTTrim.class, e.getClass()); } + @Test + public void testExpWithAlias() { + Expression expression = ExpressionFactory.exp("paintings#p1.galleries#p2.name = 'Test'"); + assertEquals("p1.p2.name", expression.getOperand(0).toString()); + assertEquals("paintings", ((ASTObjPath)expression.getOperand(0)).getPathAliases().get("p1")); + assertEquals("galleries", ((ASTObjPath)expression.getOperand(0)).getPathAliases().get("p2")); + } + + @Test + public void testExpWithAliasAndOuterJoin() { + Expression expression = ExpressionFactory.exp("paintings#p1+.name = 'Test'"); + assertEquals("p1.name", expression.getOperand(0).toString()); + assertEquals("paintings+", ((ASTObjPath)expression.getOperand(0)).getPathAliases().get("p1")); + } + + @Test(expected = CayenneRuntimeException.class) + public void testExpWithTheSameAliasToDiffSegments() { + ExpressionFactory.exp("paintings#p1.gallery#p1.name = 'Test'"); + } + // CAY-2081 @Test(expected = ExpressionException.class) public void testExceptionInParse() { http://git-wip-us.apache.org/repos/asf/cayenne/blob/c1272b17/cayenne-server/src/test/java/org/apache/cayenne/exp/property/PathAliasesIT.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/test/java/org/apache/cayenne/exp/property/PathAliasesIT.java b/cayenne-server/src/test/java/org/apache/cayenne/exp/property/PathAliasesIT.java index 45fdee7..37b3f16 100644 --- a/cayenne-server/src/test/java/org/apache/cayenne/exp/property/PathAliasesIT.java +++ b/cayenne-server/src/test/java/org/apache/cayenne/exp/property/PathAliasesIT.java @@ -150,8 +150,8 @@ public class PathAliasesIT extends ServerCase { @Test public void testAliasForPath() { - ASTPath astPath = new ASTObjPath("a.galleryName"); - astPath.setPathAliases(Collections.singletonMap("a", "paintingArray.toGallery")); + ASTPath astPath = new ASTObjPath("paintingArray.a.galleryName"); + astPath.setPathAliases(Collections.singletonMap("a", "toGallery")); ASTEqual astEqual = new ASTEqual(astPath, "tate modern"); List<Object[]> artists = ObjectSelect.columnQuery(Artist.class, Artist.ARTIST_NAME, PropertyFactory.createBase(astPath, String.class)) .where(astEqual) @@ -194,4 +194,124 @@ public class PathAliasesIT extends ServerCase { query.and(Artist.PAINTING_ARRAY.dot(Painting.TO_GALLERY).alias("p1").dot(Gallery.GALLERY_NAME).eq("g1")); query.select(context); } + + @Test + public void testExpWithAliases() { + Expression e1 = ExpressionFactory.exp("paintingArray#p1.paintingTitle = 'painting2'"); + Expression e2 = ExpressionFactory.exp("paintingArray#p2.paintingTitle = 'painting4'"); + List<Artist> artists = ObjectSelect.query(Artist.class) + .where(e1) + .and(e2) + .select(context); + assertEquals(1, artists.size()); + assertEquals("artist4", artists.get(0).getArtistName()); + } + + @Test + public void testExpWithSeveralAliases() { + Expression e1 = ExpressionFactory.exp("paintingArray#p1.toGallery#g1.galleryName = 'tate modern'"); + List<Artist> artists = ObjectSelect.query(Artist.class) + .where(e1) + .orderBy(Artist.ARTIST_NAME.asc()) + .select(context); + assertEquals(5, artists.size()); + assertEquals("artist1", artists.get(0).getArtistName()); + } + + @Test + public void testMiddleAliasForExp() { + Expression e1 = ExpressionFactory.exp("paintingArray.toGallery.paintingArray#p1.paintingTitle = 'painting2'"); + Expression e2 = ExpressionFactory.exp("paintingArray.toGallery.paintingArray#p2.paintingTitle = 'painting4'"); + List<Artist> artists = ObjectSelect.query(Artist.class) + .where(e1) + .and(e2) + .select(context); + assertEquals(1, artists.size()); + assertEquals("artist4", artists.get(0).getArtistName()); + } + + @Test + public void testEntityPropertyAliasesInExp() { + Painting painting2 = Cayenne.objectForPK(context, Painting.class, 2); + Painting painting4 = Cayenne.objectForPK(context, Painting.class, 4); + + SelectQuery<Artist> query = SelectQuery.query(Artist.class); + Expression e1 = ExpressionFactory.and( + ExpressionFactory.exp("paintingArray#p1 = $painting1", painting2), + ExpressionFactory.exp("paintingArray#p2 = $painting2", painting4)); + query.setQualifier(e1); + List<Artist> artists = query.select(context); + assertEquals(1, artists.size()); + assertEquals("artist4", artists.get(0).getArtistName()); + } + + @Test + public void testAliasForPathExp() { + ASTPath astPath = new ASTObjPath("paintingArray.p1.galleryName"); + astPath.setPathAliases(Collections.singletonMap("a", "toGallery")); + ASTEqual astEqual = new ASTEqual(astPath, "test gallery"); + Expression e1 = ExpressionFactory.exp("paintingArray.toGallery#p1.galleryName"); + List<Object[]> artists = ObjectSelect.columnQuery(Artist.class, Artist.ARTIST_NAME, PropertyFactory.createBase(e1, String.class)) + .where(astEqual) + .select(context); + assertEquals(1, artists.size()); + assertEquals("artist4", artists.get(0)[0]); + assertEquals("test gallery", artists.get(0)[1]); + } + + @Test + public void testTheSameAliasesForExp() { + Expression e1 = ExpressionFactory.exp("paintingArray#p1.paintingTitle"); + Expression e2 = ExpressionFactory.exp("paintingArray#p2.paintingTitle"); + Expression e3 = ExpressionFactory.exp("paintingArray#p1.paintingTitle = 'painting2'"); + Expression e4 = ExpressionFactory.exp("paintingArray#p2.paintingTitle = 'painting4'"); + List<Object[]> results = ObjectSelect.columnQuery(Artist.class, + Artist.ARTIST_NAME, + PropertyFactory.createBase(e1, String.class), + PropertyFactory.createBase(e2, String.class)) + .where(e3) + .and(e4) + .select(context); + assertEquals(1, results.size()); + assertEquals("artist4", results.get(0)[0]); + assertEquals("painting2", results.get(0)[1]); + assertEquals("painting4", results.get(0)[2]); + } + + @Test + public void testOrderWithAliasForExp() { + Expression e1 = ExpressionFactory.exp("paintingArray#p1.estimatedPrice"); + ObjectSelect<Artist> query = ObjectSelect.query(Artist.class) + .orderBy(PropertyFactory.createBase(e1, Number.class).asc()) + .prefetch(Artist.PAINTING_ARRAY.disjoint()); + List<Artist> artists = query.select(context); + assertEquals(5, artists.size()); + assertEquals(2, artists.get(0).getPaintingArray().size()); + } + + @Test + public void testAggregationWithAliasesForExp() { + Expression e1 = ExpressionFactory.exp("paintingArray#p1"); + List<Object[]> artistAndPaintingCount = ObjectSelect.columnQuery(Artist.class, Artist.ARTIST_NAME, Artist.PAINTING_ARRAY.count()) + .having(PropertyFactory.createBase(e1, Number.class).count().lt(5L)) + .select(context); + assertEquals(4, artistAndPaintingCount.size()); + assertTrue((Long)artistAndPaintingCount.get(0)[1] < 5); + } + + @Test(expected = CayenneRuntimeException.class) + public void testPrefetchWithAliasesForExp() { + Expression e1 = ExpressionFactory.exp("paintingArray#p1"); + ObjectSelect<Artist> query = ObjectSelect.query(Artist.class); + query.prefetch(PropertyFactory.createList("p1", e1, Painting.class).disjoint()); + query.select(context); + } + + @Test(expected = CayenneRuntimeException.class) + public void testExpWithAliasNotToRelSegment() { + Expression e1 = ExpressionFactory.exp("paintingArray.paintingTitle#p1 = 'painting2'"); + ObjectSelect.query(Artist.class) + .where(e1) + .select(context); + } }