You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by re...@apache.org on 2014/02/10 01:48:04 UTC

svn commit: r1566482 - in /cxf/trunk: parent/ rt/rs/extensions/search/ rt/rs/extensions/search/src/main/java/org/apache/cxf/jaxrs/ext/search/ rt/rs/extensions/search/src/main/java/org/apache/cxf/jaxrs/ext/search/fiql/ rt/rs/extensions/search/src/main/j...

Author: reta
Date: Mon Feb 10 00:48:04 2014
New Revision: 1566482

URL: http://svn.apache.org/r1566482
Log:
[CXF-5430]: Added initial support for OData 2.0 query language. Covered basic use cases.

Added:
    cxf/trunk/rt/rs/extensions/search/src/main/java/org/apache/cxf/jaxrs/ext/search/AbstractSearchConditionParser.java
    cxf/trunk/rt/rs/extensions/search/src/main/java/org/apache/cxf/jaxrs/ext/search/odata/
    cxf/trunk/rt/rs/extensions/search/src/main/java/org/apache/cxf/jaxrs/ext/search/odata/OData2Parser.java
    cxf/trunk/rt/rs/extensions/search/src/test/java/org/apache/cxf/jaxrs/ext/search/odata/
    cxf/trunk/rt/rs/extensions/search/src/test/java/org/apache/cxf/jaxrs/ext/search/odata/OData2ParserTest.java
Modified:
    cxf/trunk/parent/pom.xml
    cxf/trunk/rt/rs/extensions/search/pom.xml
    cxf/trunk/rt/rs/extensions/search/src/main/java/org/apache/cxf/jaxrs/ext/search/fiql/FiqlParser.java

Modified: cxf/trunk/parent/pom.xml
URL: http://svn.apache.org/viewvc/cxf/trunk/parent/pom.xml?rev=1566482&r1=1566481&r2=1566482&view=diff
==============================================================================
--- cxf/trunk/parent/pom.xml (original)
+++ cxf/trunk/parent/pom.xml Mon Feb 10 00:48:04 2014
@@ -178,6 +178,7 @@
         <cxf.oro.bundle.version>2.0.8_6</cxf.oro.bundle.version>
         <cxf.dom4j.bundle.version>1.6.1_5</cxf.dom4j.bundle.version>
         <cxf.jdom.bundle.version>1.1_4</cxf.jdom.bundle.version>
+        <cxf.olingo.version>1.0.0</cxf.olingo.version>
         <cxf.checkstyle.extension />
         <cxf.jaxb.context.class />
         <cxf.spring.validation.mode>VALIDATION_AUTO</cxf.spring.validation.mode>
@@ -1085,6 +1086,11 @@
                 <version>${cxf.spring.security.version}</version>
             </dependency>
             <dependency>
+			    <groupId>org.apache.olingo</groupId>
+			    <artifactId>olingo-odata2-core-incubating</artifactId>
+			    <version>${cxf.olingo.version}</version>
+			</dependency>
+            <dependency>
                 <groupId>org.springframework.security</groupId>
                 <artifactId>spring-security-core</artifactId>
                 <version>${cxf.spring.security.version}</version>

Modified: cxf/trunk/rt/rs/extensions/search/pom.xml
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/rs/extensions/search/pom.xml?rev=1566482&r1=1566481&r2=1566482&view=diff
==============================================================================
--- cxf/trunk/rt/rs/extensions/search/pom.xml (original)
+++ cxf/trunk/rt/rs/extensions/search/pom.xml Mon Feb 10 00:48:04 2014
@@ -67,6 +67,11 @@
             <optional>true</optional>
         </dependency>
         <dependency>
+		    <groupId>org.apache.olingo</groupId>
+		    <artifactId>olingo-odata2-core-incubating</artifactId>
+		    <optional>true</optional>
+		</dependency>
+        <dependency>
             <groupId>org.apache.lucene</groupId>
             <artifactId>lucene-analyzers-common</artifactId>
             <version>${cxf.lucene.version}</version>

