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 2014/11/27 08:12:02 UTC

[19/39] cayenne git commit: CAY-1971 Variants of Property.like(..) : contains(..), startsWith(..), endsWith(..)

CAY-1971 Variants of Property.like(..) : contains(..), startsWith(..), endsWith(..)

* implementing escaping


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

Branch: refs/heads/CAY-1946_1
Commit: 7aeba71b31bc513fda6b3678d81a4b9393aa772c
Parents: 9587d04
Author: aadamchik <aa...@apache.org>
Authored: Sat Nov 22 14:46:31 2014 +0300
Committer: aadamchik <aa...@apache.org>
Committed: Sat Nov 22 17:31:22 2014 +0300

----------------------------------------------------------------------
 .../cayenne/exp/LikeExpressionHelper.java       |  88 ++++-
 .../org/apache/cayenne/exp/PropertyTest.java    | 383 ++++++++++---------
 2 files changed, 285 insertions(+), 186 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cayenne/blob/7aeba71b/cayenne-server/src/main/java/org/apache/cayenne/exp/LikeExpressionHelper.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/LikeExpressionHelper.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/LikeExpressionHelper.java
index 7725dc9..31ec1f2 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/LikeExpressionHelper.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/LikeExpressionHelper.java
@@ -18,6 +18,7 @@
  ****************************************************************/
 package org.apache.cayenne.exp;
 
