Enhance the include/exclude filter processing and fix a NPE

git-svn-id: https://svn.apache.org/repos/asf/tuscany/sca-java-2.x/trunk@1386756 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/DataBindingJAXRSWriter.java b/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/DataBindingJAXRSWriter.java
index 4ee1ff8..6064d23 100644
--- a/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/DataBindingJAXRSWriter.java
+++ b/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/DataBindingJAXRSWriter.java
@@ -120,8 +120,16 @@
                 Set<String> fieldSet = tokenize(fields);
                 for (String f : fieldSet) {
                     if (f.startsWith("-")) {
+                        if (excludedFields == null) {
+                            excludedFields = new HashSet<String>();
+                            metadata.put(EXCLUDED_FIELDS, excludedFields);
+                        }
                         excludedFields.add(f.substring(1));
                     } else {
+                        if (includedFields == null) {
+                            includedFields = new HashSet<String>();
+                            metadata.put(INCLUDED_FIELDS, includedFields);
+                        }
                         includedFields.add(f);
                     }
                 }
diff --git a/modules/binding-rest-runtime/src/test/java/org/apache/tuscany/sca/binding/rest/wireformat/json/CatalogServiceTestCase.java b/modules/binding-rest-runtime/src/test/java/org/apache/tuscany/sca/binding/rest/wireformat/json/CatalogServiceTestCase.java
index c098712..7a07c9b 100644
--- a/modules/binding-rest-runtime/src/test/java/org/apache/tuscany/sca/binding/rest/wireformat/json/CatalogServiceTestCase.java
+++ b/modules/binding-rest-runtime/src/test/java/org/apache/tuscany/sca/binding/rest/wireformat/json/CatalogServiceTestCase.java
@@ -91,15 +91,47 @@
     }
 
     @Test