Added: cxf/trunk/rt/rs/extensions/search/src/main/java/org/apache/cxf/jaxrs/ext/search/AbstractSearchConditionParser.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/rs/extensions/search/src/main/java/org/apache/cxf/jaxrs/ext/search/AbstractSearchConditionParser.java?rev=1566482&view=auto
==============================================================================
--- cxf/trunk/rt/rs/extensions/search/src/main/java/org/apache/cxf/jaxrs/ext/search/AbstractSearchConditionParser.java (added)
+++ cxf/trunk/rt/rs/extensions/search/src/main/java/org/apache/cxf/jaxrs/ext/search/AbstractSearchConditionParser.java Mon Feb 10 00:48:04 2014
@@ -0,0 +1,251 @@
+/**
+ * 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.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Map;
+import java.util.Set;
+
+import javax.xml.datatype.DatatypeConfigurationException;
+import javax.xml.datatype.DatatypeFactory;
+
+import org.apache.cxf.jaxrs.ext.search.Beanspector.TypeInfo;
+import org.apache.cxf.jaxrs.ext.search.collections.CollectionCheck;
+import org.apache.cxf.jaxrs.ext.search.collections.CollectionCheckInfo;
+import org.apache.cxf.jaxrs.utils.InjectionUtils;
+
+public abstract class AbstractSearchConditionParser<T> implements SearchConditionParser<T> {
+    public static final String EXTENSION_COUNT = "count";
+    protected static final String EXTENSION_COUNT_OPEN = EXTENSION_COUNT + "(";
+    
+    protected final Map<String, String> contextProperties;
+    protected final Class<T> conditionClass;
+    
+    protected AbstractSearchConditionParser(Class<T> tclass) {
+        this(tclass, Collections.<String, String>emptyMap());
+    }
+    
+    protected AbstractSearchConditionParser(Class<T> tclass, Map<String, String> contextProperties) {
+        this.conditionClass = tclass;
+        this.contextProperties = contextProperties == null 
+            ? Collections.<String, String>emptyMap() : contextProperties;
+    }
+    
+    protected String getSetter(String setter) {
+        int index = getDotIndex(setter);
+        if (index != -1) {
+            return setter.substring(0, index).toLowerCase();
+        } else {
+            return setter;
+        }
+    }
+    
+    protected Object parseType(String originalPropName, 
+                             Object ownerBean, 
+                             Object lastCastedValue, 
+                             String setter, 
+                             TypeInfo typeInfo, 
+                             String value) throws SearchParseException {
+        Class<?> valueType = typeInfo.getTypeClass();
+        boolean isCollection = InjectionUtils.isSupportedCollectionOrArray(valueType);
+        Class<?> actualType = isCollection ? InjectionUtils.getActualType(typeInfo.getGenericType()) : valueType;
+        
+        int index = getDotIndex(setter);
+        if (index == -1) {
+            Object castedValue = value;
+            if (Date.class.isAssignableFrom(valueType)) {
+                castedValue = convertToDate(value);
+            } else {
+                boolean isPrimitive = InjectionUtils.isPrimitive(valueType);
+                boolean isPrimitiveOrEnum = isPrimitive || valueType.isEnum();
+                if (ownerBean == null || isPrimitiveOrEnum) {
+                    try {
+                        CollectionCheck collCheck = getCollectionCheck(originalPropName, isCollection, actualType);
+                        if (collCheck == null) {
+                            castedValue = InjectionUtils.convertStringToPrimitive(value, actualType);
+                        } 
+                        if (collCheck == null && isCollection) {
+                            castedValue = getCollectionSingleton(valueType, castedValue);
+                        } else if (isCollection) {
+                            typeInfo.setCollectionCheckInfo(new CollectionCheckInfo(collCheck, castedValue));
+                            castedValue = getEmptyCollection(valueType);
+                        }
+                    } catch (Exception e) {
+                        throw new SearchParseException("Cannot convert String value \"" + value
+                                                     + "\" to a value of class " + valueType.getName(), e);
+                    }
+                } else {
+                    Class<?> classType = isCollection ? valueType : value.getClass(); 
+                    try {
+                        Method setterM = valueType.getMethod("set" + getMethodNameSuffix(setter),
+                                                             new Class[]{classType});
+                        Object objectValue = !isCollection ? value : getCollectionSingleton(valueType, value);
+                        setterM.invoke(ownerBean, new Object[]{objectValue});
+                        castedValue = objectValue; 
+                    } catch (Throwable ex) {
+                        throw new SearchParseException("Cannot convert String value \"" + value
+                                                       + "\" to a value of class " + valueType.getName(), ex);
+                    }
+                    
+                }
+            }
+            if (lastCastedValue != null) {
+                castedValue = lastCastedValue;
+            }
+            return castedValue;
+        } else {
+            String[] names = setter.split("\\.");
+            try {
+                String nextPart = getMethodNameSuffix(names[1]);
+                Method getterM = actualType.getMethod("get" + nextPart, new Class[]{});   
+                Class<?> returnType = getterM.getReturnType();
+                boolean returnCollection = InjectionUtils.isSupportedCollectionOrArray(returnType);
+                Class<?> actualReturnType = !returnCollection ? returnType 
+                    : InjectionUtils.getActualType(getterM.getGenericReturnType());
+                
+                boolean isPrimitive = InjectionUtils.isPrimitive(returnType) || returnType.isEnum();
+                boolean lastTry = names.length == 2 
+                    && (isPrimitive || returnType == Date.class || returnCollection);
+                
+                Object valueObject = ownerBean != null ? ownerBean 
+                    : actualType.isInterface() 
+                    ? Proxy.newProxyInstance(this.getClass().getClassLoader(), 
+                                             new Class[]{actualType}, 
+                                             new InterfaceProxy())
+                    : actualType.newInstance();
+                Object nextObject;
+                
+                if (lastTry) {
+                    if (!returnCollection) {
+                        nextObject = isPrimitive ? InjectionUtils.convertStringToPrimitive(value, returnType) 
+                            : convertToDate(value);
+                    } else {
+                        CollectionCheck collCheck = getCollectionCheck(originalPropName, true, actualReturnType);
+                        if (collCheck == null) {
+                            nextObject = getCollectionSingleton(valueType, value);
+                        } else {
+                            typeInfo.setCollectionCheckInfo(new CollectionCheckInfo(collCheck, value));
+                            nextObject = getEmptyCollection(valueType);
+                        }
+                    }
+                } else {
+                    nextObject = returnType.newInstance();
+                }
+                
+                Method setterM = actualType.getMethod("set" + nextPart, new Class[]{returnType});
+                setterM.invoke(valueObject, new Object[]{nextObject});
+                
+                if (lastTry) {
+                    lastCastedValue = lastCastedValue == null ? valueObject : lastCastedValue;
+                    return isCollection ? getCollectionSingleton(valueType, lastCastedValue) : lastCastedValue;
+                } else {
+                    lastCastedValue = valueObject;
+                }
+                
+                TypeInfo nextTypeInfo = new TypeInfo(nextObject.getClass(), getterM.getGenericReturnType()); 
+                Object response = parseType(originalPropName,
+                                 nextObject, 
+                                 lastCastedValue, 
+                                 setter.substring(index + 1), 
+                                 nextTypeInfo, 
+                                 value);
+                if (ownerBean == null) {
+                    return isCollection ? getCollectionSingleton(valueType, lastCastedValue) : lastCastedValue;
+                } else {
+                    return response;
+                }
+            } catch (Throwable e) {
+                throw new SearchParseException("Cannot convert String value \"" + value
+                                               + "\" to a value of class " + valueType.getName(), e);
+            }
+        }
+    }
+
+    private CollectionCheck getCollectionCheck(String propName, boolean isCollection, Class<?> actualCls) {
+        if (isCollection) {
+            if (InjectionUtils.isPrimitive(actualCls)) {
+                if (propName.startsWith(EXTENSION_COUNT_OPEN)) {
+                    return CollectionCheck.SIZE;
+                }
+            } else {
+                return CollectionCheck.SIZE;
+            }
+        }
+        return null;
+    }
+    
+    private Object getCollectionSingleton(Class<?> collectionCls, Object value) {
+        if (Set.class.isAssignableFrom(collectionCls)) {
+            return Collections.singleton(value);
+        } else {
+            return Collections.singletonList(value);
+        }
+    }
+    
+    private Object getEmptyCollection(Class<?> collectionCls) {
+        if (Set.class.isAssignableFrom(collectionCls)) {
+            return Collections.emptySet();
+        } else {
+            return Collections.emptyList();
+        }
+    }
+    
+    private Object convertToDate(String value) throws SearchParseException {
+        try {
+            DateFormat df = SearchUtils.getDateFormat(contextProperties);
+            String dateValue = value;
+            if (SearchUtils.isTimeZoneSupported(contextProperties, Boolean.FALSE)) {
+                // zone in XML is "+01:00" in Java is "+0100"; stripping semicolon
+                int idx = value.lastIndexOf(':');
+                if (idx != -1) {
+                    dateValue = value.substring(0, idx) + value.substring(idx + 1);
+                }
+            }
+            return df.parse(dateValue);
+        } catch (ParseException e) {
+            // is that duration?
+            try {
+                Date now = new Date();
+                DatatypeFactory.newInstance().newDuration(value).addTo(now);
+                return now;
+            } catch (DatatypeConfigurationException e1) {
+                throw new SearchParseException(e1);
+            } catch (IllegalArgumentException e1) {
+                throw new SearchParseException("Can parse " + value + " neither as date nor duration", e);
+            }
+        }
+    }
+    
+    private String getMethodNameSuffix(String name) {
+        if (name.length() == 1) {
+            return name.toUpperCase();
+        } else {
+            return Character.toUpperCase(name.charAt(0)) + name.substring(1);
+        }
+    }       
+
+    private int getDotIndex(String setter) {
+        return this.conditionClass == SearchBean.class ? -1 : setter.indexOf(".");
+    }    
+}

Modified: cxf/trunk/rt/rs/extensions/search/src/main/java/org/apache/cxf/jaxrs/ext/search/fiql/FiqlParser.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/rs/extensions/search/src/main/java/org/apache/cxf/jaxrs/ext/search/fiql/FiqlParser.java?rev=1566482&r1=1566481&r2=1566482&view=diff
==============================================================================
--- cxf/trunk/rt/rs/extensions/search/src/main/java/org/apache/cxf/jaxrs/ext/search/fiql/FiqlParser.java (original)
+++ cxf/trunk/rt/rs/extensions/search/src/main/java/org/apache/cxf/jaxrs/ext/search/fiql/FiqlParser.java Mon Feb 10 00:48:04 2014
@@ -17,39 +17,26 @@
  * under the License.
  */
 package org.apache.cxf.jaxrs.ext.search.fiql;
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-import java.text.DateFormat;
-import java.text.ParseException;
 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.Set;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-import javax.xml.datatype.DatatypeConfigurationException;