+import org.apache.cayenne.CayenneRuntimeException;
 import org.apache.cayenne.exp.parser.PatternMatchNode;
 
 /**
@@ -25,11 +26,22 @@ import org.apache.cayenne.exp.parser.PatternMatchNode;
  */
 class LikeExpressionHelper {
 
-	// presumably only "?" can't be an escape char
-	private static final char[] ESCAPE_ALPHABET = new char[] { '\\', '|', '/', ' ' };
+	private static final char WILDCARD_SEQUENCE = '%';
+	private static final char WILDCARD_ONE = '_';
+	private static final boolean[] ESCAPE_ALPHABET;
+	private static final int ESCAPE_ALPHABET_START = '!';
 
-	private static final String WILDCARD_SEQUENCE = "%";
-	private static final String WILDCARD_ONE = "_";
+	static {
+
+		ESCAPE_ALPHABET = new boolean[Byte.MAX_VALUE];
+		// exclude certain chars, such as unprintable ones, and ?
+		for (int i = ESCAPE_ALPHABET_START; i < Byte.MAX_VALUE; i++) {
+
+			if (i != '?' && i != '\"' && i != '\'' && i != WILDCARD_SEQUENCE && i != WILDCARD_ONE) {
+				ESCAPE_ALPHABET[i] = true;
+			}
+		}
+	}
 
 	static void toContains(PatternMatchNode exp) {
 		escape(exp);
@@ -47,10 +59,76 @@ class LikeExpressionHelper {
 	}
 
 	static void escape(PatternMatchNode exp) {
+		Object pattern = exp.getOperand(1);
+		if (pattern instanceof String) {
+			// find _ or % and then attempt to escape...
+
+			String pString = pattern.toString();
+
+			int len = pString.length();
+			for (int i = 0; i < len; i++) {
+
+				char c = pString.charAt(i);
+				if (c == WILDCARD_SEQUENCE || c == WILDCARD_ONE) {
+					exp.setOperand(1, escapeFrom(exp, pString, i, len));
+					break;
+				}
+			}
+		}
+	}
+
+	private static String escapeFrom(PatternMatchNode exp, String pattern, int firstWildcard, int len) {
+
+		boolean[] mutableEscapeAlphabet = new boolean[Byte.MAX_VALUE];
+		System.arraycopy(ESCAPE_ALPHABET, ESCAPE_ALPHABET_START, mutableEscapeAlphabet, ESCAPE_ALPHABET_START,
+				Byte.MAX_VALUE - ESCAPE_ALPHABET_START);
+
+		// can't use chars already in the pattern, so exclude the ones already
+		// taken
+		for (int i = 0; i < len; i++) {
+			char c = pattern.charAt(i);
+			if (c < Byte.MAX_VALUE) {
+				mutableEscapeAlphabet[c] = false;
+			}
+		}
+
+		// find the first available char
+		char escapeChar = 0;
+		for (int i = ESCAPE_ALPHABET_START; i < Byte.MAX_VALUE; i++) {
+			if (mutableEscapeAlphabet[i]) {
+				escapeChar = (char) i;
+				break;
+			}
+		}
+
+		if (escapeChar == 0) {
+			// if we start seeing this this error in the wild, I guess we'll
+			// need to extend escape char set beyond ASCII
+			throw new CayenneRuntimeException("Could not properly escape pattern: " + pattern);
+		}
+		
+		exp.setEscapeChar(escapeChar);
+
+		// build escaped pattern
+		StringBuilder buffer = new StringBuilder(len + 1);
+		buffer.append(pattern.substring(0, firstWildcard));
+		buffer.append(escapeChar).append(pattern.charAt(firstWildcard));
+
+		for (int i = firstWildcard + 1; i < len; i++) {
+
+			char c = pattern.charAt(i);
+			if (c == WILDCARD_SEQUENCE || c == WILDCARD_ONE) {
+				buffer.append(escapeChar);
+				break;
+			}
+
+			buffer.append(c);
+		}
 
+		return buffer.toString();
 	}
 
-	static void wrap(PatternMatchNode exp, boolean start, boolean end) {
+	private static void wrap(PatternMatchNode exp, boolean start, boolean end) {
 
 		Object pattern = exp.getOperand(1);
 		if (pattern instanceof String) {

http://git-wip-us.apache.org/repos/asf/cayenne/blob/7aeba71b/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 70fd3ce..538fc0b 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
@@ -18,6 +18,7 @@
  ****************************************************************/
 package org.apache.cayenne.exp;
 
+import org.apache.cayenne.exp.parser.PatternMatchNode;
 import org.apache.cayenne.reflect.TstJavaBean;
 import org.apache.cayenne.reflect.UnresolvablePathException;
 import org.apache.cayenne.util.Util;
@@ -32,185 +33,205 @@ import static org.junit.Assert.fail;
 
 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);
-    	final Property<Integer> OBJECT_FIELD_INT_FIELD = new Property<Integer>("objectField.intField");
-    	try {
-    		OBJECT_FIELD_INT_FIELD.getFrom(bean);
-    		fail();
-    	} catch (Exception e) {
-    		Throwable rootException = Util.unwindException(e);
-    		if (!(rootException instanceof UnresolvablePathException)) {
-    			fail();
-    		}
-    	}
-    }
-
-    @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");
-    	try {
-    		OBJECT_FIELD_INT_FIELD.setIn(bean, 7);
-    		fail();
-    	} catch (Exception e) {
-    		Throwable rootException = Util.unwindException(e);
-    		if (!(rootException instanceof UnresolvablePathException)) {
-    			fail();
-    		}
-    	}
-    }
-
-    @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.likeInsensitive("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());
-    }
-    
-    @Test
-    public void testContains() {
-    	Property<String> p = new Property<String>("prop");
-    	Expression e = p.contains("abc");
-    	assertEquals("prop like \"%abc%\"", e.toString());
-    }
-    
-    @Test
-    public void testStartsWith() {
-    	Property<String> p = new Property<String>("prop");
-    	Expression e = p.startsWith("abc");
-    	assertEquals("prop like \"abc%\"", e.toString());
-    }
-    
-    @Test
-    public void testEndsWith() {
-    	Property<String> p = new Property<String>("prop");
-    	Expression e = p.endsWith("abc");
-    	assertEquals("prop like \"%abc\"", e.toString());
-    }
+	@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);
+		final Property<Integer> OBJECT_FIELD_INT_FIELD = new Property<Integer>("objectField.intField");
+		try {
+			OBJECT_FIELD_INT_FIELD.getFrom(bean);
+			fail();
+		} catch (Exception e) {
+			Throwable rootException = Util.unwindException(e);
+			if (!(rootException instanceof UnresolvablePathException)) {
+				fail();
+			}
+		}
+	}
+
+	@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");
+		try {
+			OBJECT_FIELD_INT_FIELD.setIn(bean, 7);
+			fail();
+		} catch (Exception e) {
+			Throwable rootException = Util.unwindException(e);
+			if (!(rootException instanceof UnresolvablePathException)) {
+				fail();
+			}
+		}
+	}
+
+	@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.likeInsensitive("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());
+	}
 }