Enhance the partial read support so that we can use the full property names in the object tree

git-svn-id: https://svn.apache.org/repos/asf/tuscany/sca-java-2.x/trunk@1384975 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 9f3cf7b..4ee1ff8 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
@@ -49,6 +49,10 @@
 @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_XML, MediaType.WILDCARD})
 public class DataBindingJAXRSWriter<T> extends DataBindingJAXRSProvider implements MessageBodyWriter<T> {
 
+    public static final String FIELDS = "fields";
+    public static final String EXCLUDED_FIELDS = "excludedFields";
+    public static final String INCLUDED_FIELDS = "includedFields";
+
     public DataBindingJAXRSWriter(ExtensionPointRegistry registry) {
         super(registry);
     }
@@ -99,15 +103,28 @@
         HTTPContext context = ThreadHTTPContext.getHTTPContext();
         if (context != null) {
             metadata = new HashMap<String, Object>();
-            String included = context.getHttpRequest().getParameter("includedFields");
-            String excluded = context.getHttpRequest().getParameter("excludedFields");
+            String included = context.getHttpRequest().getParameter(INCLUDED_FIELDS);
+            String excluded = context.getHttpRequest().getParameter(EXCLUDED_FIELDS);
             Set<String> includedFields = tokenize(included);
             if (includedFields != null) {
-                metadata.put("includedFields", includedFields);
+                metadata.put(INCLUDED_FIELDS, includedFields);
             }
             Set<String> excludedFields = tokenize(excluded);
             if (excludedFields != null) {
-                metadata.put("excludedFields", excludedFields);
+                metadata.put(EXCLUDED_FIELDS, excludedFields);
+            }
+
+            // The syntax is fields=f1,f2,-f3
+            String fields = (String)context.getHttpRequest().getParameter(FIELDS);
+            if (fields != null) {
+                Set<String> fieldSet = tokenize(fields);
+                for (String f : fieldSet) {
+                    if (f.startsWith("-")) {
+                        excludedFields.add(f.substring(1));
+                    } else {
+                        includedFields.add(f);
+                    }
+                }
             }
 
         }
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 5d9a4c8..9430a32 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
@@ -25,7 +25,9 @@
 import java.io.Reader;
 import java.io.StringWriter;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.Set;
+import java.util.Stack;
 
 import javax.xml.bind.annotation.adapters.XmlAdapter;
 import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
@@ -62,10 +64,17 @@
 import com.fasterxml.jackson.module.jsonorg.JsonOrgModule;
 
 /**
- * 
+ * Helper class for Jackson
  */
 public class JacksonHelper {
+    public static final String TUSCANY_FILTER = "tuscanyFilter";
+    public static final String EXCLUDED_FIELDS = "excludedFields";
+    public static final String INCLUDED_FIELDS = "includedFields";
     private final static SimpleBeanPropertyFilter DEFAULT_FILTER = SimpleBeanPropertyFilter.serializeAllExcept();
+
+    /**
+     * The default instance of Jackson ObjectMapper
+     */
     public final static ObjectMapper MAPPER = createMapper();
     private final static JsonFactory FACTORY = new MappingJsonFactory(createMapper());
 
@@ -123,7 +132,7 @@
             @Override
             public Object findFilterId(AnnotatedClass annotatedClass) {
                 Object filterId = super.findFilterId(annotatedClass);
-                return filterId == null ? "tuscanyFilter" : filterId;
+                return filterId == null ? TUSCANY_FILTER : filterId;
             }
 
         };
@@ -136,7 +145,7 @@
             .withSerializationInclusion(JsonSerialize.Inclusion.NON_NULL)
             .withDateFormat(StdDateFormat.getBlueprintISO8601Format()));
 
-        mapper.setFilters(new SimpleFilterProvider().addFilter("tuscanyFilter", DEFAULT_FILTER));
+        mapper.setFilters(new SimpleFilterProvider().addFilter(TUSCANY_FILTER, DEFAULT_FILTER));
         return mapper;
     }
 