-import javax.xml.datatype.DatatypeFactory;
-
+import org.apache.cxf.jaxrs.ext.search.AbstractSearchConditionParser;
 import org.apache.cxf.jaxrs.ext.search.AndSearchCondition;
 import org.apache.cxf.jaxrs.ext.search.Beanspector;
 import org.apache.cxf.jaxrs.ext.search.Beanspector.TypeInfo;
 import org.apache.cxf.jaxrs.ext.search.ConditionType;
-import org.apache.cxf.jaxrs.ext.search.InterfaceProxy;
 import org.apache.cxf.jaxrs.ext.search.OrSearchCondition;
 import org.apache.cxf.jaxrs.ext.search.PropertyNotFoundException;
 import org.apache.cxf.jaxrs.ext.search.SearchBean;
 import org.apache.cxf.jaxrs.ext.search.SearchCondition;
-import org.apache.cxf.jaxrs.ext.search.SearchConditionParser;
 import org.apache.cxf.jaxrs.ext.search.SearchParseException;
 import org.apache.cxf.jaxrs.ext.search.SearchUtils;
 import org.apache.cxf.jaxrs.ext.search.SimpleSearchCondition;
-import org.apache.cxf.jaxrs.ext.search.collections.CollectionCheck;
-import org.apache.cxf.jaxrs.ext.search.collections.CollectionCheckInfo;
-import org.apache.cxf.jaxrs.utils.InjectionUtils;
 import org.apache.cxf.message.MessageUtils;
 
 
