You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by aa...@apache.org on 2016/12/11 08:18:51 UTC

[4/4] cayenne git commit: CAY-2163 Property.path() , ExpressionFactory.pathExp()

CAY-2163 Property.path() , ExpressionFactory.pathExp()

(also switching some docs to Property API)


Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo
Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/83571185
Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/83571185
Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/83571185

Branch: refs/heads/master
Commit: 83571185859a9588c40917f98a54ee8b67402454
Parents: 94d4d83
Author: Andrus Adamchik <an...@objectstyle.com>
Authored: Sun Dec 11 10:55:41 2016 +0300
Committer: Andrus Adamchik <an...@objectstyle.com>
Committed: Sun Dec 11 11:18:15 2016 +0300

----------------------------------------------------------------------
 .../apache/cayenne/exp/ExpressionFactory.java   |  18 +
 .../java/org/apache/cayenne/exp/Property.java   |  19 +-
 .../cayenne/exp/ExpressionFactoryTest.java      |  20 +-
 .../org/apache/cayenne/exp/PropertyTest.java    | 377 ++++++++++---------
 docs/doc/src/main/resources/RELEASE-NOTES.txt   |  10 +
 .../cayenne-guide/src/docbkx/expressions.xml    |  89 +++--
 6 files changed, 299 insertions(+), 234 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cayenne/blob/83571185/cayenne-server/src/main/java/org/apache/cayenne/exp/ExpressionFactory.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/ExpressionFactory.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/ExpressionFactory.java