@@ -253,21 +262,22 @@
     public static FilterProvider configureFilterProvider(TransformationContext context) {
         SimpleBeanPropertyFilter filter = DEFAULT_FILTER;
         if (context != null) {
-            Set<String> included = (Set<String>)context.getMetadata().get("includedFields");
-            Set<String> excluded = (Set<String>)context.getMetadata().get("excludedFields");
-            Class<?> type = context.getSourceDataType() == null ? null : context.getSourceDataType().getPhysical();
-            filter = new TuscanyBeanPropertyFilter(type, included, excluded);
+            Set<String> included = (Set<String>)context.getMetadata().get(INCLUDED_FIELDS);
+            Set<String> excluded = (Set<String>)context.getMetadata().get(EXCLUDED_FIELDS);
+            // Class<?> type = context.getSourceDataType() == null ? null : context.getSourceDataType().getPhysical();
+            filter = new TuscanyBeanPropertyFilter(included, excluded);
         }
-        FilterProvider filters = new SimpleFilterProvider().addFilter("tuscanyFilter", filter);
+        FilterProvider filters = new SimpleFilterProvider().addFilter(TUSCANY_FILTER, filter);
         return filters;
     }
 
     private static class TuscanyBeanPropertyFilter extends SimpleBeanPropertyFilter {
-        private Class<?> type;
         private Set<String> includedFields;
         private Set<String> excludedFields;
 
-        public TuscanyBeanPropertyFilter(Class<?> type, Set<String> includedFields, Set<String> excludedFields) {
+        private Stack<String> path = new Stack<String>();
+
+        public TuscanyBeanPropertyFilter(Set<String> includedFields, Set<String> excludedFields) {
             if (includedFields == null) {
                 includedFields = Collections.emptySet();
             }
@@ -276,7 +286,6 @@
             }
             this.includedFields = includedFields;
             this.excludedFields = excludedFields;
-            this.type = type;
         }
 
         @Override
@@ -284,18 +293,58 @@
                                      JsonGenerator jgen,
                                      SerializerProvider provider,
                                      BeanPropertyWriter writer) throws Exception {
-            /*
-            // First check if the type matches and skip the filtering if the type is different
-            if (type != null && writer.getMember().getDeclaringClass() != type) {
-                writer.serializeAsField(bean, jgen, provider);
-                return;
+            path.push(writer.getName());
+            try {
+                // System.out.println(path);
+                if (matches(path, includedFields, true)) {
+                    writer.serializeAsField(bean, jgen, provider);
+                } else if (includedFields.isEmpty() && !matches(path, excludedFields, false)) {
+                    writer.serializeAsField(bean, jgen, provider);
+                }
+            } finally {
+                path.pop();
             }
-            */
-            if (includedFields.contains(writer.getName())) {
-                writer.serializeAsField(bean, jgen, provider);
-            } else if (includedFields.isEmpty() && !excludedFields.contains(writer.getName())) {
-                writer.serializeAsField(bean, jgen, provider);
+        }
+
+        /**
+         * Check the target string is a prefix of the source separated by .
+         * @param source
+         * @param target
+         * @return
+         */
+        private boolean isPrefix(String source, String target) {
+            int index = source.indexOf(target);
+            if (index == -1) {
+                return false;
             }
+            if (target.length() == source.length() || source.charAt(target.length()) == '.') {
+                return true;
+            }
+            return false;
+        }
+
+        /**
+         * Check if the path matches the one of the patterns
+         * @param path
+         * @param patterns
+         * @param included
+         * @return
+         */
+        private boolean matches(Stack<String> path, Set<String> patterns, boolean included) {
+            StringBuilder builder = new StringBuilder();
+            for (int i = 0; i < path.size(); i++) {
+                builder.append(path.get(i));
+                if (i != path.size() - 1) {
+                    builder.append(".");
+                }
+            }
+            String qname = builder.toString();
+            for (String p : patterns) {
+                if ((included && isPrefix(p, qname)) || ((!included) && isPrefix(qname, p))) {
+                    return true;
+                }
+            }
+            return false;
         }
 
     }
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 6e1312d..745937b 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
@@ -23,13 +23,16 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Date;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 import junit.framework.Assert;
 
 import org.apache.tuscany.sca.databinding.TransformationContext;
 import org.apache.tuscany.sca.databinding.impl.TransformationContextImpl;
 import org.apache.tuscany.sca.interfacedef.impl.DataTypeImpl;
+import org.json.JSONObject;
 import org.junit.Test;
 
 /**
@@ -263,21 +266,34 @@
         me.setDate(new Date());
         YourBean you = new YourBean();
         you.setId(123);
-        you.setName(null);
+        you.setName("You");
         me.setYou(you);
         Object2JSON t1 = new Object2JSON();
         TransformationContext context = new TransformationContextImpl();
-        context.getMetadata().put("includedFields", Collections.singleton("name"));
+        Set<String> included = new HashSet<String>();
+        included.add("name");
+        included.add("you.name");
+        // included.add("you.id");
+        context.getMetadata().put("includedFields", included);
         Object result = t1.transform(me, context);
         System.out.println(result);
-        Assert.assertTrue(result.toString().contains("name"));
-        Assert.assertFalse(result.toString().contains("age"));
+        JSONObject json = new JSONObject(result.toString());
+        Assert.assertTrue(json.has("name"));
+        Assert.assertTrue(json.has("you"));
+        Assert.assertTrue(json.getJSONObject("you").has("name"));
+        Assert.assertFalse(json.getJSONObject("you").has("id"));
         context = new TransformationContextImpl();
-        context.getMetadata().put("excludedFields", Collections.singleton("name"));
+        Set<String> excluded = new HashSet<String>();
+        excluded.add("you.name");
+        excluded.add("age");
+        context.getMetadata().put("excludedFields", excluded);
         result = t1.transform(me, context);
         System.out.println(result);
-        Assert.assertFalse(result.toString().contains("name"));
-        Assert.assertTrue(result.toString().contains("age"));
+        json = new JSONObject(result.toString());
+        Assert.assertTrue(json.has("name"));
+        Assert.assertTrue(json.has("you"));
+        Assert.assertTrue(json.getJSONObject("you").has("id"));
+        Assert.assertFalse(json.getJSONObject("you").has("name"));
     }
 
     @Test