@@ -61,7 +48,7 @@ import org.apache.cxf.message.MessageUti
  * 
  * @param <T> type of search condition.
  */
-public class FiqlParser<T> implements SearchConditionParser<T> {
+public class FiqlParser<T> extends AbstractSearchConditionParser<T> {
 
     public static final String OR = ",";
     public static final String AND = ";";
@@ -75,10 +62,8 @@ public class FiqlParser<T> implements Se
     
     public static final Map<ConditionType, String> CONDITION_MAP;
         
-    public static final String EXTENSION_COUNT = "count";
-    public static final String SUPPORT_SINGLE_EQUALS = "fiql.support.single.equals.operator";
     
-    private static final String EXTENSION_COUNT_OPEN = EXTENSION_COUNT + "(";
+    public static final String SUPPORT_SINGLE_EQUALS = "fiql.support.single.equals.operator";       
     
     private static final Map<String, ConditionType> OPERATORS_MAP;
     private static final Pattern COMPARATORS_PATTERN;
@@ -112,9 +97,7 @@ public class FiqlParser<T> implements Se
         COMPARATORS_PATTERN_SINGLE_EQUALS = Pattern.compile(s2);
     }
 
-    private Beanspector<T> beanspector;
-    private Class<T> conditionClass;
-    private Map<String, String> contextProperties;
+    private Beanspector<T> beanspector;       
     private Map<String, String> beanPropertiesMap;
     
     private Map<String, ConditionType> operatorsMap = OPERATORS_MAP;
@@ -150,11 +133,11 @@ public class FiqlParser<T> implements Se
     public FiqlParser(Class<T> tclass, 
                       Map<String, String> contextProperties,
                       Map<String, String> beanProperties) {
+        super(tclass, contextProperties);
+        
         beanspector = SearchBean.class.isAssignableFrom(tclass) 
             ? null : new Beanspector<T>(tclass);
-        conditionClass = tclass;
-        this.contextProperties = contextProperties == null 
-            ? Collections.<String, String>emptyMap() : contextProperties;
+        
         this.beanPropertiesMap = beanProperties;
         if (MessageUtils.isTrue(this.contextProperties.get(SUPPORT_SINGLE_EQUALS))) {
             operatorsMap = new HashMap<String, ConditionType>(operatorsMap);
@@ -315,195 +298,9 @@ public class FiqlParser<T> implements Se
         }
         
     }
-
-    private Object parseType(String originalPropName, 
-                             Object ownerBean, 
-                             Object lastCastedValue, 
-                             String setter, 
-                             TypeInfo typeInfo, 
-                             String value) throws SearchParseException {
-        Class<?> valueType = typeInfo.getTypeClass();
-        boolean isCollection = InjectionUtils.isSupportedCollectionOrArray(valueType);
-        Class<?> actualType = isCollection ? InjectionUtils.getActualType(typeInfo.getGenericType()) : valueType;
-        
-        int index = getDotIndex(setter);
-        if (index == -1) {
-            Object castedValue = value;
-            if (Date.class.isAssignableFrom(valueType)) {
-                castedValue = convertToDate(value);
-            } else {
-                boolean isPrimitive = InjectionUtils.isPrimitive(valueType);
-                boolean isPrimitiveOrEnum = isPrimitive || valueType.isEnum();
-                if (ownerBean == null || isPrimitiveOrEnum) {
-                    try {
-                        CollectionCheck collCheck = getCollectionCheck(originalPropName, isCollection, actualType);
-                        if (collCheck == null) {
-                            castedValue = InjectionUtils.convertStringToPrimitive(value, actualType);
-                        } 
-                        if (collCheck == null && isCollection) {
-                            castedValue = getCollectionSingleton(valueType, castedValue);
-                        } else if (isCollection) {
-                            typeInfo.setCollectionCheckInfo(new CollectionCheckInfo(collCheck, castedValue));
-                            castedValue = getEmptyCollection(valueType);
-                        }
-                    } catch (Exception e) {
-                        throw new SearchParseException("Cannot convert String value \"" + value
-                                                     + "\" to a value of class " + valueType.getName(), e);
-                    }
-                } else {
-                    Class<?> classType = isCollection ? valueType : value.getClass(); 
-                    try {
-                        Method setterM = valueType.getMethod("set" + getMethodNameSuffix(setter),
-                                                             new Class[]{classType});
-                        Object objectValue = !isCollection ? value : getCollectionSingleton(valueType, value);
-                        setterM.invoke(ownerBean, new Object[]{objectValue});
-                        castedValue = objectValue; 
-                    } catch (Throwable ex) {
-                        throw new SearchParseException("Cannot convert String value \"" + value
-                                                       + "\" to a value of class " + valueType.getName(), ex);
-                    }
-                    
-                }
-            }
-            if (lastCastedValue != null) {
-                castedValue = lastCastedValue;
-            }
-            return castedValue;
-        } else {
-            String[] names = setter.split("\\.");
-            try {
-                String nextPart = getMethodNameSuffix(names[1]);
-                Method getterM = actualType.getMethod("get" + nextPart, new Class[]{});   
-                Class<?> returnType = getterM.getReturnType();
-                boolean returnCollection = InjectionUtils.isSupportedCollectionOrArray(returnType);
-                Class<?> actualReturnType = !returnCollection ? returnType 
-                    : InjectionUtils.getActualType(getterM.getGenericReturnType());
-                
-                boolean isPrimitive = InjectionUtils.isPrimitive(returnType) || returnType.isEnum();
-                boolean lastTry = names.length == 2 
-                    && (isPrimitive || returnType == Date.class || returnCollection);
-                
-                Object valueObject = ownerBean != null ? ownerBean 
-                    : actualType.isInterface() 
-                    ? Proxy.newProxyInstance(this.getClass().getClassLoader(), 
-                                             new Class[]{actualType}, 
-                                             new InterfaceProxy())
-                    : actualType.newInstance();
-                Object nextObject;
-                
-                if (lastTry) {
-                    if (!returnCollection) {
-                        nextObject = isPrimitive ? InjectionUtils.convertStringToPrimitive(value, returnType) 
-                            : convertToDate(value);
-                    } else {
-                        CollectionCheck collCheck = getCollectionCheck(originalPropName, true, actualReturnType);
-                        if (collCheck == null) {
-                            nextObject = getCollectionSingleton(valueType, value);
-                        } else {
-                            typeInfo.setCollectionCheckInfo(new CollectionCheckInfo(collCheck, value));
-                            nextObject = getEmptyCollection(valueType);
-                        }
-                    }
-                } else {
-                    nextObject = returnType.newInstance();
-                }
-                
-                Method setterM = actualType.getMethod("set" + nextPart, new Class[]{returnType});
-                setterM.invoke(valueObject, new Object[]{nextObject});
-                
-                if (lastTry) {
-                    lastCastedValue = lastCastedValue == null ? valueObject : lastCastedValue;
-                    return isCollection ? getCollectionSingleton(valueType, lastCastedValue) : lastCastedValue;
-                } else {
-                    lastCastedValue = valueObject;
-                }
-                
-                TypeInfo nextTypeInfo = new TypeInfo(nextObject.getClass(), getterM.getGenericReturnType()); 
-                Object response = parseType(originalPropName,
-                                 nextObject, 
-                                 lastCastedValue, 
-                                 setter.substring(index + 1), 
-                                 nextTypeInfo, 
-                                 value);
-                if (ownerBean == null) {
-                    return isCollection ? getCollectionSingleton(valueType, lastCastedValue) : lastCastedValue;
-                } else {
-                    return response;
-                }
-            } catch (Throwable e) {
-                throw new SearchParseException("Cannot convert String value \"" + value
-                                               + "\" to a value of class " + valueType.getName(), e);
-            }
-        }
-    }
     
-    private CollectionCheck getCollectionCheck(String propName, boolean isCollection, Class<?> actualCls) {
-        if (isCollection) {
-            if (InjectionUtils.isPrimitive(actualCls)) {
-                if (propName.startsWith(EXTENSION_COUNT_OPEN)) {
-                    return CollectionCheck.SIZE;
-                }
-            } else {
-                return CollectionCheck.SIZE;
-            }
-        }
-        return null;
-    }
-    
-    private Object getCollectionSingleton(Class<?> collectionCls, Object value) {
-        if (Set.class.isAssignableFrom(collectionCls)) {
-            return Collections.singleton(value);
-        } else {
-            return Collections.singletonList(value);
-        }
-    }
-    
-    private Object getEmptyCollection(Class<?> collectionCls) {
-        if (Set.class.isAssignableFrom(collectionCls)) {
-            return Collections.emptySet();
-        } else {
-            return Collections.emptyList();
-        }
-    }
-    
-    private Object convertToDate(String value) throws SearchParseException {
-        try {
-            DateFormat df = SearchUtils.getDateFormat(contextProperties);
-            String dateValue = value;
-            if (SearchUtils.isTimeZoneSupported(contextProperties, Boolean.FALSE)) {
-                // zone in XML is "+01:00" in Java is "+0100"; stripping semicolon
-                int idx = value.lastIndexOf(':');
-                if (idx != -1) {
-                    dateValue = value.substring(0, idx) + value.substring(idx + 1);
-                }
-            }
-            return df.parse(dateValue);
-        } catch (ParseException e) {
-            // is that duration?
-            try {
-                Date now = new Date();
-                DatatypeFactory.newInstance().newDuration(value).addTo(now);
-                return now;
-            } catch (DatatypeConfigurationException e1) {
-                throw new SearchParseException(e1);
-            } catch (IllegalArgumentException e1) {
-                throw new SearchParseException("Can parse " + value + " neither as date nor duration", e);
-            }
-        }
-    }
     
-    private int getDotIndex(String setter) {
-        return this.conditionClass == SearchBean.class ? -1 : setter.indexOf(".");
-    }
     
-    private String getSetter(String setter) {
-        int index = getDotIndex(setter);
-        if (index != -1) {
-            return setter.substring(0, index).toLowerCase();
-        } else {
-            return setter;
-        }
-    }
     
     private String unwrapSetter(String setter) {
         if (setter.startsWith(EXTENSION_COUNT_OPEN) && setter.endsWith(")")) {
@@ -513,14 +310,6 @@ public class FiqlParser<T> implements Se
         }
     }
     
-    private String getMethodNameSuffix(String name) {
-        if (name.length() == 1) {
-            return name.toUpperCase();
-        } else {
-            return Character.toUpperCase(name.charAt(0)) + name.substring(1);
-        }
-    }
-    
     // node of abstract syntax tree
     private interface ASTNode<T> {
         SearchCondition<T> build() throws SearchParseException;

Added: cxf/trunk/rt/rs/extensions/search/src/main/java/org/apache/cxf/jaxrs/ext/search/odata/OData2Parser.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/rs/extensions/search/src/main/java/org/apache/cxf/jaxrs/ext/search/odata/OData2Parser.java?rev=1566482&view=auto
==============================================================================
--- cxf/trunk/rt/rs/extensions/search/src/main/java/org/apache/cxf/jaxrs/ext/search/odata/OData2Parser.java (added)
+++ cxf/trunk/rt/rs/extensions/search/src/main/java/org/apache/cxf/jaxrs/ext/search/odata/OData2Parser.java Mon Feb 10 00:48:04 2014
@@ -0,0 +1,261 @@
+/**
+ * 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.odata;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.cxf.jaxrs.ext.search.AbstractSearchConditionParser;
+import org.apache.cxf.jaxrs.ext.search.AndSearchCondition;
+import org.apache.cxf.jaxrs.ext.search.Beanspector;
+import org.apache.cxf.jaxrs.ext.search.Beanspector.TypeInfo;
+import org.apache.cxf.jaxrs.ext.search.ConditionType;
+import org.apache.cxf.jaxrs.ext.search.OrSearchCondition;
+import org.apache.cxf.jaxrs.ext.search.PrimitiveSearchCondition;
+import org.apache.cxf.jaxrs.ext.search.SearchCondition;
+import org.apache.cxf.jaxrs.ext.search.SearchParseException;
+import org.apache.olingo.odata2.api.edm.EdmLiteral;
+import org.apache.olingo.odata2.api.edm.EdmLiteralKind;
+import org.apache.olingo.odata2.api.edm.EdmSimpleType;
+import org.apache.olingo.odata2.api.edm.EdmSimpleTypeException;
+import org.apache.olingo.odata2.api.edm.EdmTyped;
+import org.apache.olingo.odata2.api.exception.ODataApplicationException;
+import org.apache.olingo.odata2.api.exception.ODataMessageException;
+import org.apache.olingo.odata2.api.uri.expression.BinaryExpression;
+import org.apache.olingo.odata2.api.uri.expression.BinaryOperator;
+import org.apache.olingo.odata2.api.uri.expression.ExpressionVisitor;
+import org.apache.olingo.odata2.api.uri.expression.FilterExpression;
+import org.apache.olingo.odata2.api.uri.expression.LiteralExpression;
+import org.apache.olingo.odata2.api.uri.expression.MemberExpression;
+import org.apache.olingo.odata2.api.uri.expression.MethodExpression;
+import org.apache.olingo.odata2.api.uri.expression.MethodOperator;
+import org.apache.olingo.odata2.api.uri.expression.OrderByExpression;
+import org.apache.olingo.odata2.api.uri.expression.OrderExpression;
+import org.apache.olingo.odata2.api.uri.expression.PropertyExpression;
+import org.apache.olingo.odata2.api.uri.expression.SortOrder;
+import org.apache.olingo.odata2.api.uri.expression.UnaryExpression;
+import org.apache.olingo.odata2.api.uri.expression.UnaryOperator;
+import org.apache.olingo.odata2.core.uri.expression.FilterParser;
+import org.apache.olingo.odata2.core.uri.expression.FilterParserImpl;
+
+public class OData2Parser<T> extends AbstractSearchConditionParser<T> {
+    private final FilterParser parser;
+    
+    private static class TypedProperty {
+        private final TypeInfo typeInfo;
+        private final String propertyName;
+        
+        TypedProperty(final TypeInfo typeInfo, final String propertyName) {
+            this.typeInfo = typeInfo;
+            this.propertyName = propertyName;
+        }
+    }
+    
+    private static class TypedValue {
+        private final Object value;
+        private final String literal;    
+        private final Class< ? > typeClass;
+        
+        TypedValue(final Class< ? > typeClass, final String literal, final Object value) {
+            this.literal = literal;
+            this.value = value;
+            this.typeClass = typeClass;
+        }
+    }
+    
+    private class FilterExpressionVisitor implements ExpressionVisitor {
+        private final T condition;
+        private final Beanspector< T > beanspector;
+        
+        FilterExpressionVisitor(final T condition) {
+            this.condition = condition;
+            this.beanspector = new Beanspector<T>(condition);
+        }
+
+        @Override
+        public Object visitFilterExpression(FilterExpression filterExpression, String expressionString, 
+                Object expression) {
+            return expression;
+        }
+
+        @Override
+        @SuppressWarnings("unchecked")
+        public Object visitBinary(BinaryExpression binaryExpression, BinaryOperator operator, 
+                Object leftSide, Object rightSide) {
+            
+            // AND / OR operate on search conditions
+            if (operator == BinaryOperator.AND || operator == BinaryOperator.OR) {
+                if (leftSide instanceof SearchCondition && rightSide instanceof SearchCondition) {
+                    final List< SearchCondition< T > > conditions = new ArrayList< SearchCondition< T > >(2);
+                    conditions.add((SearchCondition< T >)leftSide);
+                    conditions.add((SearchCondition< T >)rightSide);
+                    
+                    if (operator == BinaryOperator.AND) {
+                        return new AndSearchCondition< T >(conditions);
+                    } else if (operator == BinaryOperator.OR) {
+                        return new OrSearchCondition< T >(conditions);
+                    }
+                } else {
+                    throw new SearchParseException(
+                        "Unsupported binary operation arguments (SearchCondition expected): " 
+                            + leftSide + ", " + rightSide);
+                }
+            }
+
+            // Property could be either on left side (Name eq 'Tom') or 
+            // right side ('Tom' eq Name)
+            TypedValue value = null;
+            TypedProperty property = null;
+            
+            if (leftSide instanceof TypedProperty && rightSide instanceof TypedValue) {
+                property = (TypedProperty)leftSide;
+                value = (TypedValue)rightSide;
+            } else if (rightSide instanceof TypedProperty && leftSide instanceof TypedValue) {
+                property = (TypedProperty)rightSide;
+                value = (TypedValue)leftSide;
+            } else {
+                throw new SearchParseException(
+                    "Unsupported binary operation arguments (TypedValue or TypedProperty expected): " 
+                        + leftSide + ", " + rightSide);
+            }
+                       
+            ConditionType conditionType = null;
+            switch (operator) {
+            case EQ:    
+                conditionType = ConditionType.EQUALS;
+                break;
+            case NE:
+                conditionType = ConditionType.NOT_EQUALS;
+                break;
+            case LT:
+                conditionType = ConditionType.LESS_THAN;
+                break;
+            case LE:
+                conditionType = ConditionType.LESS_OR_EQUALS;
+                break;
+            case GT:
+                conditionType = ConditionType.GREATER_THAN;
+                break;                
+            case GE:
+                conditionType = ConditionType.GREATER_OR_EQUALS;
+                break;                
+            default:
+                throw new SearchParseException("Unsupported binary operation: " + operator);
+            }
+            
+            Object typedValue = null;
+            // If property type and value type are compatible, just use them
+            if (property.typeInfo.getTypeClass().isAssignableFrom(value.typeClass)) {
+                typedValue = value.value;
+            } else { // Property type and value type are not compatible and convert / cast are required
+                typedValue = parseType(property.propertyName, null, null, property.propertyName, 
+                    property.typeInfo, value.literal);
+            }
+                        
+            return new PrimitiveSearchCondition< T >(property.propertyName, 
+                typedValue, conditionType, condition);
+        }
+
+        @Override
+        public Object visitLiteral(LiteralExpression literal, EdmLiteral edmLiteral) {            
+            try {
+                final EdmSimpleType type = edmLiteral.getType();  
+                
+                final Object value = type.valueOfString(edmLiteral.getLiteral(), 
+                    EdmLiteralKind.DEFAULT, null, type.getDefaultType());
+                
+                return new TypedValue(type.getDefaultType(), edmLiteral.getLiteral(), value); 
+            } catch (EdmSimpleTypeException ex) {
+                throw new SearchParseException("Failed to convert literal to a typed form: " + literal, ex);
+            }
+        }
+        
+        @Override
+        public Object visitProperty(PropertyExpression propertyExpression, String uriLiteral, EdmTyped edmProperty) {
+            try {
+                final TypeInfo typeInfo = beanspector.getAccessorTypeInfo(uriLiteral);
+                return new TypedProperty(typeInfo, uriLiteral);
+            } catch (Exception ex) {
+                throw new SearchParseException("Failed to get type information from property path: " + uriLiteral, ex);
+            }
+        }
+
+        @Override
+        public Object visitMethod(MethodExpression methodExpression, MethodOperator method, List<Object> parameters) {
+            throw new SearchParseException("Unsupported operation visitMethod: " + methodExpression 
+                + "," + method + "," + parameters);
+        }
+
+        @Override
+        public Object visitMember(MemberExpression memberExpression, Object path, Object property) {
+            throw new SearchParseException("Unsupported operation visitMember: " 
+                + memberExpression + "," + path + "," + property);            
+        }
+
+        @Override
+        public Object visitUnary(UnaryExpression unaryExpression, UnaryOperator operator, Object operand) {
+            throw new SearchParseException("Unsupported operation visitUnary: " + unaryExpression 
+                + "," + operator + "," + operand);
+        }
+        
+        @Override
+        public Object visitOrderByExpression(OrderByExpression orderByExpression, String expressionString,
+                List<Object> orders) {
+            throw new SearchParseException("Unsupported operation visitOrderByExpression: " 
+                + orderByExpression + "," + expressionString + "," + orders);
+        }
+
+        @Override
+        public Object visitOrder(OrderExpression orderExpression, Object filterResult, SortOrder sortOrder) {
+            throw new SearchParseException("Unsupported operation visitOrder: " + orderExpression 
+                + "," + filterResult + "," + sortOrder);
+        }        
+    }
+    
+    /**
+     * Creates OData 2.0 parser.
+     * 
+     * @param conditionClass - class of T used to create condition objects. Class T must have
+     *            accessible no-arguments constructor and complementary setters to these used in 
+     *            OData 2.0 $filter expressions.
+     */
+    public OData2Parser(final Class< T > conditionClass) {    
+        super(conditionClass);
+        this.parser = new FilterParserImpl(null);
+    }
+    
+    @Override
+    @SuppressWarnings("unchecked")
+    public SearchCondition<T> parse(String searchExpression) throws SearchParseException {
+        try {
+            final T condition = conditionClass.newInstance();
+            final FilterExpression expression = parser.parseFilterString(searchExpression);
+            final FilterExpressionVisitor visitor = new FilterExpressionVisitor(condition);            
+            return (SearchCondition< T >)expression.accept(visitor);            
+        } catch (ODataMessageException ex) {
+            throw new SearchParseException(ex);
+        } catch (ODataApplicationException ex) {
+            throw new SearchParseException(ex);
+        } catch (InstantiationException ex) {
+            throw new SearchParseException(ex);
+        } catch (IllegalAccessException ex) {
+            throw new SearchParseException(ex);
+        }
+    }
+}

Added: cxf/trunk/rt/rs/extensions/search/src/test/java/org/apache/cxf/jaxrs/ext/search/odata/OData2ParserTest.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/rs/extensions/search/src/test/java/org/apache/cxf/jaxrs/ext/search/odata/OData2ParserTest.java?rev=1566482&view=auto
==============================================================================
--- cxf/trunk/rt/rs/extensions/search/src/test/java/org/apache/cxf/jaxrs/ext/search/odata/OData2ParserTest.java (added)
+++ cxf/trunk/rt/rs/extensions/search/src/test/java/org/apache/cxf/jaxrs/ext/search/odata/OData2ParserTest.java Mon Feb 10 00:48:04 2014
@@ -0,0 +1,122 @@
+/**
+ * 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.odata;
+
+import org.apache.cxf.jaxrs.ext.search.SearchCondition;
+import org.apache.cxf.jaxrs.ext.search.SearchParseException;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class OData2ParserTest extends Assert {
+    private OData2Parser<Person> parser;
+
+    public static class Person {
+        private String firstName;
+        private String lastName;
+        private int age;
+
+        public Person() {
+        }
+
+        public Person(final String firstName, final String lastName) {
+            this.firstName = firstName;
+            this.lastName = lastName;
+        }
+
+        public void setFirstName(String firstName) {
+            this.firstName = firstName;
+        }
+
+        public void setLastName(String lastName) {
+            this.lastName = lastName;
+        }
+
+        public String getFirstName() {
+            return firstName;
+        }
+
+        public String getLastName() {
+            return lastName;
+        }
+        
+        public int getAge() {
+            return age;
+        }
+        
+        public void setAge(int age) {
+            this.age = age;
+        }
+        
+        Person withAge(int newAge) {
+            setAge(newAge);
+            return this;
+        }
+    }
+
+    @Before
+    public void setUp() {
+        parser = new OData2Parser<Person>(Person.class);
+    }
+
+    @Test
+    public void testFilterByFirstNameEqualsValue() throws SearchParseException {
+        SearchCondition< Person > filter = parser.parse("FirstName eq 'Tom'");
+        assertTrue(filter.isMet(new Person("Tom", "Bombadil")));
+        assertFalse(filter.isMet(new Person("Peter", "Bombadil")));
+    }
+    
+    @Test
+    public void testFilterByFirstAndLastNameEqualValue() throws SearchParseException {
+        SearchCondition< Person > filter = parser.parse("FirstName eq 'Tom' and LastName eq 'Bombadil'");
+        assertTrue(filter.isMet(new Person("Tom", "Bombadil")));
+        assertFalse(filter.isMet(new Person("Peter", "Bombadil")));
+    }
+    
+    @Test
+    public void testFilterByFirstOrLastNameEqualValue() throws SearchParseException {
+        SearchCondition< Person > filter = 
+            parser.parse("FirstName eq 'Tom' or FirstName eq 'Peter' and LastName eq 'Bombadil'");
+        assertTrue(filter.isMet(new Person("Tom", "Bombadil")));
+        assertTrue(filter.isMet(new Person("Peter", "Bombadil")));
+    }
+    
+    @Test
+    public void testFilterByFirstAndLastNameEqualValueWithAlternative() throws SearchParseException {
+        SearchCondition< Person > filter = parser.parse(
+            "(FirstName eq 'Tom' and LastName eq 'Tommyknocker')"
+            + " or (FirstName eq 'Peter' and LastName eq 'Bombadil')");
+        assertTrue(filter.isMet(new Person("Tom", "Tommyknocker")));
+        assertTrue(filter.isMet(new Person("Peter", "Bombadil")));
+        assertFalse(filter.isMet(new Person("Tom", "Bombadil")));      
+    }
+
+    @Test
+    public void testFilterByValueEqualsFirstName() throws SearchParseException {
+        SearchCondition< Person > filter = parser.parse("'Tom' eq FirstName");
+        assertTrue(filter.isMet(new Person("Tom", "Bombadil")));
+    }
+    
+    @Test
+    public void testFilterByAgeGreatThanValue() throws SearchParseException {
+        SearchCondition< Person > filter = parser.parse("Age gt 17");
+        assertTrue(filter.isMet(new Person("Tom", "Bombadil").withAge(18)));
+        assertFalse(filter.isMet(new Person("Tom", "Bombadil").withAge(16)));
+    }
+}