You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by on...@apache.org on 2017/06/22 12:10:12 UTC

camel git commit: CAMEL-11420-Add contains ignore case operator to simple language

Repository: camel
Updated Branches:
  refs/heads/master 95ebcb4fe -> d97c96251


CAMEL-11420-Add contains ignore case operator to simple language

CAMEL-11420-add jmh test

CAMEL-11420 - null check added for containsIgnoreCase in StringHelper

CAMEL-11420 - CR fixes

CAMEL-11420- update simple-language.adoc for contains ignore case


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

Branch: refs/heads/master
Commit: d97c96251a21b2d4b525401136ec30502b5dc79a
Parents: 95ebcb4
Author: onders86 <on...@gmail.com>
Authored: Mon Jun 19 20:06:38 2017 +0300
Committer: onders86 <on...@gmail.com>
Committed: Thu Jun 22 15:07:47 2017 +0300

----------------------------------------------------------------------
 .../src/main/resources/camel-checkstyle.xml     |  2 +-
 camel-core/src/main/docs/simple-language.adoc   |  2 +
 .../apache/camel/builder/PredicateBuilder.java  | 21 +++++
 .../camel/language/simple/SimpleTokenizer.java  |  1 +
 .../language/simple/ast/BinaryExpression.java   |  2 +
 .../simple/types/BinaryOperatorType.java        |  9 ++-
 .../org/apache/camel/util/ObjectHelper.java     | 37 +++++++++
 .../org/apache/camel/util/StringHelper.java     | 35 ++++++++
 .../language/simple/SimpleOperatorTest.java     |  7 ++
 .../camel/itest/jmh/ContainsIgnoreCaseTest.java | 85 ++++++++++++++++++++
 10 files changed, 199 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/camel/blob/d97c9625/buildingtools/src/main/resources/camel-checkstyle.xml
----------------------------------------------------------------------
diff --git a/buildingtools/src/main/resources/camel-checkstyle.xml b/buildingtools/src/main/resources/camel-checkstyle.xml
index 44e7c10..3b84c67 100644
--- a/buildingtools/src/main/resources/camel-checkstyle.xml
+++ b/buildingtools/src/main/resources/camel-checkstyle.xml
@@ -264,7 +264,7 @@ lengths, if/try depths, etc...
         <module name="JUnitTestCase"/>
         -->
         <module name="ReturnCount">
-            <property name="max" value="20"/>
+            <property name="max" value="21"/>
             <property name="maxForVoid" value="25"/>
         </module>
 

http://git-wip-us.apache.org/repos/asf/camel/blob/d97c9625/camel-core/src/main/docs/simple-language.adoc
----------------------------------------------------------------------
diff --git a/camel-core/src/main/docs/simple-language.adoc b/camel-core/src/main/docs/simple-language.adoc
index f13ed33..c8e428c 100644
--- a/camel-core/src/main/docs/simple-language.adoc
+++ b/camel-core/src/main/docs/simple-language.adoc
@@ -469,6 +469,8 @@ values)
 
 |not contains |For testing if not contains in a string based value
 
+|~~ |For testing if contains by ignoring case sensitivity in a string based value
+
 |regex |For matching against a given regular expression pattern defined as a
 String value
 

http://git-wip-us.apache.org/repos/asf/camel/blob/d97c9625/camel-core/src/main/java/org/apache/camel/builder/PredicateBuilder.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/builder/PredicateBuilder.java b/camel-core/src/main/java/org/apache/camel/builder/PredicateBuilder.java
index 05c5673..b0511f8 100644
--- a/camel-core/src/main/java/org/apache/camel/builder/PredicateBuilder.java
+++ b/camel-core/src/main/java/org/apache/camel/builder/PredicateBuilder.java
@@ -330,6 +330,27 @@ public final class PredicateBuilder {
             }
         };
     }