index cbef42d..b440f8a 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/ExpressionFactory.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/ExpressionFactory.java
@@ -860,6 +860,24 @@ public class ExpressionFactory {
 	}
 
 	/**
+	 * @param pathSpec a String "obj:" path.
+	 * @since 4.0
+	 * @return a new "obj:" path expression for the specified String path.
+	 */
+	public static Expression pathExp(String pathSpec) {
+		return new ASTObjPath(pathSpec);
+	}
+
+	/**
+	 * @param pathSpec a String db: path.
+	 * @since 4.0
+	 * @return a new "db:" path expression for the specified String path.
+	 */
+	public static Expression dbPathExp(String pathSpec) {
+		return new ASTDbPath(pathSpec);
+	}
+
+	/**
 	 * A convenience shortcut for boolean true expression.
 	 * 
 	 * @since 3.0

http://git-wip-us.apache.org/repos/asf/cayenne/blob/83571185/cayenne-server/src/main/java/org/apache/cayenne/exp/Property.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/Property.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/Property.java
index 5560014..b9ae1c6 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/Property.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/Property.java
@@ -77,16 +77,18 @@ public class Property<E> {
     }
 
     /**
-     * @return Constructs a property path by appending the argument to the
-     * existing property separated by a dot
+     * Constructs a property path by appending the argument to the existing property separated by a dot.
+     *
+     * @return a newly created Property object.
      */
     public Property<Object> dot(String property) {
         return new Property<Object>(getName() + "." + property);
     }
 
     /**
-     * @return Constructs a property path by appending the argument to the
-     * existing property separated by a dot
+     * Constructs a new property path by appending the argument to the existing property separated by a dot.
+     *
+     * @return a newly created Property object.
      */
     public <T> Property<T> dot(Property<T> property) {
         return new Property<T>(getName() + "." + property.getName());
@@ -106,6 +108,15 @@ public class Property<E> {
     }
 
     /**
+     * Converts this property to a path expression.
+     *
+     * @return a newly created expression.
+     */
+    public Expression path() {
+        return ExpressionFactory.pathExp(getName());
+    }
+
+    /**
      * @return An expression representing null.
      */
     public Expression isNull() {

http://git-wip-us.apache.org/repos/asf/cayenne/blob/83571185/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 57ff918..3cf4a46 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
@@ -175,7 +175,7 @@ public class ExpressionFactoryTest {
 
 	@Test
 	public void testInExp2() throws Exception {
-		List<Object> v = new ArrayList<Object>();
+		List<Object> v = new ArrayList<>();
 		v.add("a");
 		v.add("b");
 		Expression exp = ExpressionFactory.inExp("abc", v);
@@ -184,7 +184,7 @@ public class ExpressionFactoryTest {
 
 	@Test
 	public void testInExp3() throws Exception {
-		List<Object> v = new ArrayList<Object>();
+		List<Object> v = new ArrayList<>();
 		Expression exp = ExpressionFactory.inExp("abc", v);
 		assertEquals(Expression.FALSE, exp.getType());
 	}
@@ -308,7 +308,7 @@ public class ExpressionFactoryTest {
 
 		// check for N in (1..5)
 		for (int n = 1; n <= 5; n++) {
-			Collection<Expression> list = new ArrayList<Expression>();
+			Collection<Expression> list = new ArrayList<>();
 
 			// populate map
 			for (int i = 1; i <= n; i++) {
@@ -342,7 +342,7 @@ public class ExpressionFactoryTest {
 	public void testAnd_Collection_OneElement() {
 		Expression e1 = ExpressionFactory.matchExp("a", 1);
 
-		Collection<Expression> c = Arrays.asList(e1);
+		Collection<Expression> c = Collections.singletonList(e1);
 		Expression e = ExpressionFactory.and(c);
 
 		assertEquals("a = 1", e.toString());
@@ -457,8 +457,18 @@ public class ExpressionFactoryTest {
 	}
 
 	@Test
-	public void testExp_Vararg_InAsValues() throws Exception {
+	public void testExp_Vararg_InAsValues() {
 		Expression e = ExpressionFactory.exp("k1 in ($ap, $bp)", "a", "b");
 		assertEquals("k1 in (\"a\", \"b\")", e.toString());
 	}
+
+	@Test
+	public void testPathExp() {
+		assertEquals("abc.xyz", ExpressionFactory.pathExp("abc.xyz").toString());
+	}
+
+	@Test
+	public void testDbPathExp() {
+		assertEquals("db:abc.xyz", ExpressionFactory.dbPathExp("abc.xyz").toString());
+	}
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/83571185/cayenne-server/src/test/java/org/apache/cayenne/exp/PropertyTest.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/exp/PropertyTest.java b/cayenne-server/src/test/java/org/apache/cayenne/exp/PropertyTest.java
index 0026f66..5984a94 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/exp/PropertyTest.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/exp/PropertyTest.java
@@ -31,189 +31,196 @@ import org.junit.Test;
 
 public class PropertyTest {
 
-	@Test
-	public void testIn() {
-		Property<String> p = new Property<String>("x.y");
-
-		Expression e1 = p.in("a");
-		assertEquals("x.y in (\"a\")", e1.toString());
-
-		Expression e2 = p.in("a", "b");
-		assertEquals("x.y in (\"a\", \"b\")", e2.toString());
-
-		Expression e3 = p.in(Arrays.asList("a", "b"));
-		assertEquals("x.y in (\"a\", \"b\")", e3.toString());
-	}
-
-	@Test
-	public void testGetFrom() {
-		TstJavaBean bean = new TstJavaBean();
-		bean.setIntField(7);
-		final Property<Integer> INT_FIELD = new Property<Integer>("intField");
-		assertEquals(Integer.valueOf(7), INT_FIELD.getFrom(bean));
-	}
-
-	@Test
-	public void testGetFromNestedProperty() {
-		TstJavaBean bean = new TstJavaBean();
-		TstJavaBean nestedBean = new TstJavaBean();
-		nestedBean.setIntField(7);
-		bean.setObjectField(nestedBean);
-		final Property<Integer> OBJECT_FIELD_INT_FIELD = new Property<Integer>("objectField.intField");
-		assertEquals(Integer.valueOf(7), OBJECT_FIELD_INT_FIELD.getFrom(bean));
-	}
-
-	@Test
-	public void testGetFromNestedNull() {
-		TstJavaBean bean = new TstJavaBean();
-		bean.setObjectField(null);
-		Property<Integer> OBJECT_FIELD_INT_FIELD = new Property<Integer>("objectField.intField");
-		assertNull(OBJECT_FIELD_INT_FIELD.getFrom(bean));
-	}
-
-	@Test
-	public void testGetFromAll() {
-		TstJavaBean bean = new TstJavaBean();
-		bean.setIntField(7);
-
-		TstJavaBean bean2 = new TstJavaBean();
-		bean2.setIntField(8);
-
-		List<TstJavaBean> beans = Arrays.asList(bean, bean2);
-
-		final Property<Integer> INT_FIELD = new Property<Integer>("intField");
-		assertEquals(Arrays.asList(7, 8), INT_FIELD.getFromAll(beans));
-	}
-
-	@Test
-	public void testSetIn() {
-		TstJavaBean bean = new TstJavaBean();
-		final Property<Integer> INT_FIELD = new Property<Integer>("intField");
-		INT_FIELD.setIn(bean, 7);
-		assertEquals(7, bean.getIntField());
-	}
-
-	@Test
-	public void testSetInNestedProperty() {
-		TstJavaBean bean = new TstJavaBean();
-		bean.setObjectField(new TstJavaBean());
-
-		final Property<Integer> OBJECT_FIELD_INT_FIELD = new Property<Integer>("objectField.intField");
-
-		OBJECT_FIELD_INT_FIELD.setIn(bean, 7);
-		assertEquals(7, ((TstJavaBean) bean.getObjectField()).getIntField());
-	}
-
-	@Test
-	public void testSetInNestedNull() {
-		TstJavaBean bean = new TstJavaBean();
-		bean.setObjectField(null);
-		final Property<Integer> OBJECT_FIELD_INT_FIELD = new Property<Integer>("objectField.intField");
-		OBJECT_FIELD_INT_FIELD.setIn(bean, 7);
-	}
-
-	@Test
-	public void testSetInAll() {
-		TstJavaBean bean = new TstJavaBean();
-		TstJavaBean bean2 = new TstJavaBean();
-		List<TstJavaBean> beans = Arrays.asList(bean, bean2);
-
-		final Property<Integer> INT_FIELD = new Property<Integer>("intField");
-		INT_FIELD.setInAll(beans, 7);
-		assertEquals(7, bean.getIntField());
-		assertEquals(7, bean2.getIntField());
-	}
-
-	@Test
-	public void testEquals() {
-		final Property<Integer> INT_FIELD = new Property<Integer>("intField");
-		final Property<Integer> INT_FIELD2 = new Property<Integer>("intField");
-
-		assertTrue(INT_FIELD != INT_FIELD2);
-		assertTrue(INT_FIELD.equals(INT_FIELD2));
-	}
-
-	@Test
-	public void testHashCode() {
-		final Property<Integer> INT_FIELD = new Property<Integer>("intField");
-		final Property<Integer> INT_FIELD2 = new Property<Integer>("intField");
-		final Property<Long> LONG_FIELD = new Property<Long>("longField");
-
-		assertTrue(INT_FIELD.hashCode() == INT_FIELD2.hashCode());
-		assertTrue(INT_FIELD.hashCode() != LONG_FIELD.hashCode());
-	}
-
-	@Test
-	public void testOuter() {
-		Property<String> inner = new Property<String>("xyz");
-		assertEquals("xyz+", inner.outer().getName());
-
-		Property<String> inner1 = new Property<String>("xyz.xxx");
-		assertEquals("xyz.xxx+", inner1.outer().getName());
-
-		Property<String> outer = new Property<String>("xyz+");
-		assertEquals("xyz+", outer.outer().getName());
-	}
-
-	@Test
-	public void testLike() {
-		Property<String> p = new Property<String>("prop");
-		Expression e = p.like("abc");
-		assertEquals("prop like \"abc\"", e.toString());
-	}
-
-	@Test
-	public void testLikeIgnoreCase() {
-		Property<String> p = new Property<String>("prop");
-		Expression e = p.likeIgnoreCase("abc");
-		assertEquals("prop likeIgnoreCase \"abc\"", e.toString());
-	}
-
-	@Test
-	public void testLike_NoEscape() {
-		Property<String> p = new Property<String>("prop");
-		Expression e = p.like("ab%c");
-		assertEquals("prop like \"ab%c\"", e.toString());
-		assertEquals(0, ((PatternMatchNode) e).getEscapeChar());
-	}
-
-	@Test
-	public void testContains() {
-		Property<String> p = new Property<String>("prop");
-		Expression e = p.contains("abc");
-		assertEquals("prop like \"%abc%\"", e.toString());
-		assertEquals(0, ((PatternMatchNode) e).getEscapeChar());
-	}
-
-	@Test
-	public void testStartsWith() {
-		Property<String> p = new Property<String>("prop");
-		Expression e = p.startsWith("abc");
-		assertEquals("prop like \"abc%\"", e.toString());
-		assertEquals(0, ((PatternMatchNode) e).getEscapeChar());
-	}
-
-	@Test
-	public void testEndsWith() {
-		Property<String> p = new Property<String>("prop");
-		Expression e = p.endsWith("abc");
-		assertEquals("prop like \"%abc\"", e.toString());
-		assertEquals(0, ((PatternMatchNode) e).getEscapeChar());
-	}
-	
-	@Test
-	public void testContains_Escape1() {
-		Property<String> p = new Property<String>("prop");
-		Expression e = p.contains("a%bc");
-		assertEquals("prop like \"%a!%bc%\"", e.toString());
-		assertEquals('!', ((PatternMatchNode) e).getEscapeChar());
-	}
-	
-	@Test
-	public void testContains_Escape2() {
-		Property<String> p = new Property<String>("prop");
-		Expression e = p.contains("a_!bc");
-		assertEquals("prop like \"%a#_!bc%\"", e.toString());
-		assertEquals('#', ((PatternMatchNode) e).getEscapeChar());
-	}
+    @Test
+    public void testPath() {
+        Property<String> p = new Property<>("x.y");
+        Expression pp = p.path();
+        assertEquals(ExpressionFactory.exp("x.y"), pp);
+    }
+
+    @Test
+    public void testIn() {
+        Property<String> p = new Property<>("x.y");
+
+        Expression e1 = p.in("a");
+        assertEquals("x.y in (\"a\")", e1.toString());
+
+        Expression e2 = p.in("a", "b");
+        assertEquals("x.y in (\"a\", \"b\")", e2.toString());
+
+        Expression e3 = p.in(Arrays.asList("a", "b"));
+        assertEquals("x.y in (\"a\", \"b\")", e3.toString());
+    }
+
+    @Test
+    public void testGetFrom() {
+        TstJavaBean bean = new TstJavaBean();
+        bean.setIntField(7);
+        Property<Integer> INT_FIELD = new Property<>("intField");
+        assertEquals(Integer.valueOf(7), INT_FIELD.getFrom(bean));
+    }
+
+    @Test
+    public void testGetFromNestedProperty() {
+        TstJavaBean bean = new TstJavaBean();
+        TstJavaBean nestedBean = new TstJavaBean();
+        nestedBean.setIntField(7);
+        bean.setObjectField(nestedBean);
+        Property<Integer> OBJECT_FIELD_INT_FIELD = new Property<>("objectField.intField");
+        assertEquals(Integer.valueOf(7), OBJECT_FIELD_INT_FIELD.getFrom(bean));
+    }
+
+    @Test
+    public void testGetFromNestedNull() {
+        TstJavaBean bean = new TstJavaBean();
+        bean.setObjectField(null);
+        Property<Integer> OBJECT_FIELD_INT_FIELD = new Property<>("objectField.intField");
+        assertNull(OBJECT_FIELD_INT_FIELD.getFrom(bean));
+    }
+
+    @Test
+    public void testGetFromAll() {
+        TstJavaBean bean = new TstJavaBean();
+        bean.setIntField(7);
+
+        TstJavaBean bean2 = new TstJavaBean();
+        bean2.setIntField(8);
+
+        List<TstJavaBean> beans = Arrays.asList(bean, bean2);
+
+        Property<Integer> INT_FIELD = new Property<>("intField");
+        assertEquals(Arrays.asList(7, 8), INT_FIELD.getFromAll(beans));
+    }
+
+    @Test
+    public void testSetIn() {
+        TstJavaBean bean = new TstJavaBean();
+        Property<Integer> INT_FIELD = new Property<>("intField");
+        INT_FIELD.setIn(bean, 7);
+        assertEquals(7, bean.getIntField());
+    }
+
+    @Test
+    public void testSetInNestedProperty() {
+        TstJavaBean bean = new TstJavaBean();
+        bean.setObjectField(new TstJavaBean());
+
+        Property<Integer> OBJECT_FIELD_INT_FIELD = new Property<>("objectField.intField");
+
+        OBJECT_FIELD_INT_FIELD.setIn(bean, 7);
+        assertEquals(7, ((TstJavaBean) bean.getObjectField()).getIntField());
+    }
+
+    @Test
+    public void testSetInNestedNull() {
+        TstJavaBean bean = new TstJavaBean();
+        bean.setObjectField(null);
+        Property<Integer> OBJECT_FIELD_INT_FIELD = new Property<>("objectField.intField");
+        OBJECT_FIELD_INT_FIELD.setIn(bean, 7);
+    }
+
+    @Test
+    public void testSetInAll() {
+        TstJavaBean bean = new TstJavaBean();
+        TstJavaBean bean2 = new TstJavaBean();
+        List<TstJavaBean> beans = Arrays.asList(bean, bean2);
+
+        Property<Integer> INT_FIELD = new Property<>("intField");
+        INT_FIELD.setInAll(beans, 7);
+        assertEquals(7, bean.getIntField());
+        assertEquals(7, bean2.getIntField());
+    }
+
+    @Test
+    public void testEquals() {
+        Property<Integer> INT_FIELD = new Property<>("intField");
+        Property<Integer> INT_FIELD2 = new Property<>("intField");
+
+        assertTrue(INT_FIELD != INT_FIELD2);
+        assertTrue(INT_FIELD.equals(INT_FIELD2));
+    }
+
+    @Test
+    public void testHashCode() {
+        Property<Integer> INT_FIELD = new Property<>("intField");
+        Property<Integer> INT_FIELD2 = new Property<>("intField");
+        Property<Long> LONG_FIELD = new Property<>("longField");
+
+        assertTrue(INT_FIELD.hashCode() == INT_FIELD2.hashCode());
+        assertTrue(INT_FIELD.hashCode() != LONG_FIELD.hashCode());
+    }
+
+    @Test
+    public void testOuter() {
+        Property<String> inner = new Property<>("xyz");
+        assertEquals("xyz+", inner.outer().getName());
+
+        Property<String> inner1 = new Property<>("xyz.xxx");
+        assertEquals("xyz.xxx+", inner1.outer().getName());
+
+        Property<String> outer = new Property<>("xyz+");
+        assertEquals("xyz+", outer.outer().getName());
+    }
+
+    @Test
+    public void testLike() {
+        Property<String> p = new Property<>("prop");
+        Expression e = p.like("abc");
+        assertEquals("prop like \"abc\"", e.toString());
+    }
+
+    @Test
+    public void testLikeIgnoreCase() {
+        Property<String> p = new Property<>("prop");
+        Expression e = p.likeIgnoreCase("abc");
+        assertEquals("prop likeIgnoreCase \"abc\"", e.toString());
+    }
+
+    @Test
+    public void testLike_NoEscape() {
+        Property<String> p = new Property<>("prop");
+        Expression e = p.like("ab%c");
+        assertEquals("prop like \"ab%c\"", e.toString());
+        assertEquals(0, ((PatternMatchNode) e).getEscapeChar());
+    }
+
+    @Test
+    public void testContains() {
+        Property<String> p = new Property<>("prop");
+        Expression e = p.contains("abc");
+        assertEquals("prop like \"%abc%\"", e.toString());
+        assertEquals(0, ((PatternMatchNode) e).getEscapeChar());
+    }
+
+    @Test
+    public void testStartsWith() {
+        Property<String> p = new Property<>("prop");
+        Expression e = p.startsWith("abc");
+        assertEquals("prop like \"abc%\"", e.toString());
+        assertEquals(0, ((PatternMatchNode) e).getEscapeChar());
+    }
+
+    @Test
+    public void testEndsWith() {
+        Property<String> p = new Property<>("prop");
+        Expression e = p.endsWith("abc");
+        assertEquals("prop like \"%abc\"", e.toString());
+        assertEquals(0, ((PatternMatchNode) e).getEscapeChar());
+    }
+
+    @Test
+    public void testContains_Escape1() {
+        Property<String> p = new Property<>("prop");
+        Expression e = p.contains("a%bc");
+        assertEquals("prop like \"%a!%bc%\"", e.toString());
+        assertEquals('!', ((PatternMatchNode) e).getEscapeChar());
+    }
+
+    @Test
+    public void testContains_Escape2() {
+        Property<String> p = new Property<>("prop");
+        Expression e = p.contains("a_!bc");
+        assertEquals("prop like \"%a#_!bc%\"", e.toString());
+        assertEquals('#', ((PatternMatchNode) e).getEscapeChar());
+    }
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/83571185/docs/doc/src/main/resources/RELEASE-NOTES.txt
----------------------------------------------------------------------
diff --git a/docs/doc/src/main/resources/RELEASE-NOTES.txt b/docs/doc/src/main/resources/RELEASE-NOTES.txt
index 626e549..3cccbd6 100644
--- a/docs/doc/src/main/resources/RELEASE-NOTES.txt
+++ b/docs/doc/src/main/resources/RELEASE-NOTES.txt
@@ -8,6 +8,16 @@ To browse individual bug reports check out project issue tracker:
 https://issues.apache.org/jira/browse/CAY
 
 ----------------------------------
+Release: 4.0.M5
+Date:
+----------------------------------
+Changes/New Features:
+
+CAY-2153 Property.path() , ExpressionFactory.pathExp()
+
+Bug Fixes:
+
+----------------------------------
 Release: 4.0.M4
 Date:
 ----------------------------------

http://git-wip-us.apache.org/repos/asf/cayenne/blob/83571185/docs/docbook/cayenne-guide/src/docbkx/expressions.xml
----------------------------------------------------------------------
diff --git a/docs/docbook/cayenne-guide/src/docbkx/expressions.xml b/docs/docbook/cayenne-guide/src/docbkx/expressions.xml
index dd87c1d..48e7462 100644
--- a/docs/docbook/cayenne-guide/src/docbkx/expressions.xml
+++ b/docs/docbook/cayenne-guide/src/docbkx/expressions.xml
@@ -208,39 +208,52 @@ Expression qualifier1 = template.params(p1);
         </para>
     </section>
     <section xml:id="expressions-with-expressionfactory">
-        <title>Creating Expressions with API</title>
+        <title>Creating Expressions via API</title>
         <para>Creating expressions from Strings is a powerful and dynamic approach, however a safer
-            alternative is to use Java API. It provides some degree of compile-time checking of
-            expressions validity. The API is cenetred around ExpressionFactory class, and the
-            Expression class. ExpressionFactory contains a number of rather self-explanatory factory
-            methods. We won&apos;t be going over all of them in detail, but will rather show a few
-            general examples and some gotchas. </para>
-        <para>The following code recreates the expression from the previous chapter, but now using
-            expression
-            API:<programlisting language="java">// String expression: name like &apos;A%&apos; and price &lt; 1000
-Expression e1 = ExpressionFactory.likeExp(Painting.NAME_PROPERTY, &quot;A%&quot;);
-Expression e2 = ExpressionFactory.lessExp(Painting.PRICE_PROPERTY, 1000);
-Expression finalExp = e1.andExp(e2); </programlisting>This
-            is more verbose than creating it from String, but it is also more resilient to the
-            entity properties renaming and precludes semantic errors in the expression String.<note>
+            alternative is to use Java API. It provides compile-time checking of expressions
+            validity. The API in question is provided by <code>ExpressionFactory</code> class (that
+            we've seen already), <code>Property</code> class and <code>Expression</code> class
+            itself. <code>ExpressionFactory</code> contains a number of self-explanatory static
+            methods that can be used to build expressions. E.g.:</para>
+        <para>
+            <programlisting language="java">// String expression: name like &apos;A%&apos; and price &lt; 1000
+Expression e1 = ExpressionFactory.likeExp("name", &quot;A%&quot;);
+Expression e2 = ExpressionFactory.lessExp("price, 1000);
+Expression finalExp = e1.andExp(e2); </programlisting>
+            <note>
                 <para>The last line in the example above shows how to create a new expression by
-                    &quot;chaining&quot; 2 other epxressions. A common error when chaining expressions is to
-                    assume that &quot;andExp&quot; and &quot;orExp&quot; append another expression to the current
-                    expression. In fact a new expression is created. I.e. Expression API treats
-                    existing expressions as immutable.</para>
-            </note></para>
+                    &quot;chaining&quot; two other epxressions. A common error when chaining
+                    expressions is to assume that &quot;andExp&quot; and &quot;orExp&quot; append
+                    another expression to the current expression. In fact a new expression is
+                    created. I.e. Expression API treats existing expressions as immutable.</para>
+            </note>
+        </para>
         <para>As discussed earlier, Cayenne supports aliases in path Expressions, allowing to
             control how SQL joins are generated if the same path is encountered more than once in
             the same Expression. Two ExpressionFactory methods allow to implicitly generate aliases
             to &quot;split&quot; match paths into individual joins if
             needed:<programlisting language="java">Expression matchAllExp(String path, Collection values)
 Expression matchAllExp(String path, Object... values)</programlisting></para>
-        <para>&quot;Path&quot; argument to both of these methods can use a split character (a pipe symbol &apos;|&apos;)
-            instead of dot to indicate that relationship following a path should be split into a
-            separate set of joins, one per collection value. There can only be one split at most in
-            any given path. Split must always precede a relationship. E.g. &quot;|exhibits.paintings&quot;,
-            &quot;exhibits|paintings&quot;, etc. Internally Cayenne would generate distinct aliases for each
-            of the split expressions, forcing separate joins.</para>
+        <para>&quot;Path&quot; argument to both of these methods can use a split character (a pipe
+            symbol &apos;|&apos;) instead of dot to indicate that relationship following a path
+            should be split into a separate set of joins, one per collection value. There can only
+            be one split at most in any given path. Split must always precede a relationship. E.g.
+                <code>"|exhibits.paintings"</code>, <code>"exhibits|paintings"</code>, etc.
+            Internally Cayenne would generate distinct aliases for each of the split expressions,
+            forcing separate joins.</para>
+        <para>While ExpressionFactory is pretty powerful, there's an even easier way to create
+            expression using static Property objects generated by Cayenne for each persistent class.
+            Some
+            examples:<programlisting>// Artist.NAME is generated by Cayenne and has a type of Property&lt;String>
+Expression e1 = Artist.NAME.eq("Pablo");
+
+// Chaining multiple properties into a path..
+// Painting.ARTIST is generated by Cayenne and has a type of Property&lt;Artist>
+Expression e2 = Painting.ARTIST.dot(Artist.NAME).eq("Pablo");</programlisting></para>
+        <para>Property objects provide the API mostly analogius to ExpressionFactory, though it is
+            significantly shorter and is aware of the value types. It provides compile-time checks
+            of both property names and types of arguments in conditions. We will use Property-based
+            API in further examples.</para>
     </section>
     <section xml:id="expressions-in-memory">
         <title>Evaluating Expressions in Memory</title>
@@ -249,18 +262,17 @@ Expression matchAllExp(String path, Object... values)</programlisting></para>
             is done by the database engine. However the same expressions can also be used for
             accessing object properties, calculating values, in-memory filtering. </para>
         <para>Checking whether an object satisfies an
-            expression:<programlisting language="java">Expression e = ExpressionFactory.inExp(User.NAME_PROPERTY, &quot;John&quot;, &quot;Bob&quot;);
-User user = ...
-if(e.match(user)) {
+            expression:<programlisting language="java">Expression e = Artist.NAME.in(&quot;John&quot;, &quot;Bob&quot;);
+Artist artist = ...
+if(e.match(artist)) {
    ...
 }</programlisting>Reading
             property
-            value:<programlisting language="java">Expression e = Expression.fromString(User.NAME_PROPERTY);
-String name = e.evaluate(user);</programlisting></para>
+            value:<programlisting language="java">String name = Artist.NAME.path().evaluate(artist);</programlisting></para>
         <para>Filtering a list of
-            objects:<programlisting language="java">Expression e = ExpressionFactory.inExp(User.NAME_PROPERTY, &quot;John&quot;, &quot;Bob&quot;);
-List&lt;User&gt; unfiltered = ...
-List&lt;User&gt; filtered = e.filterObjects(unfiltered);</programlisting></para>
+            objects:<programlisting language="java">Expression e = Artist.NAME.in(&quot;John&quot;, &quot;Bob&quot;);
+List&lt;Artist&gt; unfiltered = ...
+List&lt;Artist&gt; filtered = e.filterObjects(unfiltered);</programlisting></para>
         <para>
             <note>
                 <para>Current limitation of in-memory expressions is that no collections are
@@ -272,21 +284,18 @@ List&lt;User&gt; filtered = e.filterObjects(unfiltered);</programlisting></para>
     <section xml:id="expressions-to-ejbql">
         <title>Translating Expressions to EJBQL</title>
         <para>
-            <link linkend="ejbqlquery">EJBQL</link> is a textual query language that can be used with Cayenne.
-            In some situations, it is convenient to be able to convert Expression instances into EJBQL.
-            Expressions support this conversion.  An example is shown below.
-
+            <link linkend="ejbqlquery">EJBQL</link> is a textual query language that can be used
+            with Cayenne. In some situations, it is convenient to be able to convert Expression
+            instances into EJBQL. Expressions support this conversion. An example is shown below.
             <programlisting language="java">String serial = ...
-Expression e = ExpressionFactory.matchExp(Pkg.SERIAL_PROPERTY, serial);
+Expression e = Pkg.SERIAL.eq(serial);
 List&lt;Object&gt; params = new ArrayList&lt;Object&gt;();
 EJBQLQuery query = new EJBQLQuery("SELECT p FROM Pkg p WHERE " + e.toEJBQL(params,&quot;p&quot;);
 
 for(int i=0;i&lt;params.size();i++) {
   query.setParameter(i+1, params.get(i));
 }</programlisting>
-
             This would be equivalent to the following purely EJBQL querying logic;
-
             <programlisting language="java">EJBQLQuery query = new EJBQLQuery("SELECT p FROM Pkg p WHERE p.serial = ?1");
 query.setParameter(1,serial);</programlisting>
         </para>