-    public void testGetInvocationWithFilter() throws Exception {
+    public void testGetInvocationWithFilter1() throws Exception {
         WebConversation wc = new WebConversation();
-        WebRequest request = new GetMethodWebRequest(SERVICE_URL + "?excludedFields=price");
+        WebRequest request = new GetMethodWebRequest(SERVICE_URL + "?excludedFields=items.price");
         request.setHeaderField("Content-Type", "application/json");
         WebResponse response = wc.getResource(request);
 
         Assert.assertEquals(200, response.getResponseCode());
         String json = response.getText();
+        System.out.println(json);
         Assert.assertNotNull(json);
+        Assert.assertTrue(json.contains("name"));
+        Assert.assertFalse(json.contains("price"));
+    }
+
+    @Test
+    public void testGetInvocationWithFilter2() throws Exception {
+        WebConversation wc = new WebConversation();
+        WebRequest request = new GetMethodWebRequest(SERVICE_URL + "?fields=items,-items.price");
+        request.setHeaderField("Content-Type", "application/json");
+        WebResponse response = wc.getResource(request);
+
+        Assert.assertEquals(200, response.getResponseCode());
+        String json = response.getText();
+        System.out.println(json);
+        Assert.assertNotNull(json);
+        Assert.assertTrue(json.contains("name"));
+        Assert.assertFalse(json.contains("price"));
+    }
+
+    @Test
+    public void testGetInvocationWithFilter3() throws Exception {
+        WebConversation wc = new WebConversation();
+        WebRequest request = new GetMethodWebRequest(SERVICE_URL + "?includedFields=items.name&excludedFields=items");
+        request.setHeaderField("Content-Type", "application/json");
+        WebResponse response = wc.getResource(request);
+
+        Assert.assertEquals(200, response.getResponseCode());
+        String json = response.getText();
+        System.out.println(json);
+        Assert.assertNotNull(json);
+        Assert.assertTrue(json.contains("name"));
         Assert.assertFalse(json.contains("price"));
     }
 
diff --git a/modules/databinding-json/src/main/java/org/apache/tuscany/sca/databinding/json/jackson/JacksonHelper.java b/modules/databinding-json/src/main/java/org/apache/tuscany/sca/databinding/json/jackson/JacksonHelper.java
index 9430a32..50a4b0f 100644
--- a/modules/databinding-json/src/main/java/org/apache/tuscany/sca/databinding/json/jackson/JacksonHelper.java
+++ b/modules/databinding-json/src/main/java/org/apache/tuscany/sca/databinding/json/jackson/JacksonHelper.java
@@ -24,8 +24,10 @@
 import java.io.OutputStream;
 import java.io.Reader;
 import java.io.StringWriter;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Set;
 import java.util.Stack;
 
@@ -272,20 +274,23 @@
     }
 
     private static class TuscanyBeanPropertyFilter extends SimpleBeanPropertyFilter {
-        private Set<String> includedFields;
-        private Set<String> excludedFields;
+        private List<String> includedFields;
+        private List<String> excludedFields;
 
         private Stack<String> path = new Stack<String>();
 
         public TuscanyBeanPropertyFilter(Set<String> includedFields, Set<String> excludedFields) {
             if (includedFields == null) {
-                includedFields = Collections.emptySet();
+                includedFields = new HashSet<String>();
+                includedFields.add(""); // Allows any fields
             }
             if (excludedFields == null) {
                 excludedFields = Collections.emptySet();
             }
-            this.includedFields = includedFields;
-            this.excludedFields = excludedFields;
+            this.includedFields = new ArrayList<String>(includedFields);
+            Collections.sort(this.includedFields, Collections.reverseOrder());
+            this.excludedFields = new ArrayList<String>(excludedFields);
+            Collections.sort(this.excludedFields, Collections.reverseOrder());
         }
 
         @Override
@@ -294,11 +299,11 @@
                                      SerializerProvider provider,
                                      BeanPropertyWriter writer) throws Exception {
             path.push(writer.getName());
+            String fname = getFullName(path);
             try {
                 // System.out.println(path);
-                if (matches(path, includedFields, true)) {
-                    writer.serializeAsField(bean, jgen, provider);
-                } else if (includedFields.isEmpty() && !matches(path, excludedFields, false)) {
+                if (isAllowed(fname, includedFields, excludedFields)) {
+                    // Matching includes, write
                     writer.serializeAsField(bean, jgen, provider);
                 }
             } finally {
@@ -312,7 +317,7 @@
          * @param target
          * @return
          */
-        private boolean isPrefix(String source, String target) {
+        private boolean matches(String source, String target) {
             int index = source.indexOf(target);
             if (index == -1) {
                 return false;
@@ -330,7 +335,29 @@
          * @param included
          * @return
          */
-        private boolean matches(Stack<String> path, Set<String> patterns, boolean included) {
+        private boolean isAllowed(String fullName, List<String> included, List<String> excluded) {
+            String ex = null;
+            for (String p : excluded) {
+                if (matches(fullName, p)) {
+                    ex = p;
+                    break;
+                }
+            }
+            for (String p : included) {
+                if (matches(fullName, p) // If the parent element is included
+                    || matches(p, fullName) // If one of the child elements is included 
+                    ) {
+                    if (ex != null && ex.length() > p.length()) {
+                        // We already have an exclusion pattern that's more matching  
+                        return false;
+                    }
+                    return true;
+                }
+            }
+            return ex == null && included.contains("");
+        }
+
+        private String getFullName(Stack<String> path) {
             StringBuilder builder = new StringBuilder();
             for (int i = 0; i < path.size(); i++) {
                 builder.append(path.get(i));
@@ -339,12 +366,7 @@
                 }
             }
             String qname = builder.toString();
-            for (String p : patterns) {
-                if ((included && isPrefix(p, qname)) || ((!included) && isPrefix(qname, p))) {
-                    return true;
-                }
-            }
-            return false;
+            return qname;
         }
 
     }
diff --git a/modules/databinding-json/src/test/java/org/apache/tuscany/sca/databinding/json/jackson/Object2JSONTestCase.java b/modules/databinding-json/src/test/java/org/apache/tuscany/sca/databinding/json/jackson/Object2JSONTestCase.java
index 745937b..d46f7ec 100644
--- a/modules/databinding-json/src/test/java/org/apache/tuscany/sca/databinding/json/jackson/Object2JSONTestCase.java
+++ b/modules/databinding-json/src/test/java/org/apache/tuscany/sca/databinding/json/jackson/Object2JSONTestCase.java
@@ -271,10 +271,15 @@
         Object2JSON t1 = new Object2JSON();
         TransformationContext context = new TransformationContextImpl();
         Set<String> included = new HashSet<String>();
+        Set<String> excluded = new HashSet<String>();
         included.add("name");
-        included.add("you.name");
+        included.add("you");
+        excluded.add("you.id");
+
         // included.add("you.id");
         context.getMetadata().put("includedFields", included);
+        context.getMetadata().put("excludedFields", excluded);
+
         Object result = t1.transform(me, context);
         System.out.println(result);
         JSONObject json = new JSONObject(result.toString());
@@ -282,8 +287,9 @@
         Assert.assertTrue(json.has("you"));
         Assert.assertTrue(json.getJSONObject("you").has("name"));
         Assert.assertFalse(json.getJSONObject("you").has("id"));
+        
         context = new TransformationContextImpl();
-        Set<String> excluded = new HashSet<String>();
+        excluded = new HashSet<String>();
         excluded.add("you.name");
         excluded.add("age");
         context.getMetadata().put("excludedFields", excluded);