+    
+    public static Predicate containsIgnoreCase(final Expression left, final Expression right) {
+        return new BinaryPredicateSupport(left, right) {
+
+            protected boolean matches(Exchange exchange, Object leftValue, Object rightValue) {
+                if (leftValue == null && rightValue == null) {
+                    // they are equal
+                    return true;
+                } else if (leftValue == null || rightValue == null) {
+                    // only one of them is null so they are not equal
+                    return false;
+                }
+
+                return ObjectHelper.containsIgnoreCase(leftValue, rightValue);
+            }
+
+            protected String getOperationText() {
+                return "~~";
+            }
+        };
+    }
 
     public static Predicate isNull(final Expression expression) {
         return new BinaryPredicateSupport(expression, ExpressionBuilder.constantExpression(null)) {

http://git-wip-us.apache.org/repos/asf/camel/blob/d97c9625/camel-core/src/main/java/org/apache/camel/language/simple/SimpleTokenizer.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/language/simple/SimpleTokenizer.java b/camel-core/src/main/java/org/apache/camel/language/simple/SimpleTokenizer.java
index 4718cbe..b72f23d 100644
--- a/camel-core/src/main/java/org/apache/camel/language/simple/SimpleTokenizer.java
+++ b/camel-core/src/main/java/org/apache/camel/language/simple/SimpleTokenizer.java
@@ -63,6 +63,7 @@ public final class SimpleTokenizer {
         KNOWN_TOKENS.add(new SimpleTokenType(TokenType.binaryOperator, "is"));
         KNOWN_TOKENS.add(new SimpleTokenType(TokenType.binaryOperator, "not contains"));
         KNOWN_TOKENS.add(new SimpleTokenType(TokenType.binaryOperator, "contains"));
+        KNOWN_TOKENS.add(new SimpleTokenType(TokenType.binaryOperator, "~~"));
         KNOWN_TOKENS.add(new SimpleTokenType(TokenType.binaryOperator, "not regex"));
         KNOWN_TOKENS.add(new SimpleTokenType(TokenType.binaryOperator, "regex"));
         KNOWN_TOKENS.add(new SimpleTokenType(TokenType.binaryOperator, "not in"));

http://git-wip-us.apache.org/repos/asf/camel/blob/d97c9625/camel-core/src/main/java/org/apache/camel/language/simple/ast/BinaryExpression.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/language/simple/ast/BinaryExpression.java b/camel-core/src/main/java/org/apache/camel/language/simple/ast/BinaryExpression.java
index 4b6e821..e46b8c9 100644
--- a/camel-core/src/main/java/org/apache/camel/language/simple/ast/BinaryExpression.java
+++ b/camel-core/src/main/java/org/apache/camel/language/simple/ast/BinaryExpression.java
@@ -96,6 +96,8 @@ public class BinaryExpression extends BaseSimpleNode {
             return createExpression(leftExp, rightExp, PredicateBuilder.contains(leftExp, rightExp));
         } else if (operator == BinaryOperatorType.NOT_CONTAINS) {
             return createExpression(leftExp, rightExp, PredicateBuilder.not(PredicateBuilder.contains(leftExp, rightExp)));
+        } else if (operator == BinaryOperatorType.CONTAINS_IGNORECASE) {
+            return createExpression(leftExp, rightExp, PredicateBuilder.containsIgnoreCase(leftExp, rightExp));
         } else if (operator == BinaryOperatorType.IS || operator == BinaryOperatorType.NOT_IS) {
             return createIsExpression(expression, leftExp, rightExp);
         } else if (operator == BinaryOperatorType.REGEX || operator == BinaryOperatorType.NOT_REGEX) {

http://git-wip-us.apache.org/repos/asf/camel/blob/d97c9625/camel-core/src/main/java/org/apache/camel/language/simple/types/BinaryOperatorType.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/language/simple/types/BinaryOperatorType.java b/camel-core/src/main/java/org/apache/camel/language/simple/types/BinaryOperatorType.java
index 5a7751b..5ee1877 100644
--- a/camel-core/src/main/java/org/apache/camel/language/simple/types/BinaryOperatorType.java
+++ b/camel-core/src/main/java/org/apache/camel/language/simple/types/BinaryOperatorType.java
@@ -21,7 +21,8 @@ package org.apache.camel.language.simple.types;
  */
 public enum BinaryOperatorType {
 
-    EQ, EQ_IGNORE, GT, GTE, LT, LTE, NOT_EQ, CONTAINS, NOT_CONTAINS, REGEX, NOT_REGEX,
+    EQ, EQ_IGNORE, GT, GTE, LT, LTE, NOT_EQ, CONTAINS, NOT_CONTAINS, 
+    CONTAINS_IGNORECASE, REGEX, NOT_REGEX,
     IN, NOT_IN, IS, NOT_IS, RANGE, NOT_RANGE, STARTS_WITH, ENDS_WITH;
 
     public static BinaryOperatorType asOperator(String text) {
@@ -43,6 +44,8 @@ public enum BinaryOperatorType {
             return CONTAINS;
         } else if ("not contains".equals(text)) {
             return NOT_CONTAINS;
+        } else if ("~~".equals(text)) {
+            return CONTAINS_IGNORECASE;
         } else if ("regex".equals(text)) {
             return REGEX;
         } else if ("not regex".equals(text)) {
@@ -86,6 +89,8 @@ public enum BinaryOperatorType {
             return "contains";
         } else if (operator == NOT_CONTAINS) {
             return "not contains";
+        } else if (operator == CONTAINS_IGNORECASE) {
+            return "~~";
         } else if (operator == REGEX) {
             return "regex";
         } else if (operator == NOT_REGEX) {
@@ -174,6 +179,8 @@ public enum BinaryOperatorType {
             return null;
         } else if (operator == NOT_CONTAINS) {
             return null;
+        } else if (operator == CONTAINS_IGNORECASE) {
+            return null;
         } else if (operator == REGEX) {
             return new ParameterType[]{ParameterType.Literal, ParameterType.Function};
         } else if (operator == NOT_REGEX) {

http://git-wip-us.apache.org/repos/asf/camel/blob/d97c9625/camel-core/src/main/java/org/apache/camel/util/ObjectHelper.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/util/ObjectHelper.java b/camel-core/src/main/java/org/apache/camel/util/ObjectHelper.java
index 829bcaf..73b6fae 100644
--- a/camel-core/src/main/java/org/apache/camel/util/ObjectHelper.java
+++ b/camel-core/src/main/java/org/apache/camel/util/ObjectHelper.java
@@ -195,6 +195,13 @@ public final class ObjectHelper {
     public static boolean equal(Object a, Object b) {
         return equal(a, b, false);
     }
+    
+    /**
+     * A helper method for comparing objects for equality while handling case insensitivity
+     */
+    public static boolean equalIgnoreCase(Object a, Object b) {
+        return equal(a, b, true);
+    }
 
     /**
      * A helper method for comparing objects for equality while handling nulls
@@ -647,6 +654,36 @@ public final class ObjectHelper {
         }
         return false;
     }
+    
+    /**
+     * Returns true if the collection contains the specified value by considering case insensitivity
+     */
+    public static boolean containsIgnoreCase(Object collectionOrArray, Object value) {
+        // favor String types
+        if (collectionOrArray != null && (collectionOrArray instanceof StringBuffer || collectionOrArray instanceof StringBuilder)) {
+            collectionOrArray = collectionOrArray.toString();
+        }
+        if (value != null && (value instanceof StringBuffer || value instanceof StringBuilder)) {
+            value = value.toString();
+        }
+
+        if (collectionOrArray instanceof Collection) {
+            Collection<?> collection = (Collection<?>)collectionOrArray;
+            return collection.contains(value);
+        } else if (collectionOrArray instanceof String && value instanceof String) {
+            String str = (String)collectionOrArray;
+            String subStr = (String)value;
+            return StringHelper.containsIgnoreCase(str, subStr);
+        } else {
+            Iterator<Object> iter = createIterator(collectionOrArray);
+            while (iter.hasNext()) {
+                if (equalIgnoreCase(value, iter.next())) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
 
     /**
      * Creates an iterable over the value if the value is a collection, an

http://git-wip-us.apache.org/repos/asf/camel/blob/d97c9625/camel-core/src/main/java/org/apache/camel/util/StringHelper.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/util/StringHelper.java b/camel-core/src/main/java/org/apache/camel/util/StringHelper.java
index 635c07f..a9ce41f 100644
--- a/camel-core/src/main/java/org/apache/camel/util/StringHelper.java
+++ b/camel-core/src/main/java/org/apache/camel/util/StringHelper.java
@@ -672,5 +672,40 @@ public final class StringHelper {
 
         return trimmed;
     }
+    
+    /**
+     * Checks if the src string contains what
+     *
+     * @param src  is the source string to be checked
+     * @param what is the string which will be looked up in the src argument 
+     * @return true/false
+     */
+    public static boolean containsIgnoreCase(String src, String what) {
+        if (src == null || what == null) {
+            return false;
+        }
+        
+        final int length = what.length();
+        if (length == 0) {
+            return true; // Empty string is contained
+        }
+
+        final char firstLo = Character.toLowerCase(what.charAt(0));
+        final char firstUp = Character.toUpperCase(what.charAt(0));
+
+        for (int i = src.length() - length; i >= 0; i--) {
+            // Quick check before calling the more expensive regionMatches() method:
+            final char ch = src.charAt(i);
+            if (ch != firstLo && ch != firstUp) {
+                continue;
+            }
+
+            if (src.regionMatches(true, i, what, 0, length)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
 
 }

http://git-wip-us.apache.org/repos/asf/camel/blob/d97c9625/camel-core/src/test/java/org/apache/camel/language/simple/SimpleOperatorTest.java
----------------------------------------------------------------------
diff --git a/camel-core/src/test/java/org/apache/camel/language/simple/SimpleOperatorTest.java b/camel-core/src/test/java/org/apache/camel/language/simple/SimpleOperatorTest.java
index e40154f..25de22e 100644
--- a/camel-core/src/test/java/org/apache/camel/language/simple/SimpleOperatorTest.java
+++ b/camel-core/src/test/java/org/apache/camel/language/simple/SimpleOperatorTest.java
@@ -328,6 +328,13 @@ public class SimpleOperatorTest extends LanguageTestSupport {
         assertPredicate("${in.header.foo} not contains 'abc'", false);
         assertPredicate("${in.header.foo} not contains 'def'", true);
     }
+    
+    public void testContainsIgnoreCase() throws Exception {
+        assertPredicate("${in.header.foo} ~~ 'A'", true);
+        assertPredicate("${in.header.foo} ~~ 'Ab'", true);
+        assertPredicate("${in.header.foo} ~~ 'Abc'", true);
+        assertPredicate("${in.header.foo} ~~ 'defG'", false);
+    }
 
     public void testRegex() throws Exception {
         assertPredicate("${in.header.foo} regex '^a..$'", true);

http://git-wip-us.apache.org/repos/asf/camel/blob/d97c9625/tests/camel-jmh/src/test/java/org/apache/camel/itest/jmh/ContainsIgnoreCaseTest.java
----------------------------------------------------------------------
diff --git a/tests/camel-jmh/src/test/java/org/apache/camel/itest/jmh/ContainsIgnoreCaseTest.java b/tests/camel-jmh/src/test/java/org/apache/camel/itest/jmh/ContainsIgnoreCaseTest.java
new file mode 100644
index 0000000..1a29133
--- /dev/null
+++ b/tests/camel-jmh/src/test/java/org/apache/camel/itest/jmh/ContainsIgnoreCaseTest.java
@@ -0,0 +1,85 @@
+/**
+ * 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.camel.itest.jmh;
+
+import java.util.concurrent.TimeUnit;
+
+import org.apache.camel.util.StringHelper;
+import org.junit.Test;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.Level;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.infra.Blackhole;
+import org.openjdk.jmh.runner.Runner;
+import org.openjdk.jmh.runner.options.Options;
+import org.openjdk.jmh.runner.options.OptionsBuilder;
+import org.openjdk.jmh.runner.options.TimeValue;
+
+/**
+ * Tests the {@link StringHelper}.
+ * <p/>
+ * Thanks to this SO answer: https://stackoverflow.com/questions/30485856/how-to-run-jmh-from-inside-junit-tests
+ */
+public class ContainsIgnoreCaseTest {
+
+    @Test
+    public void launchBenchmark() throws Exception {
+        Options opt = new OptionsBuilder()
+            // Specify which benchmarks to run.
+            // You can be more specific if you'd like to run only one benchmark per test.
+            .include(this.getClass().getName() + ".*")
+            // Set the following options as needed
+            .mode(Mode.All)
+            .timeUnit(TimeUnit.MICROSECONDS)
+            .warmupTime(TimeValue.seconds(1))
+            .warmupIterations(2)
+            .measurementTime(TimeValue.seconds(1))
+            .measurementIterations(2)
+            .threads(2)
+            .forks(1)
+            .shouldFailOnError(true)
+            .shouldDoGC(true)
+            .build();
+
+        new Runner(opt).run();
+    }
+
+    // The JMH samples are the best documentation for how to use it
+    // http://hg.openjdk.java.net/code-tools/jmh/file/tip/jmh-samples/src/main/java/org/openjdk/jmh/samples/
+    @State(Scope.Thread)
+    public static class BenchmarkState {
+        @Setup(Level.Trial)
+        public void initialize() {
+        }
+    }
+
+    @Benchmark
+    @Measurement(batchSize = 1000000)
+    public void benchmark(BenchmarkState state, Blackhole bh) {
+        bh.consume(StringHelper.containsIgnoreCase("abc", "A"));
+        bh.consume(StringHelper.containsIgnoreCase("abc", "aB"));
+        bh.consume(StringHelper.containsIgnoreCase("abc", "aBc"));
+        bh.consume(StringHelper.containsIgnoreCase("abc", "ad"));
+        bh.consume(StringHelper.containsIgnoreCase("abc", "abD"));
+        bh.consume(StringHelper.containsIgnoreCase("abc", "ABD"));
+    }
+
+}