cxf-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From amicha...@apache.org
Subject svn commit: r921968 - in /cxf/trunk/rt/frontend/jaxrs/src: main/java/org/apache/cxf/jaxrs/ext/search/ test/java/org/apache/cxf/jaxrs/ext/search/
Date Thu, 11 Mar 2010 18:14:21 GMT
Author: amichalec
Date: Thu Mar 11 18:14:20 2010
New Revision: 921968

URL: http://svn.apache.org/viewvc?rev=921968&view=rev
Log:
FIQL parser and SearchCondition syntax tree builder

Added:
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/AndSearchCondition.java
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/Beanspector.java
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/FiqlParseException.java
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/FiqlParser.java
    cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/ext/search/FiqlParserTest.java
Modified:
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/ConditionType.java
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/OrSearchCondition.java
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/SimpleSearchCondition.java
    cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/ext/search/SimpleSearchConditionTest.java

Added: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/AndSearchCondition.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/AndSearchCondition.java?rev=921968&view=auto
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/AndSearchCondition.java (added)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/AndSearchCondition.java Thu Mar 11 18:14:20 2010
@@ -0,0 +1,74 @@
+/**
+ * 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.cxf.jaxrs.ext.search;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Composite 'and' search condition   
+ */
+public class AndSearchCondition<T> implements SearchCondition<T> {
+
+    private List<SearchCondition<T>> conditions;
+    
+    public AndSearchCondition() {
+        
+    }
+    
+    public AndSearchCondition(List<SearchCondition<T>> conditions) {
+        this.conditions = conditions;    
+    }
+    
+    public void setConditions(List<SearchCondition<T>> conditions) {
+        this.conditions = conditions;
+    }
+    
+    public boolean isMet(T pojo) {
+        for (SearchCondition<T> sc : conditions) {
+            if (!sc.isMet(pojo)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    public T getCondition() {
+        return null;
+    }
+
+    public ConditionType getConditionType() {
+        return ConditionType.AND;
+    }
+
+    public List<SearchCondition<T>> getConditions() {
+        return Collections.unmodifiableList(conditions);
+    }
+
+    public List<T> findAll(List<T> pojos) {
+        List<T> result = new ArrayList<T>();
+        for (T pojo : pojos) {
+            if (isMet(pojo)) {
+                result.add(pojo);
+            }
+        }
+        return result;
+    }
+}

Added: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/Beanspector.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/Beanspector.java?rev=921968&view=auto
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/Beanspector.java (added)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/Beanspector.java Thu Mar 11 18:14:20 2010
@@ -0,0 +1,222 @@
+/**
+ * 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.cxf.jaxrs.ext.search;
+
+import java.beans.IntrospectionException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Bean introspection utility.
+ */
+class Beanspector<T> {
+
+    private Class<T> tclass;
+    private T tobj;
+    private Map<String, Method> getters = new HashMap<String, Method>();
+    private Map<String, Method> setters = new HashMap<String, Method>();
+
+    public Beanspector(Class<T> tclass) {
+        if (tclass == null) {
+            throw new IllegalArgumentException("tclass is null");
+        }
+        this.tclass = tclass;
+        init();
+    }
+
+    public Beanspector(T tobj) {
+        if (tobj == null) {
+            throw new IllegalArgumentException("tobj is null");
+        }
+        this.tobj = tobj;
+        init();
+    }
+
+    @SuppressWarnings("unchecked")
+    private void init() {
+        if (tclass == null) {
+            tclass = (Class<T>)tobj.getClass();
+        }
+        for (Method m : tclass.getMethods()) {
+            if (isGetter(m)) {
+                getters.put(getterName(m), m);
+            } else if (isSetter(m)) {
+                setters.put(setterName(m), m);
+            }
+        }
+        // check type equality for getter-setter pairs
+        Set<String> pairs = new HashSet<String>(getters.keySet());
+        pairs.retainAll(setters.keySet());
+        for (String accessor : pairs) {
+            Class<?> getterClass = getters.get(accessor).getReturnType();
+            Class<?> setterClass = setters.get(accessor).getParameterTypes()[0];
+            if (!getterClass.equals(setterClass)) {
+                throw new IllegalArgumentException(String
+                    .format("Accessor '%s' type mismatch, getter type is %s while setter type is %s",
+                            accessor, getterClass.getName(), setterClass.getName()));
+            }
+        }
+    }
+
+    public T getBean() {
+        return tobj;
+    }
+
+    public Set<String> getGettersNames() {
+        return Collections.unmodifiableSet(getters.keySet());
+    }
+
+    public Set<String> getSettersNames() {
+        return Collections.unmodifiableSet(setters.keySet());
+    }
+
+    public Class<?> getAccessorType(String getterOrSetterName) throws Exception {
+        Method m = getters.get(getterOrSetterName);
+        if (m == null) {
+            m = setters.get(getterOrSetterName);
+        }
+        if (m == null) {
+            String msg = String.format("Accessor '%s' not found, "
+                                       + "known setters are: %s, known getters are: %s", getterOrSetterName,
+                                       setters.keySet(), getters.keySet());
+            throw new IntrospectionException(msg);
+        }
+        return m.getReturnType();
+    }
+
+    public Beanspector<T> swap(T newobject) throws Exception {
+        if (newobject == null) {
+            throw new IllegalArgumentException("newobject is null");
+        }
+        tobj = newobject;
+        return this;
+    }
+
+    public Beanspector<T> instantiate() throws Exception {
+        tobj = tclass.newInstance();
+        return this;
+    }
+
+    public Beanspector<T> setValue(String setterName, Object value) throws Throwable {
+        Method m = setters.get(setterName);
+        if (m == null) {
+            String msg = String.format("Setter '%s' not found, " + "known setters are: %s", setterName,
+                                       setters.keySet());
+            throw new IntrospectionException(msg);
+        }
+        setValue(m, value);
+        return this;
+    }
+
+    public Beanspector<T> setValue(Map<String, Object> settersWithValues) throws Throwable {
+        for (Map.Entry<String, Object> entry : settersWithValues.entrySet()) {
+            setValue(entry.getKey(), entry.getValue());
+        }
+        return this;
+    }
+
+    public Beanspector<T> setValue(Method setter, Object value) throws Throwable {
+        Class<?> paramType = setter.getParameterTypes()[0];
+        value = narrowNumerics(paramType, value);
+        try {
+            setter.invoke(tobj, value);
+            return this;
+        } catch (InvocationTargetException e) {
+            throw e.getCause();
+        } catch (IllegalArgumentException e) {
+            String msg = String.format("; setter parameter type: %s, set value type: %s",
+                                       paramType.getName(), value.getClass().getName());
+            throw new IllegalArgumentException(e.getMessage() + msg);
+        } catch (Exception e) {
+            throw e;
+        }
+    }
+
+    // narrowing conversion is compile-time so in reflection is not supported
+    // this is clunky workaround for numeric datatypes
+    private Object narrowNumerics(Class<?> targetType, Object value) {
+        Class<?> sourceType = value.getClass();
+        if (sourceType == Double.class) {
+            if (targetType == Integer.class || targetType == Integer.TYPE) {
+                value = ((Double)value).intValue();
+            } else if (targetType == Short.class || targetType == Short.TYPE) {
+                value = ((Double)value).shortValue();
+            } else if (targetType == Byte.class || targetType == Byte.TYPE) {
+                value = ((Double)value).byteValue();
+            }
+        } else if (sourceType == Float.class) {
+            if (targetType == Integer.class || targetType == Integer.TYPE) {
+                value = ((Float)value).intValue();
+            } else if (targetType == Short.class || targetType == Short.TYPE) {
+                value = ((Float)value).shortValue();
+            } else if (targetType == Byte.class || targetType == Byte.TYPE) {
+                value = ((Float)value).byteValue();
+            }
+        } else if (sourceType == Integer.class) {
+            if (targetType == Short.class || targetType == Short.TYPE) {
+                value = ((Integer)value).shortValue();
+            } else if (targetType == Byte.class || targetType == Byte.TYPE) {
+                value = ((Integer)value).byteValue();
+            }
+        } else if (sourceType == Short.class 
+                && (targetType == Byte.class || targetType == Byte.TYPE)) {
+            value = ((Integer)value).byteValue();
+
+        }
+        return value;
+    }
+
+    public Object getValue(String getterName) throws Throwable {
+        return getValue(getters.get(getterName));
+    }
+
+    public Object getValue(Method getter) throws Throwable {
+        try {
+            return getter.invoke(tobj);
+        } catch (InvocationTargetException e) {
+            throw e.getCause();
+        } catch (Exception e) {
+            throw e;
+        }
+    }
+
+    private boolean isGetter(Method m) {
+        return m.getParameterTypes().length == 0
+               && (m.getName().startsWith("get") || m.getName().startsWith("is"));
+    }
+
+    private String getterName(Method m) {
+        return m.getName().replace("is", "").replace("get", "").toLowerCase();
+    }
+
+    private boolean isSetter(Method m) {
+        return m.getReturnType().equals(void.class) && m.getParameterTypes().length == 1
+               && (m.getName().startsWith("set") || m.getName().startsWith("is"));
+    }
+
+    private String setterName(Method m) {
+        return m.getName().replace("is", "").replace("set", "").toLowerCase();
+    }
+
+}

Modified: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/ConditionType.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/ConditionType.java?rev=921968&r1=921967&r2=921968&view=diff
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/ConditionType.java (original)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/ConditionType.java Thu Mar 11 18:14:20 2010
@@ -25,12 +25,12 @@ package org.apache.cxf.jaxrs.ext.search;
  */
 public enum ConditionType {
     EQUALS,
+    NOT_EQUALS,
     LESS_THAN,
     GREATER_THAN,
     LESS_OR_EQUALS,
     GREATER_OR_EQUALS,
     OR,
     AND,
-    NOT,
     CUSTOM
 }

Added: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/FiqlParseException.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/FiqlParseException.java?rev=921968&view=auto
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/FiqlParseException.java (added)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/FiqlParseException.java Thu Mar 11 18:14:20 2010
@@ -0,0 +1,39 @@
+/**
+ * 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.cxf.jaxrs.ext.search;
+
+public class FiqlParseException extends Exception {
+
+    public FiqlParseException() {
+        super();
+    }
+
+    public FiqlParseException(String arg0, Throwable arg1) {
+        super(arg0, arg1);
+    }
+
+    public FiqlParseException(String arg0) {
+        super(arg0);
+    }
+
+    public FiqlParseException(Throwable arg0) {
+        super(arg0);
+    }
+
+}

Added: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/FiqlParser.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/FiqlParser.java?rev=921968&view=auto
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/FiqlParser.java (added)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/FiqlParser.java Thu Mar 11 18:14:20 2010
@@ -0,0 +1,339 @@
+/**
+ * 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.cxf.jaxrs.ext.search;
+
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.xml.datatype.DatatypeConfigurationException;
+import javax.xml.datatype.DatatypeFactory;
+
+/**
+ * Parses <a href="http://tools.ietf.org/html/draft-nottingham-atompub-fiql-00">FIQL</a> expression to
+ * construct {@link SearchCondition} structure. Since this class operates on Java type T, not on XML
+ * structures "selectors" part of specification is not applicable; instead selectors describes getters of type
+ * T used as search condition type (see {@link SimpleSearchCondition#isMet(Object)} for details.
+ * 
+ * @param <T> type of search condition.
+ */
+public class FiqlParser<T> {
+
+    public static final String OR = ",";
+    public static final String AND = ";";
+
+    public static final String GT = "=gt=";
+    public static final String GE = "=ge=";
+    public static final String LT = "=lt=";
+    public static final String LE = "=le=";
+    public static final String EQ = "==";
+    public static final String NEQ = "!=";
+
+    private static Map<String, ConditionType> operatorsMap;
+    static {
+        operatorsMap = new HashMap<String, ConditionType>();
+        operatorsMap.put(GT, ConditionType.GREATER_THAN);
+        operatorsMap.put(GE, ConditionType.GREATER_OR_EQUALS);
+        operatorsMap.put(LT, ConditionType.LESS_THAN);
+        operatorsMap.put(LE, ConditionType.LESS_OR_EQUALS);
+        operatorsMap.put(EQ, ConditionType.EQUALS);
+        operatorsMap.put(NEQ, ConditionType.NOT_EQUALS);
+    }
+
+    private Beanspector<T> beanspector;
+
+    /**
+     * Creates FIQL parser.
+     * 
+     * @param tclass - class of T used to create condition objects in built syntax tree. Class T must have
+     *            accessible no-arg constructor and complementary setters to these used in FIQL expressions.
+     */
+    public FiqlParser(Class<T> tclass) {
+        beanspector = new Beanspector<T>(tclass);
+    }
+
+    /**
+     * Parses expression and builds search filter. Names used in FIQL expression are names of getters/setters
+     * in type T.
+     * <p>
+     * Example:
+     * 
+     * <pre>
+     * class Condition {
+     *   public String getFoo() {...}
+     *   public void setFoo(String foo) {...}
+     *   public int getBar() {...}
+     *   public void setBar(int bar) {...}
+     * }
+     * 
+     * FiqlParser&lt;Condition> parser = new FiqlParser&lt;Condition&gt;(Condition.class);
+     * parser.parse("foo==mystery*;bar=ge=10");
+     * </pre>
+     * 
+     * @param fiqlExpression expression of filter.
+     * @return tree of {@link SearchCondition} objects representing runtime search structure.
+     * @throws FiqlParseException when expression does not follow FIQL grammar
+     */
+    public SearchCondition<T> parse(String fiqlExpression) throws FiqlParseException {
+        ASTNode<T> ast = parseAndsOrsParens(fiqlExpression);
+        // System.out.println(ast);
+        return ast.build();
+    }
+
+    private ASTNode<T> parseAndsOrsParens(String expr) throws FiqlParseException {
+        String s1 = "([\\p{ASCII}&&[^;,()]]+|\\([\\p{ASCII}]+\\))([;,])?";
+        Pattern p = Pattern.compile(s1);
+        Matcher m = p.matcher(expr);
+        List<String> subexpressions = new ArrayList<String>();
+        List<String> operators = new ArrayList<String>();
+        int lastEnd = -1;
+        while (m.find()) {
+            subexpressions.add(m.group(1));
+            operators.add(m.group(2));
+            if (lastEnd != -1 && lastEnd != m.start()) {
+                throw new FiqlParseException(String
+                    .format("Unexpected characters \"%s\" starting at position %d", expr.substring(lastEnd, m
+                        .start()), lastEnd));
+            }
+            lastEnd = m.end();
+        }
+        if (operators.get(operators.size() - 1) != null) {
+            String op = operators.get(operators.size() - 1);
+            String ex = subexpressions.get(subexpressions.size() - 1);
+            throw new FiqlParseException("Dangling operator at the end of expression: ..." + ex + op);
+        }
+        // looking for adjacent ANDs then group them into ORs
+        // Note: in case not ANDs is found (e.g only ORs) every single subexpression is
+        // treated as "single item group of ANDs"
+        int from = 0;
+        int to = 0;
+        SubExpression ors = new SubExpression(OR);
+        while (to < operators.size()) {
+            while (to < operators.size() && AND.equals(operators.get(to))) {
+                to++;
+            }
+            SubExpression ands = new SubExpression(AND);
+            for (; from <= to; from++) {
+                String subex = subexpressions.get(from);
+                ASTNode<T> node = null;
+                if (subex.startsWith("(")) {
+                    node = parseAndsOrsParens(subex.substring(1, subex.length() - 1));
+                } else {
+                    node = parseComparison(subex);
+                }
+                ands.add(node);
+            }
+            to = from;
+            if (ands.getSubnodes().size() == 1) {
+                ors.add(ands.getSubnodes().get(0));
+            } else {
+                ors.add(ands);
+            }
+        }
+        if (ors.getSubnodes().size() == 1) {
+            return ors.getSubnodes().get(0);
+        } else {
+            return ors;
+        }
+    }
+
+    private Comparison parseComparison(String expr) throws FiqlParseException {
+        String comparators = GT + "|" + GE + "|" + LT + "|" + LE + "|" + EQ + "|" + NEQ;
+        String s1 = "[\\p{ASCII}]+(" + comparators + ")";
+        Pattern p = Pattern.compile(s1);
+        Matcher m = p.matcher(expr);
+        if (m.find()) {
+            String name = expr.substring(0, m.start(1));
+            String operator = m.group(1);
+            String value = expr.substring(m.end(1));
+            if ("".equals(value)) {
+                throw new FiqlParseException("Not a comparison expression: " + expr);
+            }
+            Object castedValue = parseDatatype(name, value);
+            return new Comparison(name, operator, castedValue);
+        } else {
+            throw new FiqlParseException("Not a comparison expression: " + expr);
+        }
+    }
+
+    private Object parseDatatype(String setter, String value) throws FiqlParseException {
+        Object castedValue = value;
+        Class<?> valueType;
+        try {
+            valueType = beanspector.getAccessorType(setter);
+        } catch (Exception e) {
+            throw new FiqlParseException(e);
+        }
+        if (Number.class.isAssignableFrom(valueType)) {
+            try {
+                castedValue = Float.parseFloat(value);
+            } catch (NumberFormatException e) {
+                throw new FiqlParseException("Cannot parse " + value + " as number", e);
+            }
+        } else if (Date.class.isAssignableFrom(valueType)) {
+            DateFormat df;
+            try {
+                df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
+                // zone in XML is "+01:00" in Java is "+0100"; stripping semicolon
+                int idx = value.lastIndexOf(':');
+                if (idx != -1) {
+                    String v = value.substring(0, idx) + value.substring(idx + 1);
+                    castedValue = df.parse(v);
+                } else {
+                    castedValue = df.parse(value);
+                }
+            } catch (ParseException e) {
+                // is that duration?
+                try {
+                    Date now = new Date();
+                    DatatypeFactory.newInstance().newDuration(value).addTo(now);
+                    castedValue = now;
+                } catch (DatatypeConfigurationException e1) {
+                    throw new FiqlParseException(e1);
+                } catch (IllegalArgumentException e1) {
+                    throw new FiqlParseException("Can parse " + value + " neither as date nor duration", e);
+                }
+            }
+        }
+        return castedValue;
+    }
+
+    // node of abstract syntax tree
+    private interface ASTNode<T> {
+        SearchCondition<T> build() throws FiqlParseException;
+    }
+
+    private class SubExpression implements ASTNode<T> {
+        private String operator;
+        private List<ASTNode<T>> subnodes = new ArrayList<ASTNode<T>>();
+
+        public SubExpression(String operator) {
+            this.operator = operator;
+        }
+
+        public void add(ASTNode<T> node) {
+            subnodes.add(node);
+        }
+
+        public List<ASTNode<T>> getSubnodes() {
+            return Collections.unmodifiableList(subnodes);
+        }
+
+        @Override
+        public String toString() {
+            String s = operator.equals(AND) ? "AND" : "OR";
+            s += ":[";
+            for (int i = 0; i < subnodes.size(); i++) {
+                s += subnodes.get(i);
+                if (i < subnodes.size() - 1) {
+                    s += ", ";
+                }
+            }
+            s += "]";
+            return s;
+        }
+
+        public SearchCondition<T> build() throws FiqlParseException {
+            boolean hasSubtree = false;
+            for (ASTNode<T> node : subnodes) {
+                if (node instanceof FiqlParser.SubExpression) {
+                    hasSubtree = true;
+                    break;
+                }
+            }
+            if (!hasSubtree && AND.equals(operator)) {
+                try {
+                    // Optimization: single SimpleSearchCondition for 'AND' conditions
+                    Map<String, ConditionType> map = new HashMap<String, ConditionType>();
+                    beanspector.instantiate();
+                    for (ASTNode<T> node : subnodes) {
+                        FiqlParser<T>.Comparison comp = (Comparison)node;
+                        map.put(comp.getName(), operatorsMap.get(comp.getOperator()));
+                        beanspector.setValue(comp.getName(), comp.getValue());
+                    }
+                    return new SimpleSearchCondition<T>(map, beanspector.getBean());
+                } catch (Throwable e) {
+                    throw new RuntimeException(e);
+                }
+            } else {
+                List<SearchCondition<T>> scNodes = new ArrayList<SearchCondition<T>>();
+                for (ASTNode<T> node : subnodes) {
+                    scNodes.add(node.build());
+                }
+                if (OR.equals(operator)) {
+                    return new OrSearchCondition<T>(scNodes);
+                } else {
+                    return new AndSearchCondition<T>(scNodes);
+                }
+            }
+        }
+    }
+
+    private class Comparison implements ASTNode<T> {
+        private String name;
+        private String operator;
+        private Object value;
+
+        public Comparison(String name, String operator, Object value) {
+            this.name = name;
+            this.operator = operator;
+            this.value = value;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public String getOperator() {
+            return operator;
+        }
+
+        public Object getValue() {
+            return value;
+        }
+
+        @Override
+        public String toString() {
+            return name + " " + operator + " " + value + " (" + value.getClass().getSimpleName() + ")";
+        }
+
+        public SearchCondition<T> build() throws FiqlParseException {
+            T cond = createTemplate(name, value);
+            ConditionType ct = operatorsMap.get(operator);
+            return new SimpleSearchCondition<T>(ct, cond);
+        }
+
+        private T createTemplate(String setter, Object val) throws FiqlParseException {
+            try {
+                beanspector.instantiate().setValue(setter, val);
+                return beanspector.getBean();
+            } catch (Throwable e) {
+                throw new FiqlParseException(e);
+            }
+        }
+    }
+}

Modified: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/OrSearchCondition.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/OrSearchCondition.java?rev=921968&r1=921967&r2=921968&view=diff
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/OrSearchCondition.java (original)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/OrSearchCondition.java Thu Mar 11 18:14:20 2010
@@ -18,6 +18,7 @@
  */
 package org.apache.cxf.jaxrs.ext.search;
 
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
@@ -62,8 +63,13 @@ public class OrSearchCondition<T> implem
     }
 
     public List<T> findAll(List<T> pojos) {
-        // TODO Auto-generated method stub
-        return null;
+        List<T> result = new ArrayList<T>();
+        for (T pojo : pojos) {
+            if (isMet(pojo)) {
+                result.add(pojo);
+            }
+        }
+        return result;
     }
 
 }

Modified: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/SimpleSearchCondition.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/SimpleSearchCondition.java?rev=921968&r1=921967&r2=921968&view=diff
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/SimpleSearchCondition.java (original)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/SimpleSearchCondition.java Thu Mar 11 18:14:20 2010
@@ -18,28 +18,35 @@
  */
 package org.apache.cxf.jaxrs.ext.search;
 
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
+/**
+ * Simple search condition comparing primitive objects or complex object by its getters. For details see
+ * {@link #isMet(Object)} description.
+ * 
+ * @param <T> type of search condition.
+ */
 public class SimpleSearchCondition<T> implements SearchCondition<T> {
 
     private static Set<ConditionType> supportedTypes = new HashSet<ConditionType>();
     static {
         supportedTypes.add(ConditionType.EQUALS);
+        supportedTypes.add(ConditionType.NOT_EQUALS);
         supportedTypes.add(ConditionType.GREATER_THAN);
         supportedTypes.add(ConditionType.GREATER_OR_EQUALS);
         supportedTypes.add(ConditionType.LESS_THAN);
         supportedTypes.add(ConditionType.LESS_OR_EQUALS);
     }
     private ConditionType cType;
-    private Map<String, ConditionType> getters2operators;
     private T condition;
-    private List<Method> getters;
+    private Map<String, ConditionType> getters2operators;
+    private Map<String, Object> getters2values;
+    private Beanspector<T> beanspector;
 
     /**
      * Creates search condition with same operator (equality, inequality) applied in all comparison; see
@@ -119,8 +126,8 @@ public class SimpleSearchCondition<T> im
      * inequalities requires type T implementing {@link Comparable}.
      * <p>
      * For other types comparison of given object against template object is done using these <b>getters</b>;
-     * returned "is met" value is <b>conjunction (*and*)</b> of comparisons per each getter. Getters of
-     * template object that return null or throw exception are not used in comparison, in extreme if all
+     * returned "is met" value is <b>conjunction ('and' operator)</b> of comparisons per each getter. Getters
+     * of template object that return null or throw exception are not used in comparison, in extreme if all
      * getters are excluded it means every given pojo object matches. If
      * {@link #SimpleSearchCondition(ConditionType, Object) constructor with shared operator} was used, then
      * getters are compared using the same operator. If {@link #SimpleSearchCondition(Map, Object) constructor
@@ -130,9 +137,19 @@ public class SimpleSearchCondition<T> im
      * {@link Object#equals(Object)}, using inequalities requires that getter type implements
      * {@link Comparable}.
      * <p>
+     * For equality comparison and String type in template object (either being primitive or getter from
+     * complex type) it is allowed to used asterisk at the beginning or at the end of text as wild card (zero
+     * or more of any characters) e.g. "foo*", "*foo" or "*foo*". Inner asterisks are not interpreted as wild
+     * cards.
+     * <p>
      * <b>Example:</b>
      * 
      * <pre>
+     * SimpleSearchCondition&lt;Integer&gt; ssc = new SimpleSearchCondition&lt;Integer&gt;(
+     *   ConditionType.GREATER_THAN, 10);    
+     * ssc.isMet(20);
+     * // true since 20&gt;10 
+     * 
      * class Entity {
      *   public String getName() {...
      *   public int getLevel() {...
@@ -140,17 +157,17 @@ public class SimpleSearchCondition<T> im
      * }
      * 
      * Entity template = new Entity("bbb", 10, null);
-     * SimpleSearchCondition&lt;Entity> ssc = new SimpleSearchCondition&lt;Entity>(
+     * ssc = new SimpleSearchCondition&lt;Entity&gt;(
      *   ConditionType.GREATER_THAN, template);    
      * 
      * ssc.isMet(new Entity("aaa", 20, "some mesage")); 
-     * // false: is not met, expression '"aaa">"bbb" and 20>10' is not true  
+     * // false: is not met, expression '"aaa"&gt;"bbb" and 20&gt;10' is not true  
      * // since "aaa" is not greater than "bbb"; not that message is null in template hence ingored
      * 
      * ssc.isMet(new Entity("ccc", 30, "other message"));
-     * // true: is met, expression '"ccc">"bbb" and 30>10' is true
+     * // true: is met, expression '"ccc"&gt;"bbb" and 30&gt;10' is true
      * 
-     * Map&lt;String,ConditionType> map;
+     * Map&lt;String,ConditionType&gt; map;
      * map.put("name", ConditionType.EQUALS);
      * map.put("level", ConditionType.GREATER_THAN);
      * ssc = new SimpleSearchCondition&lt;Entity&gt;(
@@ -168,16 +185,17 @@ public class SimpleSearchCondition<T> im
             return compare(pojo, cType, condition);
         } else {
             boolean matches = false;
-            for (Method getter : loadGetters()) {
+            Map<String, Object> get2val = getGettersAndValues();
+            for (String getter : get2val.keySet()) {
                 ConditionType ct = cType;
                 if (ct == null) {
-                    ct = getters2operators.get(getterName(getter));
+                    ct = getters2operators.get(getter);
                     if (ct == null) {
                         continue;
                     }
                 }
                 Object lval = getValue(getter, pojo);
-                Object rval = getValue(getter, condition);
+                Object rval = get2val.get(getter);
                 matches = compare(lval, ct, rval);
                 if (!matches) {
                     break;
@@ -187,49 +205,55 @@ public class SimpleSearchCondition<T> im
         }
     }
 
-    private List<Method> loadGetters() {
-        if (getters == null) {
-            getters = new ArrayList<Method>();
-            for (Method m : condition.getClass().getMethods()) {
-                if (isGetter(m)) {
-                    getters.add(m);
-                }
+    /**
+     * Creates cache of getters from template (condition) object and its values returned during one-pass
+     * invocation. Method isMet() will use its keys to introspect getters of passed pojo object, and values
+     * from map in comparison.
+     * 
+     * @return template (condition) object getters mapped to their non-null values
+     */
+    private Map<String, Object> getGettersAndValues() {
+        if (getters2values == null) {
+            getters2values = new HashMap<String, Object>();
+            beanspector = new Beanspector<T>(condition);
+            for (String getter : beanspector.getGettersNames()) {
+                Object value = getValue(getter, condition);
+                getters2values.put(getter, value);
             }
+            //we do not need compare class objects
+            getters2values.keySet().remove("class");
         }
-        return getters;
+        return getters2values;
     }
 
-    private boolean isPrimitive(T pojo) {
-        return pojo.getClass().getName().startsWith("java.lang");
-    }
-
-    private boolean isGetter(Method m) {
-        return m.getParameterTypes().length == 0
-               && (m.getName().startsWith("get") || m.getName().startsWith("is"));
-    }
-    
-    private String getterName(Method m) {
-        return m.getName().replace("is", "").replace("get", "").toLowerCase();
-    }
-
-    private Object getValue(Method getter, T pojo) {
+    private Object getValue(String getter, T pojo) {
         try {
-            return getter.invoke(pojo);
-        } catch (IllegalArgumentException e) {
-            throw e;
-        } catch (IllegalAccessException e) {
-            throw new RuntimeException(e);
-        } catch (InvocationTargetException e) {
-            // getter exception is null equivalent
+            return beanspector.swap(pojo).getValue(getter);
+        } catch (Throwable e) {
             return null;
         }
     }
 
+    private boolean isPrimitive(T pojo) {
+        return pojo.getClass().getName().startsWith("java.lang");
+    }
+
     @SuppressWarnings("unchecked")
     private boolean compare(Object lval, ConditionType cond, Object rval) {
         boolean compares = true;
-        if (cond == ConditionType.EQUALS) {
-            compares = (lval != null) ? lval.equals(rval) : true;
+        if (cond == ConditionType.EQUALS || cond == ConditionType.NOT_EQUALS) {
+            if (rval == null) {
+                compares = true;
+            } else {
+                if (lval instanceof String) {
+                    compares = textCompare((String)lval, (String)rval);
+                } else {
+                    compares = lval.equals(rval);
+                }
+                if (cond == ConditionType.NOT_EQUALS) {
+                    compares = !compares;
+                }
+            }
         } else {
             if (lval instanceof Comparable && rval instanceof Comparable) {
                 Comparable lcomp = (Comparable)lval;
@@ -257,6 +281,32 @@ public class SimpleSearchCondition<T> im
         return compares;
     }
 
+    private boolean textCompare(String lval, String rval) {
+        // check wild cards
+        boolean starts = false;
+        boolean ends = false;
+        if (rval.charAt(0) == '*') {
+            starts = true;
+            rval = rval.substring(1);
+        }
+        if (rval.charAt(rval.length() - 1) == '*') {
+            ends = true;
+            rval = rval.substring(0, rval.length() - 1);
+        }
+        if (starts || ends) {
+            // wild card tests
+            if (starts && !ends) {
+                return lval.endsWith(rval);
+            } else if (ends && !starts) {
+                return lval.startsWith(rval);
+            } else {
+                return lval.contains(rval);
+            }
+        } else {
+            return lval.equals(rval);
+        }
+    }
+
     public List<T> findAll(List<T> pojos) {
         List<T> result = new ArrayList<T>();
         for (T pojo : pojos) {

Added: cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/ext/search/FiqlParserTest.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/ext/search/FiqlParserTest.java?rev=921968&view=auto
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/ext/search/FiqlParserTest.java (added)
+++ cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/ext/search/FiqlParserTest.java Thu Mar 11 18:14:20 2010
@@ -0,0 +1,240 @@
+/**
+ * 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.cxf.jaxrs.ext.search;
+
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import javax.xml.datatype.DatatypeFactory;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class FiqlParserTest {
+    private FiqlParser<Condition> parser = new FiqlParser<Condition>(Condition.class);
+
+    @Test(expected = FiqlParseException.class)
+    public void testCompareWrongComparator() throws FiqlParseException {
+        parser.parse("name>booba");
+    }
+
+    @Test(expected = FiqlParseException.class)
+    public void testCompareMissingName() throws FiqlParseException {
+        parser.parse("==30");
+    }
+
+    @Test(expected = FiqlParseException.class)
+    public void testCompareMissingValue() throws FiqlParseException {
+        parser.parse("name=gt=");
+    }
+
+    @Test
+    public void testCompareValueTextSpaces() throws FiqlParseException {
+        parser.parse("name=gt=some text");
+    }
+
+    @Test(expected = FiqlParseException.class)
+    public void testCompareNameTextSpaces() throws FiqlParseException {
+        parser.parse("some name=gt=text");
+    }
+
+    @Test(expected = FiqlParseException.class)
+    public void testDanglingOperator() throws FiqlParseException {
+        parser.parse("name==a;(level==10;),");
+    }
+
+    @Test
+    public void testMultilevelExpression() throws FiqlParseException {
+        parser.parse("name==a;(level==10,(name!=b;name!=c;(level=gt=10)))");
+    }
+
+    @Test
+    public void testRedundantParens() throws FiqlParseException {
+        parser.parse("name==a;((((level==10))))");
+    }
+
+    @Test(expected = FiqlParseException.class)
+    public void testUnmatchedParen() throws FiqlParseException {
+        parser.parse("name==a;(level==10,(name!=b)");
+    }
+
+    @Test(expected = FiqlParseException.class)
+    public void testUnmatchedParen2() throws FiqlParseException {
+        parser.parse("name==bbb;))()level==111");
+    }
+
+    @Test(expected = FiqlParseException.class)
+    public void testMissingComparison() throws FiqlParseException {
+        parser.parse("name==bbb;,level==111");
+    }
+
+    @Test(expected = FiqlParseException.class)
+    public void testSetterMissing() throws FiqlParseException {
+        parser.parse("noSuchSetter==xxx");
+    }
+
+    @Test(expected = FiqlParseException.class)
+    public void testSetterWrongType() throws FiqlParseException {
+        parser.parse("exception==text");
+    }
+
+    @Test
+    public void testSetterNumericText() throws FiqlParseException {
+        parser.parse("name==10");
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testMismatchedAccessorTypes() throws FiqlParseException {
+        new FiqlParser<MismatchedTypes>(MismatchedTypes.class);
+    }
+
+    @Test
+    public void testParseName() throws FiqlParseException {
+        SearchCondition<Condition> filter = parser.parse("name==king");
+        assertTrue(filter.isMet(new Condition("king", 10, new Date())));
+        assertTrue(filter.isMet(new Condition("king", 0, null)));
+        assertFalse(filter.isMet(new Condition("diamond", 10, new Date())));
+        assertFalse(filter.isMet(new Condition("diamond", 0, null)));
+    }
+
+    @Test
+    public void testParseLevel() throws FiqlParseException {
+        SearchCondition<Condition> filter = parser.parse("level=gt=10");
+        assertTrue(filter.isMet(new Condition("whatever", 15, new Date())));
+        assertTrue(filter.isMet(new Condition(null, 15, null)));
+        assertFalse(filter.isMet(new Condition("blah", 5, new Date())));
+        assertFalse(filter.isMet(new Condition("foobar", 0, null)));
+    }
+
+    @Test
+    public void testParseDate() throws FiqlParseException, ParseException {
+        SearchCondition<Condition> filter = parser.parse("time=le=2010-03-11T18:00:00.000+00:00");
+        DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
+        assertTrue(filter.isMet(new Condition("whatever", 15, df.parse("2010-03-11T18:00:00.000+0000"))));
+        assertTrue(filter.isMet(new Condition(null, null, df.parse("2010-03-10T22:22:00.000+0000"))));
+        assertFalse(filter.isMet(new Condition("blah", null, df.parse("2010-03-12T00:00:00.000+0000"))));
+        assertFalse(filter.isMet(new Condition(null, 123, df.parse("2010-03-12T00:00:00.000+0000"))));
+    }
+
+    @Test
+    public void testParseDateDuration() throws Exception {
+        SearchCondition<Condition> filter = parser.parse("time=gt=-PT1M");
+        Date now = new Date();
+        Date tenMinutesAgo = new Date();
+        DatatypeFactory.newInstance().newDuration("-PT10M").addTo(tenMinutesAgo);
+        assertTrue(filter.isMet(new Condition(null, null, now)));
+        assertFalse(filter.isMet(new Condition(null, null, tenMinutesAgo)));
+    }
+
+    @Test
+    public void testParseComplex1() throws FiqlParseException {
+        SearchCondition<Condition> filter = parser.parse("name==ami*;level=gt=10");
+        assertTrue(filter.isMet(new Condition("amichalec", 12, new Date())));
+        assertTrue(filter.isMet(new Condition("ami", 12, new Date())));
+        assertFalse(filter.isMet(new Condition("ami", 8, null)));
+        assertFalse(filter.isMet(new Condition("am", 20, null)));
+    }
+
+    @Test
+    public void testParseComplex2() throws FiqlParseException {
+        SearchCondition<Condition> filter = parser.parse("name==ami*,level=gt=10");
+        assertTrue(filter.isMet(new Condition("ami", 0, new Date())));
+        assertTrue(filter.isMet(new Condition("foo", 20, null)));
+        assertFalse(filter.isMet(new Condition("foo", 0, null)));
+    }
+
+    @Test
+    public void testParseComplex3() throws FiqlParseException {
+        SearchCondition<Condition> filter = parser.parse("name==foo*;(name!=*bar,level=gt=10)");
+        assertTrue(filter.isMet(new Condition("fooooo", 0, null)));
+        assertTrue(filter.isMet(new Condition("fooooobar", 20, null)));
+        assertFalse(filter.isMet(new Condition("fooobar", 0, null)));
+        assertFalse(filter.isMet(new Condition("bar", 20, null)));
+    }
+
+    @Test
+    public void testParseComplex4() throws FiqlParseException {
+        SearchCondition<Condition> filter = parser.parse("name==foo*;name!=*bar,level=gt=10");
+        assertTrue(filter.isMet(new Condition("zonk", 20, null)));
+        assertTrue(filter.isMet(new Condition("foobaz", 0, null)));
+        assertTrue(filter.isMet(new Condition("foobar", 20, null)));
+        assertFalse(filter.isMet(new Condition("fooxxxbar", 0, null)));
+    }
+
+    @Ignore
+    static class Condition {
+        private String name;
+        private Integer level;
+        private Date time;
+
+        public Condition() {
+        }
+
+        public Condition(String name, Integer level, Date time) {
+            this.name = name;
+            this.level = level;
+            this.time = time;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        public Integer getLevel() {
+            return level;
+        }
+
+        public void setLevel(Integer level) {
+            this.level = level;
+        }
+
+        public Date getTime() {
+            return time;
+        }
+
+        public void setTime(Date time) {
+            this.time = time;
+        }
+
+        public void setException(Exception ex) {
+            // do nothing
+        }
+
+    }
+
+    // TODO refactor to Beanspector unit tests
+    @Ignore
+    static class MismatchedTypes {
+        public Date getFoo() {
+            return null;
+        }
+
+        public void setFoo(String val) {
+        }
+    }
+}

Modified: cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/ext/search/SimpleSearchConditionTest.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/ext/search/SimpleSearchConditionTest.java?rev=921968&r1=921967&r2=921968&view=diff
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/ext/search/SimpleSearchConditionTest.java (original)
+++ cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/ext/search/SimpleSearchConditionTest.java Thu Mar 11 18:14:20 2010
@@ -26,9 +26,10 @@ import java.util.Map;
 import org.junit.BeforeClass;
 import org.junit.Test;
 
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 public class SimpleSearchConditionTest {
 
@@ -65,6 +66,7 @@ public class SimpleSearchConditionTest {
     private static SearchCondition<DoubleAttr> dc2Leq;
 
     private static List<ConditionType> supported = Arrays.asList(ConditionType.EQUALS,
+                                                                 ConditionType.NOT_EQUALS,
                                                                  ConditionType.GREATER_OR_EQUALS,
                                                                  ConditionType.GREATER_THAN,
                                                                  ConditionType.LESS_OR_EQUALS,
@@ -121,7 +123,7 @@ public class SimpleSearchConditionTest {
     public void testCtorMapNull() {
         new SimpleSearchCondition<SingleAttr>((Map<String, ConditionType>)null, attr);
     }
-    
+
     @Test
     public void testCtorMapCondSupported() {
         for (ConditionType ct : ConditionType.values()) {
@@ -287,16 +289,50 @@ public class SimpleSearchConditionTest {
         Map<String, ConditionType> map = new HashMap<String, ConditionType>();
         map.put("foo", ConditionType.LESS_THAN);
         map.put("bar", ConditionType.GREATER_THAN);
-        
+
         // expression "template.getFoo() < pojo.getFoo() & template.getBar() > pojo.getBar()"
         assertTrue(new SimpleSearchCondition<DoubleAttr>(map, new DoubleAttr("bbb", "ccc"))
             .isMet(new DoubleAttr("aaa", "ddd")));
-        
+
         // expression "template.getBar() > pojo.getBar()"
         assertTrue(new SimpleSearchCondition<DoubleAttr>(map, new DoubleAttr(null, "ccc"))
             .isMet(new DoubleAttr("!not-interpreted!", "ddd")));
     }
 
+    @Test
+    public void testIsMetWildcardEnds() {
+        SimpleSearchCondition<String> ssc = new SimpleSearchCondition<String>(ConditionType.EQUALS, "bar*");
+        assertTrue(ssc.isMet("bar"));
+        assertTrue(ssc.isMet("barbaz"));
+        assertFalse(ssc.isMet("foobar"));
+    }
+
+    @Test
+    public void testIsMetWildcardStarts() {
+        SimpleSearchCondition<String> ssc = new SimpleSearchCondition<String>(ConditionType.EQUALS, "*bar");
+        assertTrue(ssc.isMet("bar"));
+        assertFalse(ssc.isMet("barbaz"));
+        assertTrue(ssc.isMet("foobar"));
+    }
+
+    @Test
+    public void testIsMetWildcardStartsEnds() {
+        SimpleSearchCondition<String> ssc = new SimpleSearchCondition<String>(ConditionType.EQUALS, "*bar*");
+        assertTrue(ssc.isMet("bar"));
+        assertTrue(ssc.isMet("barbaz"));
+        assertTrue(ssc.isMet("foobar"));
+    }
+
+    @Test
+    public void testIsMetWildcardMultiAsterisk() {
+        SimpleSearchCondition<String> ssc = new SimpleSearchCondition<String>(ConditionType.EQUALS, "*ba*r*");
+        assertFalse(ssc.isMet("bar"));
+        assertTrue(ssc.isMet("ba*r"));
+        assertTrue(ssc.isMet("fooba*r"));
+        assertTrue(ssc.isMet("fooba*rbaz"));
+        assertFalse(ssc.isMet("foobarbaz"));
+    }
+
     static class SingleAttr {
         private String foo;
 



Mime
View raw message